Downloads:
205,249
Downloads of v 0.9.702:
163
Last Update:
02 Dec 2018
Package Maintainer(s):
Software Author(s):
- Chrissy LeMaire
Tags:
admin powershell module template dba sqlserver sql tools database- Software Specific:
- Software Site
- Software Source
- Software License
- Software Docs
- Software Issues
- Package Specific:
- Package Source
- Package outdated?
- Package broken?
- Contact Maintainers
- Contact Site Admins
- Software Vendor?
- Report Abuse
- Download
dbatools (PowerShell Module)
This is not the latest version of dbatools (PowerShell Module) available.
- 1
- 2
- 3
0.9.702 | Updated: 02 Dec 2018
- Software Specific:
- Software Site
- Software Source
- Software License
- Software Docs
- Software Issues
- Package Specific:
- Package Source
- Package outdated?
- Package broken?
- Contact Maintainers
- Contact Site Admins
- Software Vendor?
- Report Abuse
- Download
Downloads:
205,249
Downloads of v 0.9.702:
163
Maintainer(s):
Software Author(s):
- Chrissy LeMaire
dbatools (PowerShell Module) 0.9.702
This is not the latest version of dbatools (PowerShell Module) available.
Legal Disclaimer: Neither this package nor Chocolatey Software, Inc. are affiliated with or endorsed by Chrissy LeMaire. The inclusion of Chrissy LeMaire trademark(s), if any, upon this webpage is solely to identify Chrissy LeMaire goods or services and not for commercial purposes.
- 1
- 2
- 3
All Checks are Passing
3 Passing Tests
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:
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
Option 1: Cached Package (Unreliable, Requires Internet - Same As Community)-
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
-
Open Source
-
Download the package:
Download - Follow manual internalization instructions
-
-
Package Internalizer (C4B)
-
Run: (additional options)
choco download dbatools --internalize --version=0.9.702 --source=https://community.chocolatey.org/api/v2/
-
For package and dependencies run:
choco push --source="'INTERNAL REPO URL'"
- Automate package internalization
-
Run: (additional options)
3. Copy Your Script
choco upgrade dbatools -y --source="'INTERNAL REPO URL'" --version="'0.9.702'" [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.702'"
$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.702'
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.702'
end
See docs at https://docs.chef.io/resource_chocolatey_package.html.
cChocoPackageInstaller dbatools
{
Name = "dbatools"
Version = "0.9.702"
Source = "INTERNAL REPO URL"
}
Requires cChoco DSC Resource. See docs at https://github.com/chocolatey/cChoco.
package { 'dbatools':
ensure => '0.9.702',
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.
This package was approved as a trusted package on 02 Dec 2018.
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.
$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
}
}
}
$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
}
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"ms-vscode.PowerShell",
"dbatools.search"
]
}
// Place your settings in this file to overwrite default and user settings.
{
"files.trimTrailingWhitespace": true,
"powershell.codeFormatting.preset": "OTBS"
}
### DO NOT EDIT THIS FILE DIRECTLY ###
function Export-DbatoolsConfig
{
<#
.SYNOPSIS
Exports configuration items to a Json file.
.DESCRIPTION
Exports configuration items to a Json file.
.PARAMETER FullName
Select the configuration objects to export by filtering by their full name.
.PARAMETER Module
Select the configuration objects to export by filtering by their module name.
.PARAMETER Name
Select the configuration objects to export by filtering by their name.
.PARAMETER Config
The configuration object(s) to export.
Returned by Get-DbatoolsConfig.
.PARAMETER ModuleName
Exports all configuration pertinent to a module to a predefined path.
Exported configuration items include all settings marked as 'ModuleExport' that have been changed from the default value.
.PARAMETER ModuleVersion
The configuration version of the module-settings to write.
.PARAMETER Scope
Which predefined path to write module specific settings to.
Only file scopes are considered.
By default it writes to the suer profile.
.PARAMETER OutPath
The path (filename included) to export to.
Will fail if the folder does not exist, will overwrite the file if it exists.
.PARAMETER SkipUnchanged
If set, configuration objects whose value was not changed from its original value will not be exported.
(Note: Settings that were updated with the same value as the original default will still be considered changed)
.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-DbatoolsConfig | Export-DbatoolsConfig -OutPath '~/export.json'
Exports all current settings to json.
.EXAMPLE
Export-DbatoolsConfig -Module message -OutPath '~/export.json' -SkipUnchanged
Exports all settings of the module 'message' that are no longer the original default values to json.
#>
[CmdletBinding(DefaultParameterSetName = 'FullName')]
Param (
[Parameter(ParameterSetName = "FullName", Position = 0, Mandatory = $true)]
[string]
$FullName,
[Parameter(ParameterSetName = "Module", Position = 0, Mandatory = $true)]
[string]
$Module,
[Parameter(ParameterSetName = "Module", Position = 1)]
[string]
$Name = "*",
[Parameter(ParameterSetName = "Config", Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
[Sqlcollaborative.Dbatools.Configuration.Config[]]
$Config,
[Parameter(ParameterSetName = "ModuleName", Mandatory = $true)]
[string]
$ModuleName,
[Parameter(ParameterSetName = "ModuleName")]
[int]
$ModuleVersion = 1,
[Parameter(ParameterSetName = "ModuleName")]
[Sqlcollaborative.Dbatools.Configuration.ConfigScope]
$Scope = "FileUserShared",
[Parameter(Position = 1, Mandatory = $true, ParameterSetName = 'Config')]
[Parameter(Position = 1, Mandatory = $true, ParameterSetName = 'FullName')]
[Parameter(Position = 2, Mandatory = $true, ParameterSetName = 'Module')]
[string]
$OutPath,
[switch]
$SkipUnchanged,
[switch]
$EnableException
)
begin
{
Write-Message -Level InternalComment -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")" -Tag 'debug', 'start', 'param'
$items = @()
if (($Scope -band 15) -and ($ModuleName))
{
Stop-Function -Message "Cannot export modulecache to registry! Please pick a file scope for your export destination" -EnableException $EnableException -Category InvalidArgument -Tag 'fail', 'scope', 'registry'
return
}
}
process
{
if (Test-FunctionInterrupt) { return }
if (-not $ModuleName)
{
foreach ($item in $Config) { $items += $item }
if ($FullName) { $items = Get-DbatoolsConfig -FullName $FullName }
if ($Module) { $items = Get-DbatoolsConfig -Module $Module -Name $Name }
}
}
end
{
if (Test-FunctionInterrupt) { return }
if (-not $ModuleName)
{
try { Write-DbatoolsConfigFile -Config ($items | Where-Object { -not $SkipUnchanged -or -not $_.Unchanged } ) -Path $OutPath -Replace }
catch
{
Stop-Function -Message "Failed to export to file" -EnableException $EnableException -ErrorRecord $_ -Tag 'fail', 'export'
return
}
}
else
{
if ($Scope -band 16)
{
Write-DbatoolsConfigFile -Config (Get-DbatoolsConfig -Module $ModuleName -Force | Where-Object ModuleExport | Where-Object Unchanged -NE $true) -Path (Join-Path $script:path_FileUserLocal "$($ModuleName.ToLower())-$($ModuleVersion).json")
}
if ($Scope -band 32)
{
Write-DbatoolsConfigFile -Config (Get-DbatoolsConfig -Module $ModuleName -Force | Where-Object ModuleExport | Where-Object Unchanged -NE $true) -Path (Join-Path $script:path_FileUserShared "$($ModuleName.ToLower())-$($ModuleVersion).json")
}
if ($Scope -band 64)
{
Write-DbatoolsConfigFile -Config (Get-DbatoolsConfig -Module $ModuleName -Force | Where-Object ModuleExport | Where-Object Unchanged -NE $true) -Path (Join-Path $script:path_FileSystem "$($ModuleName.ToLower())-$($ModuleVersion).json")
}
}
}
}
function Get-DbatoolsConfig {
<#
.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 (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbatoolsConfig 'Mail.To'
Retrieves the configuration element for the key "Mail.To"
.EXAMPLE
PS C:\> Get-DbatoolsConfig -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
}
}
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaConfig
}
function Get-DbatoolsConfigValue {
<#
.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.
.NOTES
Tags: Config, Module
Author: Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbatoolsConfigValue -Name 'System.MailServer'
Returns the configured value that was assigned to the key 'System.MailServer'
.EXAMPLE
PS C:\> Get-DbatoolsConfigValue -Name 'Default.CoffeeMilk' -Fallback 0
Returns the configured value for 'Default.CoffeeMilk'. If no such value is configured, it returns '0' instead.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSPossibleIncorrectComparisonWithNull", "")]
[CmdletBinding()]
param (
[Alias('Name')]
[Parameter(Mandatory)]
[string]$FullName,
[object]$Fallback,
[switch]$NotNull
)
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaConfigValue
$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
}
}
function Import-DbatoolsConfig
{
<#
.SYNOPSIS
Imports a json configuration file into the configuration system.
.DESCRIPTION
Imports a json configuration file into the configuration system.
.PARAMETER Path
The path to the json file to import.
.PARAMETER ModuleName
Import configuration items specific to a module from the default configuration paths.
.PARAMETER ModuleVersion
The configuration version of the module-settings to load.
.PARAMETER Scope
Where to import the module specific configuration items form.
Only file-based scopes are supported for this.
By default, all locations are queried, with user settings beating system settings.
.PARAMETER IncludeFilter
If specified, only elements with names that are similar (-like) to names in this list will be imported.
.PARAMETER ExcludeFilter
Elements that are similar (-like) to names in this list will not be imported.
.PARAMETER Peek
Rather than applying the setting, return the configuration items that would have been applied.
.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:\> Import-DbatoolsConfig -Path '.\config.json'
Imports the configuration stored in '.\config.json'
.EXAMPLE
PS C:\> Import-DbatoolsConfig -ModuleName message
Imports all the module specific settings that have been persisted in any of the default file system paths.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingEmptyCatchBlock", "")]
[CmdletBinding(DefaultParameterSetName = "Path")]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Path")]
[string[]]
$Path,
[Parameter(ParameterSetName = "ModuleName", Mandatory = $true)]
[string]
$ModuleName,
[Parameter(ParameterSetName = "ModuleName")]
[int]
$ModuleVersion = 1,
[Parameter(ParameterSetName = "ModuleName")]
[Sqlcollaborative.Dbatools.Configuration.ConfigScope]
$Scope = "FileUserLocal, FileUserShared, FileSystem",
[Parameter(ParameterSetName = "Path")]
[string[]]
$IncludeFilter,
[Parameter(ParameterSetName = "Path")]
[string[]]
$ExcludeFilter,
[Parameter(ParameterSetName = "Path")]
[switch]
$Peek,
[switch]
$EnableException
)
begin
{
Write-Message -Level InternalComment -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")" -Tag 'debug','start','param'
}
process
{
#region Explicit Path
foreach ($item in $Path)
{
try
{
if ($item -like "http*") { $data = Read-DbatoolsConfigFile -Weblink $item -ErrorAction Stop }
else
{
$pathItem = $null
try { $pathItem = Resolve-DbaPath -Path $item -SingleItem -Provider FileSystem }
catch { }
if ($pathItem) { $data = Read-DbatoolsConfigFile -Path $pathItem -ErrorAction Stop }
else { $data = Read-DbatoolsConfigFile -RawJson $item -ErrorAction Stop }
}
}
catch { Stop-Function -Message "Failed to import $item" -EnableException $EnableException -Tag 'fail', 'import' -ErrorRecord $_ -Continue -Target $item }
:element foreach ($element in $data)
{
#region Exclude Filter
foreach ($exclusion in $ExcludeFilter)
{
if ($element.FullName -like $exclusion)
{
continue element
}
}
#endregion Exclude Filter
#region Include Filter
if ($IncludeFilter)
{
$isIncluded = $false
foreach ($inclusion in $IncludeFilter)
{
if ($element.FullName -like $inclusion)
{
$isIncluded = $true
break
}
}
if (-not $isIncluded) { continue }
}
#endregion Include Filter
if ($Peek) { $element }
else
{
try
{
if (-not $element.KeepPersisted) { Set-DbatoolsConfig -FullName $element.FullName -Value $element.Value -EnableException }
else { Set-DbatoolsConfig -FullName $element.FullName -PersistedValue $element.Value -PersistedType $element.Type }
}
catch
{
Stop-Function -Message "Failed to set '$($element.FullName)'" -ErrorRecord $_ -EnableException $EnableException -Tag 'fail', 'import' -Continue -Target $item
}
}
}
}
#endregion Explicit Path
if ($ModuleName)
{
$data = Read-DbatoolsConfigPersisted -Module $ModuleName -Scope $Scope -ModuleVersion $ModuleVersion
foreach ($value in $data.Values)
{
if (-not $value.KeepPersisted) { Set-DbatoolsConfig -FullName $value.FullName -Value $value.Value -EnableException:$EnableException}
else { Set-DbatoolsConfig -FullName $value.FullName -Value ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::ConvertFromPersistedValue($value.Value, $value.Type)) -EnableException:$EnableException }
}
}
}
end
{
}
}
function Register-DbatoolsConfig
{
<#
.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-DbatoolsConfig.
.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
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-DbatoolsConfig message.style.* | Register-DbatoolsConfig
Retrieves all configuration items that that start with message.style. and registers them in registry for the current user.
.EXAMPLE
PS C:\> Register-DbatoolsConfig -FullName "message.consoleoutput.disable" -Scope SystemDefault
Retrieves the configuration item "message.consoleoutput.disable" and registers it in registry as the default setting for all users on this machine.
.EXAMPLE
PS C:\> Register-DbatoolsConfig -Module Message -Scope SystemMandatory
Retrieves all configuration items of the module Message, 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
{
if ($script:NoRegistry -and ($Scope -band 14))
{
Stop-Function -Message "Cannot register configurations on non-windows machines to registry. Please specify a file-based scope" -Tag 'NotSupported' -Category NotImplemented
return
}
# Linux and MAC default to local user store file
if ($script:NoRegistry -and ($Scope -eq "UserDefault"))
{
$Scope = [Sqlcollaborative.Dbatools.Configuration.ConfigScope]::FileUserLocal
}
# Linux and MAC get redirection for SystemDefault to FileSystem
if ($script:NoRegistry -and ($Scope -eq "SystemDefault"))
{
$Scope = [Sqlcollaborative.Dbatools.Configuration.ConfigScope]::FileSystem
}
$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 -Tag "config", "fail" -Target $Config -FunctionName $FunctionName
return
}
try
{
Write-Message -Level Verbose -Message "Registering $($Config.FullName) for $Scope" -Tag "Config" -Target $Config -FunctionName $FunctionName
#region User Default
if (1 -band $Scope)
{
Ensure-RegistryPath -Path $script:path_RegistryUserDefault -ErrorAction Stop
Set-ItemProperty -Path $script:path_RegistryUserDefault -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
}
#endregion User Default
#region User Mandatory
if (2 -band $Scope)
{
Ensure-RegistryPath -Path $script:path_RegistryUserEnforced -ErrorAction Stop
Set-ItemProperty -Path $script:path_RegistryUserEnforced -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
}
#endregion User Mandatory
#region System Default
if (4 -band $Scope)
{
Ensure-RegistryPath -Path $script:path_RegistryMachineDefault -ErrorAction Stop
Set-ItemProperty -Path $script:path_RegistryMachineDefault -Name $Config.FullName -Value $Config.RegistryData -ErrorAction Stop
}
#endregion System Default
#region System Mandatory
if (8 -band $Scope)
{
Ensure-RegistryPath -Path $script:path_RegistryMachineEnforced -ErrorAction Stop
Set-ItemProperty -Path $script:path_RegistryMachineEnforced -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 -Tag "config", "fail" -Target $Config -ErrorRecord $_ -FunctionName $FunctionName
return
}
}
function Ensure-RegistryPath
{
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "")]
[CmdletBinding()]
Param (
[string]
$Path
)
if (-not (Test-Path $Path))
{
$null = New-Item $Path -Force
}
}
# For file based persistence
$configurationItems = @()
}
process
{
if (Test-FunctionInterrupt) { return }
#region Registry Based
if ($Scope -band 15)
{
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
}
}
}
}
#endregion Registry Based
#region File Based
else
{
switch ($parSet)
{
"Default"
{
foreach ($item in $Config)
{
if ($configurationItems.FullName -notcontains $item.FullName) { $configurationItems += $item }
}
foreach ($item in $FullName)
{
if (($configurationItems.FullName -notcontains $item) -and ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.ContainsKey($item.ToLower())))
{
$configurationItems += [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations[$item.ToLower()]
}
}
}
"Name"
{
foreach ($item in ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Configurations.Values | Where-Object Module -EQ $Module | Where-Object Name -Like $Name))
{
if ($configurationItems.FullName -notcontains $item.FullName) { $configurationItems += $item }
}
}
}
}
#endregion File Based
}
end
{
if (Test-FunctionInterrupt) { return }
#region Finish File Based Persistence
if ($Scope -band 16)
{
Write-DbatoolsConfigFile -Config $configurationItems -Path (Join-Path $script:path_FileUserLocal "psf_config.json")
}
if ($Scope -band 32)
{
Write-DbatoolsConfigFile -Config $configurationItems -Path (Join-Path $script:path_FileUserShared "psf_config.json")
}
if ($Scope -band 64)
{
Write-DbatoolsConfigFile -Config $configurationItems -Path (Join-Path $script:path_FileSystem "psf_config.json")
}
#endregion Finish File Based Persistence
}
}
function Reset-DbatoolsConfig
{
<#
.SYNOPSIS
Reverts a configuration item to its default value.
.DESCRIPTION
This command can be used to revert a configuration item to the value it was initialized with.
Generally, this amounts to reverting it to its default value.
In order for a reset to be possible, two conditions must be met:
- The setting must have been initialized.
- The setting cannot have been enforced by policy.
.PARAMETER ConfigurationItem
A configuration object as returned by Get-DbatoolsConfig.
.PARAMETER FullName
The full name of the setting to reset, offering the maximum of precision.
.PARAMETER Module
The name of the module, from which configurations should be reset.
Used in conjunction with the -Name parameter to filter a specific set of items.
.PARAMETER Name
Used in conjunction with the -Module parameter to select which settings to reset using wildcard comparison.
.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.
.PARAMETER Confirm
If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
.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.
.EXAMPLE
PS C:\> Reset-DbatoolsConfig -Module MyModule
Resets all configuration items of the MyModule to default.
.EXAMPLE
PS C:\> Get-DbatoolsConfig | Reset-DbatoolsConfig
Resets ALL configuration items to default.
.EXAMPLE
PS C:\> Reset-DbatoolsConfig -FullName MyModule.Group.Setting1
Resets the configuration item named 'MyModule.Group.Setting1'.
#>
[CmdletBinding(DefaultParameterSetName = 'Pipeline')]
param (
[Parameter(ValueFromPipeline = $true, ParameterSetName = 'Pipeline')]
[Sqlcollaborative.Dbatools.Configuration.Config[]]
$ConfigurationItem,
[Parameter(ValueFromPipeline = $true, ParameterSetName = 'Pipeline')]
[string[]]
$FullName,
[Parameter(Mandatory = $true, ParameterSetName = 'Module')]
[string]
$Module,
[Parameter(ParameterSetName = 'Module')]
[string]
$Name = "*",
[switch]
$EnableException
)
process
{
#region By configuration Item
foreach ($item in $ConfigurationItem)
{
if ($PSCmdlet.ShouldProcess($item.FullName, 'Reset to default value'))
{
try { $item.ResetValue() }
catch { Stop-Function -Message "Failed to reset the configuration item." -ErrorRecord $_ -Continue -EnableException $EnableException }
}
}
#endregion By configuration Item
#region By FullName
foreach ($nameItem in $FullName)
{
# The configuration items themselves can be cast to string, so they need to be filtered out,
# otherwise on bind they would execute for this code-path as well.
if ($nameItem -ceq "Sqlcollaborative.Dbatools.Configuration.Config") { continue }
foreach ($item in (Get-DbatoolsConfig -FullName $nameItem))
{
if ($PSCmdlet.ShouldProcess($item.FullName, 'Reset to default value'))
{
try { $item.ResetValue() }
catch { Stop-Function -Message "Failed to reset the configuration item." -ErrorRecord $_ -Continue -EnableException $EnableException }
}
}
}
#endregion By FullName
if ($Module)
{
foreach ($item in (Get-DbatoolsConfig -Module $Module -Name $Name))
{
if ($PSCmdlet.ShouldProcess($item.FullName, 'Reset to default value'))
{
try { $item.ResetValue() }
catch { Stop-Function -Message "Failed to reset the configuration item." -ErrorRecord $_ -Continue -EnableException $EnableException }
}
}
}
}
}
function Unregister-DbatoolsConfig
{
<#
.SYNOPSIS
Removes registered configuration settings.
.DESCRIPTION
Removes registered configuration settings.
This function can be used to remove settings that have been persisted for either user or computer.
Note: This command has no effect on configuration setings currently in memory.
.PARAMETER ConfigurationItem
A configuration object as returned by Get-DbatoolsConfig.
.PARAMETER FullName
The full name of the configuration setting to purge.
.PARAMETER Module
The module, amongst which settings should be unregistered.
.PARAMETER Name
The name of the setting to unregister.
For use together with the module parameter, to limit the amount of settings that are unregistered.
.PARAMETER Scope
Settings can be set to either default or enforced, for user or the entire computer.
By default, only DefaultSettings for the user are unregistered.
Use this parameter to choose the actual scope for the command to process.
.EXAMPLE
PS C:\> Get-DbatoolsConfig | Unregister-DbatoolsConfig
Completely removes all registered configurations currently loaded in memory.
In most cases, this will mean removing all registered configurations.
.EXAMPLE
PS C:\> Unregister-DbatoolsConfig -Scope SystemDefault -FullName 'MyModule.Path.DefaultExport'
Unregisters the setting 'MyModule.Path.DefaultExport' from the list of computer-wide defaults.
Note: Changing system wide settings requires running the console with elevation.
.EXAMPLE
PS C:\> Unregister-DbatoolsConfig -Module MyModule
Unregisters all configuration settings for the module MyModule.
#>
[CmdletBinding(DefaultParameterSetName = 'Pipeline')]
param (
[Parameter(ValueFromPipeline = $true, ParameterSetName = 'Pipeline')]
[Sqlcollaborative.Dbatools.Configuration.Config[]]
$ConfigurationItem,
[Parameter(ValueFromPipeline = $true, ParameterSetName = 'Pipeline')]
[string[]]
$FullName,
[Parameter(Mandatory = $true, ParameterSetName = 'Module')]
[string]
$Module,
[Parameter(ParameterSetName = 'Module')]
[string]
$Name = "*",
[Sqlcollaborative.Dbatools.Configuration.ConfigScope]
$Scope = "UserDefault"
)
begin
{
if (($PSVersionTable.PSVersion.Major -ge 6) -and ($PSVersionTable.OS -notlike "*Windows*") -and ($Scope -band 15))
{
Stop-Function -Message "Cannot unregister configurations from registry on non-windows machines." -Tag 'NotSupported' -Category ResourceUnavailable
return
}
#region Initialize Collection
$registryProperties = @()
if ($Scope -band 1)
{
if (Test-Path $script:path_RegistryUserDefault) { $registryProperties += Get-ItemProperty -Path $script:path_RegistryUserDefault }
}
if ($Scope -band 2)
{
if (Test-Path $script:path_RegistryUserEnforced) { $registryProperties += Get-ItemProperty -Path $script:path_RegistryUserEnforced }
}
if ($Scope -band 4)
{
if (Test-Path $script:path_RegistryMachineDefault) { $registryProperties += Get-ItemProperty -Path $script:path_RegistryMachineDefault }
}
if ($Scope -band 8)
{
if (Test-Path $script:path_RegistryMachineEnforced) { $registryProperties += Get-ItemProperty -Path $script:path_RegistryMachineEnforced }
}
$pathProperties = @()
if ($Scope -band 16)
{
$fileUserLocalSettings = @()
if (Test-Path (Join-Path $script:path_FileUserLocal "psf_config.json")) { $fileUserLocalSettings = Get-Content (Join-Path $script:path_FileUserLocal "psf_config.json") -Encoding UTF8 | ConvertFrom-Json }
if ($fileUserLocalSettings)
{
$pathProperties += [pscustomobject]@{
Path = (Join-Path $script:path_FileUserLocal "psf_config.json")
Properties = $fileUserLocalSettings
Changed = $false
}
}
}
if ($Scope -band 32)
{
$fileUserSharedSettings = @()
if (Test-Path (Join-Path $script:path_FileUserShared "psf_config.json")) { $fileUserSharedSettings = Get-Content (Join-Path $script:path_FileUserShared "psf_config.json") -Encoding UTF8 | ConvertFrom-Json }
if ($fileUserSharedSettings)
{
$pathProperties += [pscustomobject]@{
Path = (Join-Path $script:path_FileUserShared "psf_config.json")
Properties = $fileUserSharedSettings
Changed = $false
}
}
}
if ($Scope -band 64)
{
$fileSystemSettings = @()
if (Test-Path (Join-Path $script:path_FileSystem "psf_config.json")) { $fileSystemSettings = Get-Content (Join-Path $script:path_FileSystem "psf_config.json") -Encoding UTF8 | ConvertFrom-Json }
if ($fileSystemSettings)
{
$pathProperties += [pscustomobject]@{
Path = (Join-Path $script:path_FileSystem "psf_config.json")
Properties = $fileSystemSettings
Changed = $false
}
}
}
#endregion Initialize Collection
$common = 'PSPath', 'PSParentPath', 'PSChildName', 'PSDrive', 'PSProvider'
}
process
{
if (Test-FunctionInterrupt) { return }
# Silently skip since no action necessary
if (-not ($pathProperties -or $registryProperties)) { return }
foreach ($item in $ConfigurationItem)
{
# Registry
foreach ($hive in ($registryProperties | Where-Object { $_.PSObject.Properties.Name -eq $item.FullName }))
{
Remove-ItemProperty -Path $hive.PSPath -Name $item.FullName
}
# Prepare file
foreach ($fileConfig in ($pathProperties | Where-Object { $_.Properties.FullName -contains $item.FullName }))
{
$fileConfig.Properties = $fileConfig.Properties | Where-Object FullName -NE $item.FullName
$fileConfig.Changed = $true
}
}
foreach ($item in $FullName)
{
# Ignore string-casted configurations
if ($item -ceq "Sqlcollaborative.Dbatools.Configuration.Config") { continue }
# Registry
foreach ($hive in ($registryProperties | Where-Object { $_.PSObject.Properties.Name -eq $item }))
{
Remove-ItemProperty -Path $hive.PSPath -Name $item
}
# Prepare file
foreach ($fileConfig in ($pathProperties | Where-Object { $_.Properties.FullName -contains $item }))
{
$fileConfig.Properties = $fileConfig.Properties | Where-Object FullName -NE $item
$fileConfig.Changed = $true
}
}
if ($Module)
{
$compoundName = "{0}.{1}" -f $Module, $Name
# Registry
foreach ($hive in ($registryProperties | Where-Object { $_.PSObject.Properties.Name -like $compoundName }))
{
foreach ($propName in $hive.PSObject.Properties.Name)
{
if ($propName -in $common) { continue }
if ($propName -like $compoundName)
{
Remove-ItemProperty -Path $hive.PSPath -Name $propName
}
}
}
# Prepare file
foreach ($fileConfig in ($pathProperties | Where-Object { $_.Properties.FullName -like $compoundName }))
{
$fileConfig.Properties = $fileConfig.Properties | Where-Object FullName -NotLike $compoundName
$fileConfig.Changed = $true
}
}
}
end
{
if (Test-FunctionInterrupt) { return }
foreach ($fileConfig in $pathProperties)
{
if (-not $fileConfig.Changed) { continue }
if ($fileConfig.Properties)
{
$fileConfig.Properties | ConvertTo-Json | Set-Content -Path $fileConfig.Path -Encoding UTF8
}
else
{
Remove-Item $fileConfig.Path
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaAgDatabase {
<#
.SYNOPSIS
Adds a database to an availability group on a SQL Server instance.
.DESCRIPTION
Adds a database to an availability group on a SQL Server instance.
Before joining the replica databases to the availablity group, the databases will be initialized with automatic seeding or full/log backup.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
This should be the primary replica.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Database
The database or databases to add.
.PARAMETER AvailabilityGroup
The availability group where the databases will be added.
.PARAMETER InputObject
Enables piping from Get-DbaDatabase, Get-DbaDbSharePoint and more.
.PARAMETER SeedingMode
Specifies how the secondary replica will be initially seeded.
Automatic enables direct seeding. This method will seed the secondary replica over the network. This method does not require you to backup and restore a copy of the primary database on the replica.
Manual requires you to create a backup of the database on the primary replica and manually restore that backup on the secondary replica.
If not specified, the setting from the availability group replica will be used. Otherwise the setting will be updated.
.PARAMETER SharedPath
The network share where the backups will be backed up and restored from.
Each SQL Server service account must have access to this share.
NOTE: If a backup / restore is performed, the backups will be left in tact on the network share.
.PARAMETER UseLastBackup
Use the last full backup of database.
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Add-DbaAgDatabase
.EXAMPLE
PS C:\> Add-DbaAgDatabase -SqlInstance sql2017a -AvailabilityGroup ag1 -Database db1, db2 -Confirm
Adds db1 and db2 to ag1 on sql2017a. Prompts for confirmation.
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2017a | Out-GridView -Passthru | Add-DbaAgDatabase -AvailabilityGroup ag1
Adds selected databases from sql2017a to ag1
.EXAMPLE
PS C:\> Get-DbaDbSharePoint -SqlInstance sqlcluster | Add-DbaAgDatabase -AvailabilityGroup SharePoint
Adds SharePoint databases as found in SharePoint_Config on sqlcluster to ag1 on sqlcluster
.EXAMPLE
PS C:\> Get-DbaDbSharePoint -SqlInstance sqlcluster -ConfigDatabase SharePoint_Config_2019 | Add-DbaAgDatabase -AvailabilityGroup SharePoint
Adds SharePoint databases as found in SharePoint_Config_2019 on sqlcluster to ag1 on sqlcluster
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(Mandatory)]
[string]$AvailabilityGroup,
[string[]]$Database,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[ValidateSet('Automatic', 'Manual')]
[string]$SeedingMode,
[string]$SharedPath,
[switch]$UseLastBackup,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance)) {
if ((Test-Bound -Not -ParameterName Database) -or (Test-Bound -Not -ParameterName AvailabilityGroup)) {
Stop-Function -Message "You must specify one or more databases and one Availability Group when using the SqlInstance parameter."
return
}
}
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $instance -SqlCredential $SqlCredential -Database $Database
}
foreach ($db in $InputObject) {
$allbackups = @{
}
$Primary = $db.Parent
# check primary, should be run against primary
$ag = Get-DbaAvailabilityGroup -SqlInstance $db.Parent -AvailabilityGroup $AvailabilityGroup
if ($ag.AvailabilityDatabases.Name -contains $db.Name) {
Stop-Function -Message "$($db.Name) is already joined to $($ag.Name)" -Continue
}
$secondaryReplicas = $ag.AvailabilityReplicas | Where-Object Role -eq Secondary | Select-Object -Unique -ExpandProperty Name
if ($SeedingMode -eq "Automatic") {
# first check
if ($Pscmdlet.ShouldProcess($Primary, "Backing up $db to NUL")) {
$null = Backup-DbaDatabase -BackupFileName NUL -SqlInstance $Primary -SqlCredential $SqlCredential -Database $db
}
}
if ($Pscmdlet.ShouldProcess($ag.Parent.Name, "Adding availability group $db to $($db.Parent.Name)")) {
try {
$agdb = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityDatabase($ag, $db.Name)
# something is up with .net create(), force a stop
Invoke-Create -Object $agdb
Get-DbaAgDatabase -SqlInstance $ag.Parent -Database $db.Name
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
foreach ($replica in $secondaryReplicas) {
$agreplica = Get-DbaAgReplica -SqlInstance $Primary -AvailabilityGroup $ag.name -Replica $replica
if ($SeedingMode) {
$agreplica.SeedingMode = $SeedingMode
$agreplica.alter()
}
$agreplica.refresh()
$SeedingModeReplica = $agreplica.SeedingMode
$primarydb = Get-DbaDatabase -SqlInstance $Primary -SqlCredential $SqlCredential -Database $db.name
if ($SeedingModeReplica -ne 'Automatic') {
try {
if (-not $allbackups[$db]) {
if ($UseLastBackup) {
$allbackups[$db] = Get-DbaBackupHistory -SqlInstance $primarydb.Parent -Database $primarydb.Name -IncludeCopyOnly -Last -EnableException
} else {
$fullbackup = $primarydb | Backup-DbaDatabase -BackupDirectory $SharedPath -Type Full -EnableException
$logbackup = $primarydb | Backup-DbaDatabase -BackupDirectory $SharedPath -Type Log -EnableException
$allbackups[$db] = $fullbackup, $logbackup
}
Write-Message -Level Verbose -Message "Backups still exist on $SharedPath"
}
if ($Pscmdlet.ShouldProcess("$Secondary", "restoring full and log backups of $primarydb from $Primary")) {
# keep going to ensure output is shown even if dbs aren't added well.
$null = $allbackups[$db] | Restore-DbaDatabase -SqlInstance $replica -WithReplace -NoRecovery -TrustDbBackupHistory -EnableException
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
$replicadb = Get-DbaAgDatabase -SqlInstance $replica -SqlCredential $SqlCredential -Database $db.Name -AvailabilityGroup $ag.Name #credential of secondary !!
if ($replicadb -and -not ($SeedingModeReplica -eq 'Automatic')) {
if ($Pscmdlet.ShouldProcess($ag.Parent.Name, "Joining availability group $db to $($db.Parent.Name)")) {
$timeout = 1
do {
try {
Write-Message -Level Verbose -Message "Trying to add $($replicadb.Name) to $replica"
$timeout++
$replicadb.JoinAvailablityGroup()
$replicadb.Refresh()
Start-Sleep -Seconds 1
} catch {
Stop-Function -Message "Error joining database to availability group" -ErrorRecord $_ -Continue
}
} while (-not $replicadb.IsJoined -and $timeout -lt 10)
if ($replicadb.IsJoined) {
$replicadb
} else {
Stop-Function -Continue -Message "Could not join $($replicadb.Name) to $replica"
}
}
} else {
$replicadb
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaAgListener {
<#
.SYNOPSIS
Adds a listener to an availability group on a SQL Server instance.
.DESCRIPTION
Adds a listener to an availability group on a SQL Server instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER AvailabilityGroup
The Availability Group to which a listener will be bestowed upon.
.PARAMETER IPAddress
Sets the IP address of the availability group listener.
.PARAMETER SubnetMask
Sets the subnet IP mask of the availability group listener. Defaults to 255.255.255.0.
.PARAMETER Port
Sets the port number used to communicate with the availability group. Defaults to 1433.
.PARAMETER Dhcp
Indicates whether the listener uses DHCP.
.PARAMETER Passthru
Don't create the listener, just pass thru an object that can be further customized before creation.
.PARAMETER InputObject
Enables piping from Get-DbaAvailabilityGroup
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Add-DbaAgListener
.EXAMPLE
PS C:\> Add-DbaAgListener -SqlInstance sql2017 -AvailabilityGroup SharePoint -IPAddress 10.0.20.20
Creates a listener on 10.0.20.20 port 1433 for the SharePoint availability group on sql2017.
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql2017 -AvailabilityGroup availabilitygroup1 | Add-DbaAgListener -Dhcp
Creates a listener on port 1433 with a dynamic IP for the group1 availability group on sql2017.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[ipaddress]$IPAddress,
[ipaddress]$SubnetMask = "255.255.255.0",
[int]$Port = 1433,
[switch]$Dhcp,
[switch]$Passthru,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName AvailabilityGroup)) {
Stop-Function -Message "You must specify one or more databases and one or more Availability Groups when using the SqlInstance parameter."
return
}
if ($SqlInstance) {
$InputObject += Get-DbaAvailabilityGroup -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup
}
foreach ($ag in $InputObject) {
if ((Test-Bound -Not -ParameterName Name)) {
$Name = $ag.Name
}
if ($Pscmdlet.ShouldProcess($ag.Parent.Name, "Adding $($IPAddress.IPAddressToString) to $($ag.Name)")) {
try {
$aglistener = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityGroupListener -ArgumentList $ag, $Name
$aglistener.PortNumber = $Port
$listenerip = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityGroupListenerIPAddress -ArgumentList $aglistener
if (Test-Bound -ParameterName IPAddress) {
$listenerip.IPAddress = $IPAddress.IPAddressToString
$listenerip.SubnetMask = $SubnetMask.IPAddressToString
}
$listenerip.IsDHCP = $Dhcp
$aglistener.AvailabilityGroupListenerIPAddresses.Add($listenerip)
if ($Passthru) {
return $aglistener
} else {
# something is up with .net create(), force a stop
Invoke-Create -Object $aglistener
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
}
Get-DbaAgListener -SqlInstance $ag.Parent -AvailabilityGroup $ag.Name -Listener $Name
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaAgReplica {
<#
.SYNOPSIS
Adds a replica to an availability group on a SQL Server instance.
.DESCRIPTION
Adds a replica to an availability group on a SQL Server instance.
Automatically creates a database mirroring endpoint if required.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Name
The name of the replica. Defaults to the SQL Server instance name.
.PARAMETER AvailabilityGroup
The Availability Group to which a replica will be bestowed upon.
.PARAMETER AvailabilityMode
Sets the availability mode of the availability group replica. Options are: AsynchronousCommit and SynchronousCommit. SynchronousCommit is default.
.PARAMETER FailoverMode
Sets the failover mode of the availability group replica. Options are Automatic and Manual. Automatic is default.
.PARAMETER BackupPriority
Sets the backup priority availability group replica. Default is 50.
.PARAMETER Endpoint
By default, this command will attempt to find a DatabaseMirror endpoint. If one does not exist, it will create it.
If an endpoint must be created, the name "hadr_endpoint" will be used. If an alternative is preferred, use Endpoint.
.PARAMETER Passthru
Don't create the replica, just pass thru an object that can be further customized before creation.
.PARAMETER InputObject
Enables piping from Get-DbaAvailabilityGroup.
.PARAMETER ConnectionModeInPrimaryRole
Specifies the connection intent modes of an Availability Replica in primary role. AllowAllConnections by default.
.PARAMETER ConnectionModeInSecondaryRole
Specifies the connection modes of an Availability Replica in secondary role. AllowAllConnections by default.
.PARAMETER ReadonlyRoutingConnectionUrl
Sets the read only routing connection url for the availability replica.
.PARAMETER SeedingMode
Specifies how the secondary replica will be initially seeded.
Automatic enables direct seeding. This method will seed the secondary replica over the network. This method does not require you to backup and restore a copy of the primary database on the replica.
Manual requires you to create a backup of the database on the primary replica and manually restore that backup on the secondary replica.
.PARAMETER Certificate
Specifies that the endpoint is to authenticate the connection using the certificate specified by certificate_name to establish identity for authorization.
The far endpoint must have a certificate with the public key matching the private key of the specified certificate.
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Add-DbaAgReplica
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql2017a -AvailabilityGroup SharePoint | Add-DbaAgReplica -SqlInstance sql2017b
Adds sql2017b to the SharePoint availability group on sql2017a
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql2017a -AvailabilityGroup SharePoint | Add-DbaAgReplica -SqlInstance sql2017b -FailoverMode Manual
Adds sql2017b to the SharePoint availability group on sql2017a with a manual failover mode.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string]$AvailabilityGroup,
[string]$Name,
[ValidateSet('AsynchronousCommit', 'SynchronousCommit')]
[string]$AvailabilityMode = "SynchronousCommit",
[ValidateSet('Automatic', 'Manual', 'External')]
[string]$FailoverMode = "Automatic",
[int]$BackupPriority = 50,
[ValidateSet('AllowAllConnections', 'AllowReadWriteConnections')]
[string]$ConnectionModeInPrimaryRole = 'AllowAllConnections',
[ValidateSet('AllowAllConnections', 'AllowNoConnections', 'AllowReadIntentConnectionsOnly')]
[string]$ConnectionModeInSecondaryRole = 'AllowAllConnections',
[ValidateSet('Automatic', 'Manual')]
[string]$SeedingMode = 'Automatic',
[string]$Endpoint,
[switch]$Passthru,
[string]$ReadonlyRoutingConnectionUrl,
[string]$Certificate,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup]$InputObject,
[switch]$EnableException
)
process {
if (-not $AvailabilityGroup -and -not $InputObject) {
Stop-Function -Message "You must specify either AvailabilityGroup or pipe in an availabilty group to continue."
return
}
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if ($Certificate) {
$cert = Get-DbaDbCertificate -SqlInstance $server -Certificate $Certificate
if (-not $cert) {
Stop-Function -Message "Certificate $Certificate does not exist on $instance" -ErrorRecord $_ -Target $Certificate -Continue
}
}
if ($AvailabilityGroup) {
$InputObject = Get-DbaAvailabilityGroup -SqlInstance $server -AvailabilityGroup $AvailabilityGroup
}
$ep = Get-DbaEndpoint -SqlInstance $server -Type DatabaseMirroring
if (-not $ep) {
if ($Pscmdlet.ShouldProcess($server.Name, "Adding endpoint named $Endpoint to $instance")) {
if (-not $Endpoint) {
$Endpoint = "hadr_endpoint"
}
$ep = New-DbaEndpoint -SqlInstance $server -Name hadr_endpoint -Type DatabaseMirroring -EndpointEncryption Supported -EncryptionAlgorithm Aes -Certificate $Certificate
$null = $ep | Start-DbaEndpoint
}
}
if ((Test-Bound -Not -ParameterName Name)) {
$Name = $server.DomainInstanceName
}
if ($Pscmdlet.ShouldProcess($server.Name, "Creating a replica for $($InputObject.Name) named $Name")) {
try {
$replica = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityReplica -ArgumentList $InputObject, $Name
$replica.EndpointUrl = $ep.Fqdn
$replica.FailoverMode = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaFailoverMode]::$FailoverMode
$replica.AvailabilityMode = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaAvailabilityMode]::$AvailabilityMode
if ($server.EngineEdition -ne "Standard") {
$replica.ConnectionModeInPrimaryRole = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaConnectionModeInPrimaryRole]::$ConnectionModeInPrimaryRole
$replica.ConnectionModeInSecondaryRole = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaConnectionModeInSecondaryRole]::$ConnectionModeInSecondaryRole
}
$replica.BackupPriority = $BackupPriority
if ($ReadonlyRoutingConnectionUrl) {
$replica.ReadonlyRoutingConnectionUrl = $ReadonlyRoutingConnectionUrl
}
if ($SeedingMode -and $server.VersionMajor -ge 13) {
$replica.SeedingMode = $SeedingMode
}
if ($Passthru) {
return $replica
}
$defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'AvailabilityGroup', 'Name', 'Role', 'RollupSynchronizationState', 'AvailabilityMode', 'BackupPriority', 'EndpointUrl', 'SessionTimeout', 'FailoverMode', 'ReadonlyRoutingList'
$InputObject.AvailabilityReplicas.Add($replica)
$agreplica = $InputObject.AvailabilityReplicas[$Name]
Add-Member -Force -InputObject $agreplica -MemberType NoteProperty -Name ComputerName -value $agreplica.Parent.ComputerName
Add-Member -Force -InputObject $agreplica -MemberType NoteProperty -Name InstanceName -value $agreplica.Parent.InstanceName
Add-Member -Force -InputObject $agreplica -MemberType NoteProperty -Name SqlInstance -value $agreplica.Parent.SqlInstance
Add-Member -Force -InputObject $agreplica -MemberType NoteProperty -Name AvailabilityGroup -value $agreplica.Parent.Name
Add-Member -Force -InputObject $agreplica -MemberType NoteProperty -Name Replica -value $agreplica.Name # backwards compat
Select-DefaultView -InputObject $agreplica -Property $defaults
} catch {
$msg = $_.Exception.InnerException.InnerException.Message
if (-not $msg) {
$msg = $_
}
Stop-Function -Message $msg -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaCmsRegServer {
<#
.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-DbaCmsRegServer 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
Tags: RegisteredServer, CMS
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Add-DbaCmsRegServer
.EXAMPLE
PS C:\> Add-DbaCmsRegServer -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
PS C:\> Add-DbaCmsRegServer -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
PS C:\> Add-DbaCmsRegServer -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
PS C:\> Get-DbaCmsRegServerGroup -SqlInstance sql2008 -Group hr\Seattle | Add-DbaCmsRegServer -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-DbaCmsRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group.Name
} else {
$InputObject += Get-DbaCmsRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
}
} else {
$InputObject += Get-DbaCmsRegServerGroup -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-DbaCmsRegServer -SqlInstance $server -Name $Name -ServerName $ServerName
} catch {
Stop-Function -Message "Failed to add $ServerName on $($parentserver.SqlInstance)" -ErrorRecord $_ -Continue
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Add-DbaRegisteredServer
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaCmsRegServerGroup {
<#
.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 flexibility, look into Import-DbaCmsRegServer which accepts multiple kinds of input and allows you to add reg servers and groups from different CMS.
.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-DbaCmsRegServerGroup 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
Tags: RegisteredServer, CMS
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Add-DbaCmsRegServerGroup
.EXAMPLE
PS C:\> Add-DbaCmsRegServerGroup -SqlInstance sql2012 -Name HR
Creates a registered server group called HR, in the root of sql2012's CMS
.EXAMPLE
PS C:\> Add-DbaCmsRegServerGroup -SqlInstance sql2012, sql2014 -Name sub-folder -Group HR
Creates a registered server group on sql2012 and sql2014 called sub-folder within the HR group
.EXAMPLE
PS C:\> Get-DbaCmsRegServerGroup -SqlInstance sql2012, sql2014 -Group HR | Add-DbaCmsRegServerGroup -Name sub-folder
Creates a registered server group on sql2012 and sql2014 called sub-folder 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-DbaCmsRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
} else {
$InputObject += Get-DbaCmsRegServerGroup -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-DbaCmsRegServerGroup -SqlInstance $parentserver.ServerConnection.SqlConnectionObject -Group (Get-RegServerGroupReverseParse -object $newgroup)
$parentserver.ServerConnection.Disconnect()
} catch {
Stop-Function -Message "Failed to add $reggroup on $server" -ErrorRecord $_ -Continue
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Add-DbaRegisteredServerGroup
}
}
#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 instance or instances. Defaults to localhost.
.PARAMETER Credential
Allows you to login to $ComputerName using alternative credentials.
.PARAMETER SecurePassword
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> 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
PS C:\> 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 = "Low")]
param (
[Alias("ServerInstance", "SqlServer", "SqlInstance")]
[DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[Alias("Password")]
[SecureString]$SecurePassword,
[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 oh well
$bytes = [System.IO.File]::ReadAllBytes($Path)
$Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$Certificate.Import($bytes, $SecurePassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
} catch {
Stop-Function -Message "Can't import certificate." -ErrorRecord $_
return
}
}
#region Remoting Script
$scriptBlock = {
param (
$CertificateData,
[SecureString]$SecurePassword,
$Store,
$Folder
)
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($CertificateData, $SecurePassword, [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, $SecurePassword)
} 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, $SecurePassword, $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
}
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Add-DbaDbMirrorMonitor {
<#
.SYNOPSIS
Creates a database mirroring monitor job that periodically updates the mirroring status for every mirrored database on the server instance.
.DESCRIPTION
Creates a database mirroring monitor job that periodically updates the mirroring status for every mirrored database on the server instance.
Basically executes sp_dbmmonitoraddmonitoring.
.PARAMETER SqlInstance
The target SQL Server instance
.PARAMETER SqlCredential
Login to the target instance using alternate Windows or SQL Login Authentication. 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: Mirror, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Add-DbaDbMirrorMonitor
.EXAMPLE
PS C:\> Add-DbaDbMirrorMonitor -SqlInstance sql2008, sql2012
Creates a database mirroring monitor job that periodically updates the mirroring status for every mirrored database on sql2008 and sql2012.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[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
}
if ($Pscmdlet.ShouldProcess($instance, "add mirror monitoring")) {
try {
$server.Query("msdb.dbo.sp_dbmmonitoraddmonitoring")
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
MonitorStatus = "Added"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Add-DbaPfDataCollectorCounter
.EXAMPLE
PS C:\> 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
PS C:\> 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
}
}
}
}
}
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 does not 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).
Passing in NUL as the BackupFileName will backup to the NUL: device
.PARAMETER TimeStampFormat
By default the command timestamps backups using the format yyyyMMddHHmm. Using this parameter this can be overridden. The timestamp format should be defined using the Get-Date formats, illegal formats will cause an error to be thrown
.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 ReplaceInName
If this switch is set, the following list of strings will be replaced in the BackupFileName and BackupDirectory strings:
instancename - will be replaced with the instance Name
servername - will be replaced with the server name
dbname - will be replaced with the database name
timestamp - will be replaced with the timestamp (either the default, or the format provided)
backuptype - will be replaced with Full, Log or Differential as appropriate
.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 base container 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, only needed if using Storage access keys
If using SAS credentials, the command will look for a credential with a name matching the AzureBaseUrl
.PARAMETER NoRecovery
This is passed in to perform a tail log backup if needed
.PARAMETER BuildPath
By default this command will not attempt to create missing paths, this switch will change the behaviour so that it will
.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 cannot 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> 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 default backup directory.
.EXAMPLE
PS C:\> Backup-DbaDatabase -SqlInstance sql2016 -BackupDirectory C:\temp -Database AdventureWorks2014 -Type Full
Backs up AdventureWorks2014 to sql2016 C:\temp folder.
.EXAMPLE
PS C:\> 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.
.EXAMPLE
PS C:\> Backup-DbaDatabase -SqlInstance sql2016 -AzureBaseUrl https://dbatoolsaz.blob.core.windows.net/azbackups/ -Type Full
Performs a full backup of all databases on the sql2016 instance to the https://dbatoolsaz.blob.core.windows.net/azbackups/ container on Azure blog storage using the Shared Access Signature sql credential "https://dbatoolsaz.blob.core.windows.net/azbackups" registered on the sql2016 instance.
.EXAMPLE
PS C:\> Backup-Dbadatabase -SqlInstance Server1\Prod -Database db1 -BackupDirectory \\filestore\backups\servername\instancename\dbname\backuptype -Type Full -ReplaceInName
Performs a full backup of db1 into the folder \\filestore\backups\server1\prod\db1
.EXAMPLE
PS C:\> Backup-Dbadatabase -SqlInstance Server1\Prod -BackupDirectory \\filestore\backups\servername\instancename\dbname\backuptype -BackupFileName dbname-backuptype-timestamp.trn -Type Log -ReplaceInName
Performs a log backup for every database. For the database db1 this would results in backup files in \\filestore\backups\server1\prod\db1\Log\db1-log-31102018.trn
.EXAMPLE
PS C:\> Backup-DbaDatabase -SqlInstance Sql2017 -Database master -BackupFileName NUL
Performs a backup of master, but sends the output to the NUL device (ie; throws it away)
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] #For AzureCredential
param (
[parameter(ParameterSetName = "Pipe", Mandatory)]
[DbaInstanceParameter]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[string[]]$BackupDirectory,
[string]$BackupFileName,
[switch]$ReplaceInName,
[switch]$CopyOnly,
[ValidateSet('Full', 'Log', 'Differential', 'Diff', 'Database')]
[string]$Type = 'Database',
[parameter(ParameterSetName = "NoPipe", Mandatory, ValueFromPipeline)]
[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,
[string]$TimeStampFormat,
[switch]$IgnoreFileChecks,
[switch]$OutputScriptOnly,
[Alias('Silent')]
[switch]$EnableException
)
begin {
if (-not (Test-Bound 'TimeStampFormat')) {
Write-Message -Message 'Setting Default timestampformat' -Level Verbose
$TimeStampFormat = "yyyyMMddHHmm"
}
if ($SqlInstance.length -ne 0) {
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 ($null -eq $BackupDirectory -and $backupfileName -ne 'NUL') {
Write-Message -Message 'No backupfolder passed in, setting it to instance default' -Level Verbose
$BackupDirectory = (Get-DbaDefaultPath -SqlInstance $SqlInstance).Backup
}
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 '' -and $True -ne $ReplaceInFile) {
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) {
$AzureBaseUrl = $AzureBaseUrl.Trim("/")
if ('' -ne $AzureCredential) {
Write-Message -Message "Azure Credential name passed in, will proceed assuming it's value" -Level Verbose
$FileCount = 1
} else {
Write-Message -Message "AzureUrl and no credential, testing for SAS credential"
if (Get-DbaCredential -SqlInstance $server -Name $AzureBaseUrl) {
Write-Message -Message "Found a SAS backup credental" -Level Verbose
} else {
Stop-Function -Message "You must provide the credential name for the Azure Storage Account"
return
}
}
$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 ($database.EncryptionEnabled) {
Write-Message -Level Warning -Message "$dbname is enabled for encryption, will not compress"
$backup.CompressionOption = 2
} elseif ($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 -and $null -ne $AzureCredential) {
$backup.CredentialName = $AzureCredential
}
Write-Message -Level Verbose -Message "Building file name"
$BackupFinalName = ''
$FinalBackupPath = @()
$timestamp = Get-Date -Format $TimeStampFormat
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 {
Write-Message -Level VeryVerbose -Message "Setting filename - $timestamp"
$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 ($True -eq $ReplaceInName) {
for ($i = 0; $i -lt $FinalBackupPath.count; $i++) {
$FinalBackupPath[$i] = $FinalBackupPath[$i] -replace ('dbname', $dbname)
$FinalBackupPath[$i] = $FinalBackupPath[$i] -replace ('instancename', $SqlInstance.InstanceName)
$FinalBackupPath[$i] = $FinalBackupPath[$i] -replace ('servername', $SqlInstance.ComputerName)
$FinalBackupPath[$i] = $FinalBackupPath[$i] -replace ('timestamp', $timestamp)
$FinalBackupPath[$i] = $FinalBackupPath[$i] -replace ('backuptype', $outputType)
}
}
if (-not $IgnoreFileChecks -and -not $AzureBaseUrl) {
$parentPaths = ($FinalBackupPath | ForEach-Object { Split-Path $_ } | Select-Object -Unique)
foreach ($parentPath in $parentPaths) {
if (-not (Test-DbaPath -SqlInstance $server -Path $parentPath)) {
if (($BuildPath -eq $true) -or ($CreateFolder -eq $True)) {
$null = New-DbaDirectory -SqlInstance $server -Path $parentPath
} else {
$failreason += "SQL Server cannot check if $parentPath exists. You can try disabling 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")) {
$CurrentLsn = (Read-DbaTransactionLog -SqlInstance $server -Database $dbname -RowLimit 1).'Current LSN'
$NumericLSN = (Convert-DbaLSN -LSN $CurrentLsn).Numeric
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 -LastLsn $NumericLsn -RecoveryFork $database.RecoveryForkGuid | 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" -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
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. 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
Enables piping from Get-DbaDbCertificate
.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
Tags: Migration, Certificate
Author: Jess Pomfret (@jpomfret)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Backup-DbaDbCertificate -SqlInstance Server1
Exports all the certificates on the specified SQL Server to the default data path for the instance.
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> $EncryptionPassword = ConvertTo-SecureString -AsPlainText "GoodPass1234!!" -force
PS C:\> $DecryptionPassword = ConvertTo-SecureString -AsPlainText "Password4567!!" -force
PS C:\> 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
PS C:\> Backup-DbaDbCertificate -SqlInstance Server1 -Path \\Server1\Certificates
Exports all certificates on the specified SQL Server to the specified path.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaDbCertificate -SqlInstance sql2016 | Backup-DbaDbCertificate
Exports all certificates found on sql2016 to the default data directory.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess, ConfirmImpact = 'Low')]
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,
[Security.SecureString]$EncryptionPassword,
[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 (-not $EncryptionPassword -and $DecryptionPassword) {
Stop-Function -Message "If you specify a 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
}
$actualPath = "$actualPath".TrimEnd('\')
$fullCertName = "$actualPath\$certName$Suffix"
$exportPathKey = "$fullCertName.pvk"
if (!(Test-DbaPath -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 }
if ($SqlInstance) {
$InputObject += Get-DbaDbCertificate -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase
}
foreach ($cert in $InputObject) {
if ($cert.Name.StartsWith("##")) {
Write-Message -Level Output -Message "Skipping system cert $cert"
} else {
export-cert $cert
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Backup-DbaDbMasterKey {
<#
.SYNOPSIS
Backs up specified database master key.
.DESCRIPTION
Backs up specified database master key.
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.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 Credential
Pass a credential object for the password
.PARAMETER SecurePassword
The password to encrypt the exported key. This must be a SecureString.
.PARAMETER InputObject
Database object piped in from Get-DbaDatabase
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Backup-DbaDbMasterKey -SqlInstance server1\sql2016
```
ComputerName : SERVER1
InstanceName : SQL2016
SqlInstance : SERVER1\SQL2016
Database : master
Filename : E:\MSSQL13.SQL2016\MSSQL\Backup\server1$sql2016-master-20170614162311.key
Status : Success
```
Prompts for export password, then logs into server1\sql2016 with Windows credentials then backs up all database keys to the default backup directory.
.EXAMPLE
PS C:\> Backup-DbaDbMasterKey -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(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[string[]]$Database,
[string[]]$ExcludeDatabase,
[Alias("Password")]
[Security.SecureString]$SecurePassword,
[string]$Path,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
begin {
if ($Credential) {
$SecurePassword = $Credential.Password
}
}
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase
}
foreach ($db in $InputObject) {
$server = $db.Parent
if (Test-Bound -ParameterName Path -Not) {
$Path = $server.BackupDirectory
}
if (-not $Path) {
Stop-Function -Message "Path discovery failed. Please explicitly specify -Path" -Target $server -Continue
}
if (!(Test-DbaPath -SqlInstance $server -Path $Path)) {
Stop-Function -Message "$instance cannot access $Path" -Target $server -ErrorRecord $_ -Continue
}
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 (-not $SecurePassword -and -not $Credential) {
$SecurePassword = Read-Host -AsSecureString -Prompt "You must enter Service Key password for $instance"
$SecurePassword2 = Read-Host -AsSecureString -Prompt "Type the password again"
if (([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($SecurePassword))) -ne ([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($SecurePassword2)))) {
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"
if ($Pscmdlet.ShouldProcess($instance, "Backing up master key to $filename")) {
try {
$masterkey.Export($filename, [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($SecurePassword)))
$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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Backup-DbaDatabaseMasterKey
}
}
function Clear-DbaConnectionPool {
<#
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Clear-DbaConnectionPool
.EXAMPLE
PS C:\> Clear-DbaConnectionPool
Clears all local connection pools.
.EXAMPLE
PS C:\> Clear-DbaConnectionPool -ComputerName workstation27
Clears all connection pools on workstation27.
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Clear-DbaSqlConnectionPool
}
}
function Clear-DbaLatchStatistics {
<#
.SYNOPSIS
Clears Latch Statistics
.DESCRIPTION
Reset the aggregated statistics - basically just executes DBCC SQLPERF (N'sys.dm_os_latch_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: LatchStatistic, Waits
Author: Patrick Flynn (@sqllensman)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Clear-DbaLatchStatistics
.EXAMPLE
PS C:\> Clear-DbaLatchStatistics -SqlInstance sql2008, sqlserver2012
After confirmation, clears latch statistics on servers sql2008 and sqlserver2012
.EXAMPLE
PS C:\> Clear-DbaLatchStatistics -SqlInstance sql2008, sqlserver2012 -Confirm:$false
Clears latch statistics on servers sql2008 and sqlserver2012, without prompting
.EXAMPLE
PS C:\> 'sql2008','sqlserver2012' | Clear-DbaLatchStatistics
After confirmation, clears latch statistics on servers sql2008 and sqlserver2012
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Clear-DbaLatchStatistics -SqlInstance sql2008 -SqlCredential $cred
Connects using sqladmin credential and clears latch statistics on servers sql2008 and sqlserver2012
#>
[CmdletBinding(ConfirmImpact = 'High', SupportsShouldProcess)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Singular Noun doesn't make sense")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
Write-Message -Level Verbose -Message "Attempting to connect 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_latch_stats")) {
try {
$server.Query("DBCC SQLPERF (N'sys.dm_os_latch_stats' , CLEAR);")
$status = "Success"
} catch {
$status = $_.Exception
}
[PSCustomObject]@{
ComputerName = $server.NetName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Status = $status
}
}
}
}
}
function Clear-DbaPlanCache {
<#
.SYNOPSIS
Removes ad-hoc and prepared plan caches is single use plans are over defined threshold.
.DESCRIPTION
Checks ad-hoc 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 or instances.
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Clear-DbaPlanCache
.EXAMPLE
PS C:\> Clear-DbaPlanCache -SqlInstance sql2017 -Threshold 200
Logs into the SQL Server instance "sql2017" and removes plan caches if over 200 MB.
.EXAMPLE
PS C:\> Clear-DbaPlanCache -SqlInstance sql2017 -SqlCredential 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.
.EXAMPLE
PS C:\> Find-DbaInstance -ComputerName localhost | Get-DbaPlanCache | Clear-DbaPlanCache -Threshold 200
Scans localhost for instances using the browser service, traverses all instances and gets the plan cache for each, clears them out if they are above 200 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")) {
try {
$server = Connect-SqlInstance -SqlInstance $result.SqlInstance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$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-DbaWaitStatistics {
<#
.SYNOPSIS
Clears wait statistics
.DESCRIPTION
Reset the aggregated statistics - basically just executes DBCC SQLPERF (N'sys.dm_os_wait_stats', CLEAR)
.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 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, Waits
Author: Chrissy LeMaire (@cl)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Clear-DbaWaitStatistics
.EXAMPLE
PS C:\> Clear-DbaWaitStatistics -SqlInstance sql2008, sqlserver2012
After confirmation, clears wait stats on servers sql2008 and sqlserver2012
.EXAMPLE
PS C:\> Clear-DbaWaitStatistics -SqlInstance sql2008, sqlserver2012 -Confirm:$false
Clears wait stats on servers sql2008 and sqlserver2012, without prompting
#>
[CmdletBinding(ConfirmImpact = 'High', SupportsShouldProcess)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Singular Noun doesn't make sense")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstance[]]$SqlInstance,
[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
}
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
The target SQL Server instance or instances. 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(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 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.
.PARAMETER DisableException
By default in most of our commands, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This command, however, gifts you with "sea of red" exceptions, by default, because it is useful for advanced scripting.
Using this switch turns our "nice by default" feature on which makes errors into pretty warnings.
.NOTES
Tags: Connect, Connection
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Connect-DbaInstance
.EXAMPLE
PS C:\> Connect-DbaInstance -SqlInstance sql2014
Creates an SMO Server object that connects using Windows Authentication
.EXAMPLE
PS C:\> $wincred = Get-Credential ad\sqladmin
PS C:\> Connect-DbaInstance -SqlInstance sql2014 -SqlCredential $wincred
Creates an SMO Server object that connects using alternative Windows credentials
.EXAMPLE
PS C:\> $sqlcred = Get-Credential sqladmin
PS C:\> $server = Connect-DbaInstance -SqlInstance sql2014 -SqlCredential $sqlcred
Login to sql2014 as SQL login sqladmin.
.EXAMPLE
PS C:\> $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
PS C:\> $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
PS C:\> $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
PS C:\> $server = Connect-DbaInstance sql2016 -ApplicationIntent ReadOnly
Connects with ReadOnly ApplicationIntent.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[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,
[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,
[switch]$DisableException
)
begin {
if ($DisableException) {
$EnableException = $false
} else {
$EnableException = $true
}
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Connect-DbaServer
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) {
#region Safely convert input into instance parameters
if ($instance.GetType() -eq [Sqlcollaborative.Dbatools.Parameter.DbaInstanceParameter]) {
[DbaInstanceParameter]$ConvertedSqlInstance = $instance
if ($ConvertedSqlInstance.Type -like "SqlConnection") {
[DbaInstanceParameter]$ConvertedSqlInstance = New-Object Microsoft.SqlServer.Management.Smo.Server($ConvertedSqlInstance.InputObject)
}
} else {
[DbaInstanceParameter]$ConvertedSqlInstance = [DbaInstanceParameter]($instance | Select-Object -First 1)
if ($instance.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
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) {
if (-not $server.NetName -or $SqlInstance -match '\.') {
$parsedcomputername = $ConvertedSqlInstance.ComputerName
} else {
$parsedcomputername = $server.NetName
}
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 '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 $SqlCredential.UserName) {
$username = ($SqlCredential.UserName).TrimStart("\")
# support both ad\username and username@ad
if ($username -like "*\*" -or $username -like "*@*") {
if ($username -like "*\*") {
$domain, $login = $username.Split("\")
$authtype = "Windows Authentication with Credential"
if ($domain) {
$formatteduser = "$login@$domain"
} else {
$formatteduser = $username.Split("\")[1]
}
} else {
$formatteduser = $SqlCredential.UserName
}
$server.ConnectionContext.LoginSecure = $true
$server.ConnectionContext.ConnectAsUser = $true
$server.ConnectionContext.ConnectAsUserName = $formatteduser
$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)
}
}
if ($NonPooled) {
$server.ConnectionContext.Connect()
} elseif ($authtype -eq "Windows Authentication with Credential") {
# Make it connect in a natural way, hard to explain.
$null = $server.Information.Version
if ($server.ConnectionContext.IsOpen -eq $false) {
$server.ConnectionContext.Connect()
}
} else {
$server.ConnectionContext.SqlConnectionObject.Open()
}
} catch {
$originalException = $_.Exception
try {
$message = $originalException.InnerException.InnerException.ToString()
} catch {
$message = $originalException.ToString()
}
$message = ($message -Split '-->')[0]
$message = ($message -Split 'at System.Data.SqlClient')[0]
$message = ($message -Split 'at System.Data.ProviderBase')[0]
Stop-Function -Message "Can't connect to $instance" -ErrorRecord $_ -Continue
}
}
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) {
if (-not $server.NetName -or $SqlInstance -match '\.') {
$parsedcomputername = $ConvertedSqlInstance.ComputerName
} else {
$parsedcomputername = $server.NetName
}
Add-Member -InputObject $server -NotePropertyName ComputerName -NotePropertyValue $parsedcomputername -Force
}
}
$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 data table 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io/
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/ConvertTo-DbaDataTable
.OUTPUTS
System.Object[]
.EXAMPLE
PS C:\> Get-Service | ConvertTo-DbaDataTable
Creates a DataTable from the output of Get-Service.
.EXAMPLE
PS C:\> ConvertTo-DbaDataTable -InputObject $csv.cheesetypes
Creates a DataTable from the CSV object $csv.cheesetypes.
.EXAMPLE
PS C:\> $dblist | ConvertTo-DbaDataTable
Creates a DataTable from the $dblist object passed in via pipeline.
.EXAMPLE
PS C:\> Get-Process | ConvertTo-DbaDataTable -TimeSpanType TotalSeconds
Creates a DataTable with the running processes and converts any TimeSpan property to TotalSeconds.
#>
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
[OutputType([System.Object[]])]
param (
[Parameter(Position = 0,
Mandatory,
ValueFromPipeline)]
[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 don't 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-DbaTimeline {
<#
.SYNOPSIS
Converts InputObject to a html timeline using Google Chart
.DESCRIPTION
This function accepts input as pipeline from the following dbatools functions:
Get-DbaAgentJobHistory
Get-DbaBackupHistory
(more to come...)
And generates Bootstrap based, HTML file with Google Chart Timeline
.PARAMETER InputObject
Pipe input, must an output from the above functions.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Chart
Author: Marcin Gminski (@marcingminski)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Dependency: ConvertTo-JsDate, Convert-DbaTimelineStatusColor
.LINK
https://dbatools.io/ConvertTo-DbaTimeline
.EXAMPLE
PS C:\> Get-DbaAgentJobHistory -SqlInstance sql-1 -StartDate '2018-08-13 00:00' -EndDate '2018-08-13 23:59' -ExcludeJobSteps | ConvertTo-DbaTimeline | Out-File C:\temp\DbaAgentJobHistory.html -Encoding ASCII
Creates an output file containing a pretty timeline for all of the agent job history results for sql-1 the whole day of 2018-08-13
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlcm | Get-DbaBackupHistory -Since '2018-08-13 00:00' | ConvertTo-DbaTimeline | Out-File C:\temp\DbaBackupHistory.html -Encoding ASCII
Creates an output file containing a pretty timeline for the agent job history since 2018-08-13 for all of the registered servers on sqlcm
.EXAMPLE
PS C:\> $messageParameters = @{
>> Subject = "Backup history for sql2017 and sql2016"
>> Body = Get-DbaBackupHistory -SqlInstance sql2017, sql2016 -Since '2018-08-13 00:00' | ConvertTo-DbaTimeline
>> From = "[email protected]"
>> To = "[email protected]"
>> SmtpServer = "smtp.ad.local"
>> }
>>
PS C:\> Send-MailMessage @messageParameters -BodyAsHtml
Sends an email to [email protected] with the results of Get-DbaBackupHistory. Note that viewing these reports may not be supported in all email clients.
#>
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[object[]]$InputObject,
[switch]$EnableException
)
begin {
$body = $servers = @()
$begin = @"
<html>
<head>
<!-- Developed by Marcin Gminski, https://marcin.gminski.net, 2018 -->
<!-- Load jQuery required to autosize timeline -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<!-- Load Bootstrap -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- Load Google Charts library -->
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<!-- a bit of custom styling to work with bootstrap grid -->
<style>
html,body{height:100%;background-color:#c2c2c2;}
.viewport {height:100%}
.chart{
background-color:#fff;
text-align:left;
padding:0;
border:1px solid #7D7D7D;
-webkit-box-shadow:1px 1px 3px 0 rgba(0,0,0,.45);
-moz-box-shadow:1px 1px 3px 0 rgba(0,0,0,.45);
box-shadow:1px 1px 3px 0 rgba(0,0,0,.45)
}
.badge-custom{background-color:#939}
.container {
height:100%;
}
.fill{
width:100%;
height:100%;
min-height:100%;
padding:10px;
}
.timeline-tooltip{
border:1px solid #E0E0E0;
font-family:Arial,Helvetica;
font-size:10pt;
padding:12px
}
.timeline-tooltip div{padding:6px}
.timeline-tooltip span{font-weight:700}
</style>
<script type="text/javascript">
google.charts.load('43', {'packages':['timeline']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var container = document.getElementById('Chart');
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({type: 'string', id: 'vLabel'});
dataTable.addColumn({type: 'string', id: 'hLabel'});
dataTable.addColumn({type: 'string', role: 'style' });
dataTable.addColumn({type: 'date', id: 'date_start'});
dataTable.addColumn({type: 'date', id: 'date_end'});
dataTable.addRows([
"@
}
process {
# create server list to support multiple servers
if ($InputObject[0].SqlInstance -notin $servers) {
$servers += $InputObject[0].SqlInstance
}
# This is where do column mapping.
# Check for types - this will help support if someone assigns a variable then pipes
# AgentJobHistory is a forced type while backuphistory is a legit type
if ($InputObject[0].TypeName -eq 'AgentJobHistory') {
$CallerName = "Get-DbaAgentJobHistory"
$data = $InputObject | Select-Object @{ Name = "SqlInstance"; Expression = { $_.SqlInstance } }, @{ Name = "InstanceName"; Expression = { $_.InstanceName } }, @{ Name = "vLabel"; Expression = { $_.Job -replace "\'", ''} }, @{ Name = "hLabel"; Expression = { $_.Status } }, @{ Name = "Style"; Expression = { $(Convert-DbaTimelineStatusColor($_.Status)) } }, @{ Name = "StartDate"; Expression = { $(ConvertTo-JsDate($_.StartDate)) } }, @{ Name = "EndDate"; Expression = { $(ConvertTo-JsDate($_.EndDate)) } }
} elseif ($InputObject[0] -is [Sqlcollaborative.Dbatools.Database.BackupHistory]) {
$CallerName = "Get-DbaBackupHistory"
$data = $InputObject | Select-Object @{ Name = "SqlInstance"; Expression = { $_.SqlInstance } }, @{ Name = "InstanceName"; Expression = { $_.InstanceName } }, @{ Name = "vLabel"; Expression = { $_.Database } }, @{ Name = "hLabel"; Expression = { $_.Type } }, @{ Name = "StartDate"; Expression = { $(ConvertTo-JsDate($_.Start)) } }, @{ Name = "EndDate"; Expression = { $(ConvertTo-JsDate($_.End)) } }
} else {
# sorry to be so formal, can't help it ;)
Stop-Function -Message "Unsupported input data. To request support for additional commands, please file an issue at dbatools.io/issues and we'll take a look"
return
}
$body += "$($data | ForEach-Object{ "['$($_.vLabel)','$($_.hLabel)','$($_.Style)',$($_.StartDate), $($_.EndDate)]," })"
}
end {
if (Test-FunctionInterrupt) { return }
$end = @"
]);
var paddingHeight = 20;
var rowHeight = dataTable.getNumberOfRows() * 41;
var chartHeight = rowHeight + paddingHeight;
dataTable.insertColumn(2, {type: 'string', role: 'tooltip', p: {html: true}});
var dateFormat = new google.visualization.DateFormat({
pattern: 'dd/MM/yy HH:mm:ss'
});
for (var i = 0; i < dataTable.getNumberOfRows(); i++) {
var duration = (dataTable.getValue(i, 5).getTime() - dataTable.getValue(i, 4).getTime()) / 1000;
var hours = parseInt( duration / 3600 ) % 24;
var minutes = parseInt( duration / 60 ) % 60;
var seconds = duration % 60;
var tooltip = '<div class="timeline-tooltip"><span>' +
dataTable.getValue(i, 1).split(",").join("<br />") + '</span></div><div class="timeline-tooltip"><span>' +
dataTable.getValue(i, 0) + '</span>: ' +
dateFormat.formatValue(dataTable.getValue(i, 4)) + ' - ' +
dateFormat.formatValue(dataTable.getValue(i, 5)) + '</div>' +
'<div class="timeline-tooltip"><span>Duration: </span>' +
hours + 'h ' + minutes + 'm ' + seconds + 's ';
dataTable.setValue(i, 2, tooltip);
}
var options = {
timeline: {
rowLabelStyle: { },
barLabelStyle: { },
},
hAxis: {
format: 'dd/MM HH:mm',
},
}
// Autosize chart. It would not be enough to just count rows and expand based on row height as there can be overlapping rows.
// this will draw the chart, get the size of the underlying div and apply that size to the parent container and redraw:
chart.draw(dataTable, options);
// get the size of the chold div:
var realheight= parseInt(`$("#Chart div:first-child div:first-child div:first-child div svg").attr( "height"))+70;
// set the height:
options.height=realheight
// draw again:
chart.draw(dataTable, options);
}
</script>
</head>
<body>
<div class="container-fluid">
<div class="pull-left"><h3><code>$($CallerName)</code> timeline for server <code>$($servers -join ', ')</code></h3></div><div class="pull-right text-right"><img class="text-right" style="vertical-align:bottom; margin-top: 10px;" src="https://dbatools.io/wp-content/uploads/2016/05/dbatools-logo-1.png" width=150></div>
<div class="clearfix"></div>
<div class="col-12">
<div class="chart" id="Chart"></div>
</div>
<hr>
<p><a href="https://dbatools.io">dbatools.io</a> - the community's sql powershell module. Find us on Twitter: <a href="https://twitter.com/psdbatools">@psdbatools</a> | Chart by <a href="https://twitter.com/marcingminski">@marcingminski</a></p>
</div>
</body>
</html>
"@
$begin, $body, $end
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaAgentAlert
.EXAMPLE
PS C:\> 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
PS C:\> 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 an alert with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.
.EXAMPLE
PS C:\> Copy-DbaAgentAlert -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[cmdletbinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[object[]]$Alert,
[object[]]$ExcludeAlert,
[switch]$IncludeDefaults,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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 on destination"
$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)
}
# can't 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
}
}
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Copy-DbaAgentJob
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Copy-DbaAgentJob -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[cmdletbinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[object[]]$Job,
[object[]]$ExcludeJob,
[switch]$DisableOnSource,
[switch]$DisableOnDestination,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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 | where-object {$_.SubSystem -ne 'ActiveScripting'}).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 | Where-Object ProxyName).ProxyName
$missingProxy = $proxyNames | Where-Object { $destServer.JobServer.ProxyAccounts.Name -notcontains $_ }
if ($missingProxy -and $proxyNames) {
if ($Pscmdlet.ShouldProcess($destinstance, "Proxy Account(s) $missingProxy 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 $missingProxy"
$copyJobStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
Write-Message -Level Verbose -Message "Proxy Account(s) $missingProxy 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 = "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
}
}
#ValidationTags#Messaging#
function Copy-DbaAgentJobCategory {
<#
.SYNOPSIS
Copy-DbaAgentJobCategory 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaAgentJobCategory
.EXAMPLE
PS C:\> Copy-DbaAgentJobCategory -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
PS C:\> Copy-DbaAgentJobCategory -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 an operator category with the same name exists on sqlcluster, it will be dropped and recreated because -Force was used.
.EXAMPLE
PS C:\> Copy-DbaAgentJobCategory -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[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 {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Copy-DbaAgentCategory
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 on destination"
$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)]
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 on destination"
$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)]
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 on destination"
$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 {
$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 {
$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-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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaAgentOperator
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Copy-DbaAgentOperator -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]
$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]
$DestinationSqlCredential,
[object[]]$Operator,
[object[]]$ExcludeOperator,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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 on destination"
$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-DbaAgentProxy {
<#
.SYNOPSIS
Copy-DbaAgentProxy 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 ProxyAccount
Only migrate specific proxy accounts
.PARAMETER ExcludeProxyAccount
Migrate all proxy accounts except the ones explicitly excluded
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaAgentProxy
.EXAMPLE
PS C:\> Copy-DbaAgentProxy -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
PS C:\> Copy-DbaAgentProxy -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
PS C:\> Copy-DbaAgentProxy -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[string[]]$ProxyAccount,
[string[]]$ExcludeProxyAccount,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Copy-DbaAgentProxyAccount
try {
$sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 9
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
return
}
$serverProxyAccounts = $sourceServer.JobServer.ProxyAccounts
if ($ProxyAccount) {
$serverProxyAccounts | Where-Object Name -in $ProxyAccount
}
if ($ExcludeProxyAccount) {
$serverProxyAccounts | Where-Object Name -notin $ProxyAccount
}
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($destinstance in $Destination) {
try {
$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 ($account in $serverProxyAccounts) {
$proxyName = $account.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)
}
$credentialName = $account.CredentialName
$copyAgentProxyAccountStatus.Name = $proxyName
$copyAgentProxyAccountStatus.Type = "Credential"
# Proxy accounts rely on Credential accounts
if (-not $CredentialName) {
$copyAgentProxyAccountStatus.Status = "Skipped"
$copyAgentProxyAccountStatus.Notes = "Skipping migration of $proxyName due to misconfigured (empty) credential name"
$copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
Write-Message -Level Verbose -Message "Skipping migration of $proxyName due to misconfigured (empty) credential name"
continue
}
try {
$credentialtest = $destServer.Credentials[$CredentialName]
} catch {
#here to avoid an empty catch
$null = 1
}
if ($null -eq $credentialtest) {
$copyAgentProxyAccountStatus.Status = "Skipped"
$copyAgentProxyAccountStatus.Notes = "Associated credential account, $CredentialName, does not exist on $destinstance"
$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"
continue
}
if ($destProxyAccounts.Name -contains $proxyName) {
$copyAgentProxyAccountStatus.Name = $proxyName
$copyAgentProxyAccountStatus.Type = "ProxyAccount"
if ($force -eq $false) {
$copyAgentProxyAccountStatus.Status = "Skipped"
$copyAgentProxyAccountStatus.Notes = "Already exists on destination"
$copyAgentProxyAccountStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
Stop-Function -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 = $account.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-DbaAgentSchedule {
<#
.SYNOPSIS
Copy-DbaAgentSchedule 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaAgentSchedule
.EXAMPLE
PS C:\> Copy-DbaAgentSchedule -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
PS C:\> Copy-DbaAgentSchedule -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]
$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]
$DestinationSqlCredential,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Copy-DbaAgentSharedSchedule
try {
$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 {
$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 on destination"
$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-DbaAgentServer {
<#
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaAgentServer
.EXAMPLE
PS C:\> Copy-DbaAgentServer -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
PS C:\> Copy-DbaAgentServer -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
PS C:\> Copy-DbaAgentServer -Source sqlserver2014a -Destination sqlcluster -WhatIf
Shows what would happen if the command were executed.
#>
[cmdletbinding(SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[Switch]$DisableJobsOnDestination,
[Switch]$DisableJobsOnSource,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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-DbaAgentJobCategory -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-DbaAgentProxy -Source $sourceServer -Destination $destServer -Force:$force
$destServer.JobServer.ProxyAccounts.Refresh()
Copy-DbaAgentSchedule -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
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-DbaSqlServerAgent
}
}
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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaBackupDevice
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Copy-DbaBackupDevice -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[object[]]$BackupDevice,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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 on destination"
$copyBackupDeviceStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
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-DbaPath -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-DbaCmsRegServer {
<#
.SYNOPSIS
Migrates SQL Server Central Management groups and server instances from one SQL Server to another.
.DESCRIPTION
Copy-DbaCmsRegServer 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 Group
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 Group 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaCmsRegServer
.EXAMPLE
PS C:\> Copy-DbaCmsRegServer -Source sqlserver2014a -Destination sqlcluster
All groups, subgroups, and server instances are copied from sqlserver2014a CMS to sqlcluster CMS.
.EXAMPLE
PS C:\> Copy-DbaCmsRegServer -Source sqlserver2014a -Destination sqlcluster -Group Group1,Group3
Top-level groups Group1 and Group3 along with their subgroups and server instances are copied from sqlserver to sqlcluster.
.EXAMPLE
PS C:\> Copy-DbaCmsRegServer -Source sqlserver2014a -Destination sqlcluster -Group 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)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[Alias('CMSGroup')]
[string[]]$Group,
[switch]$SwitchServerName,
[switch]$Force,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Copy-DbaCentralManagementServer
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 on destination"
$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 on destination"
$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 on destination"
$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 {
$sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
$fromCmStore = Get-DbaCmsRegServerStore -SqlInstance $sourceServer
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
return
}
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($destinstance in $Destination) {
try {
$destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
}
$toCmStore = Get-DbaCmsRegServerStore -SqlInstance $destServer
$stores = $fromCmStore.DatabaseEngineServerGroup
if ($Group) {
$stores = @();
foreach ($groupName in $Group) {
$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/
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires:
- PowerShell Version 3.0
- Administrator access on Windows
- sysadmin access on SQL Server.
- DAC access enabled for local (default)
.LINK
https://dbatools.io/Copy-DbaCredential
.EXAMPLE
PS C:\> 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
PS C:\> 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
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "", Justification = "For Credentials")]
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 on destination"
$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 {
$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 {
$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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaCustomError
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> Copy-DbaCustomError -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]
$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]
$DestinationSqlCredential,
[object[]]$CustomError,
[object[]]$ExcludeCustomError,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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 on destination"
$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 -SharedPath in a valid UNC format (\\server\share).
Backups will be immediately deleted after use unless -NoBackupCleanup is specified.
.PARAMETER SharedPath
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 UseLastBackup
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 Continue
If specified, will to attempt to restore transaction log backups on top of existing database(s) in Recovering or Standby states. Only usable with -UseLastBackup
.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 NewName
If a single database is being copied, this will be used to rename the database during the copy process. Any occurrence of the original database name in the physical file names will be replaced with NewName
If specified with multiple databases a warning will be raised and the copy stopped
This option is mutually exclusive of Prefix
.PARAMETER Prefix
All copied database names and physical files will be prefixed with this string
This option is mutually exclusive of NewName
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
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.
.LINK
https://dbatools.io/Copy-DbaDatabase
.EXAMPLE
PS C:\> Copy-DbaDatabase -Source sql2014a -Destination sql2014b -Database TestDB -BackupRestore -SharedPath \\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
PS C:\> 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
PS C:\> Copy-DbaDatabase -Source sql2014a -Destination sqlcluster, sql2016 -BackupRestore -UseLastBackup -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
PS C:\> Copy-DbaDatabase -Source sql2014a -Destination sqlcluster -ExcludeDatabase Northwind, pubs -IncludeSupportDbs -Force -BackupRestore -SharedPath \\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)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[Alias("All")]
[parameter(ParameterSetName = "DbBackup")]
[parameter(ParameterSetName = "DbAttachDetach")]
[switch]$AllDatabases,
[parameter(Mandatory, ParameterSetName = "DbBackup")]
[switch]$BackupRestore,
[Alias("NetworkShare")]
[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]$SharedPath,
[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, 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]$UseLastBackup,
[parameter(ParameterSetName = "DbBackup")]
[switch]$Continue,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$NoCopyOnly,
[switch]$SetSourceOffline,
[string]$NewName,
[string]$Prefix,
[switch]$Force,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter NetworkShare -CustomMessage "Using the parameter NetworkShare is deprecated. This parameter will be removed in version 1.0.0 or before. Use SharedPath instead."
$CopyOnly = -not $NoCopyOnly
if ($BackupRestore -and (-not $SharedPath -and -not $UseLastBackup)) {
Stop-Function -Message "When using -BackupRestore, you must specify -SharedPath or -UseLastBackup"
return
}
if ($SharedPath -and $UseLastBackup) {
Stop-Function -Message "-SharedPath cannot be used with -UseLastBackup 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 ($Continue -and -not $UseLastBackup) {
Stop-Function -Message "-Continue cannot be used without -UseLastBackup"
return
}
function Join-Path {
<#
An internal command that does not require the local path to exist
Boo, this does not work, but keeping it for future ref.
#>
[CmdletBinding()]
param (
[string]$Path,
[string]$ChildPath
)
process {
try {
[IO.Path]::Combine($Path, $ChildPath)
} catch {
"$Path\$ChildPath"
}
}
}
function Join-AdminUnc {
<#
.SYNOPSIS
Internal function. Parses a path to make it an admin UNC.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$servername,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$filepath
)
if ($script:sameserver) {
return $filepath
}
if (-not $filepath) {
return
}
if ($filepath.StartsWith("\\")) {
return $filepath
}
$servername = $servername.Split("\")[0]
if ($filepath -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
# here to avoid an empty catch
$null = 1
}
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) {
$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
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[object]$fileStructure,
[string]$dbName
)
$filestructure
$copydb = $fileStructure.databases[$dbName]
$dbsource = $copydb.source
$dbdestination = $copydb.destination
foreach ($file in $dbsource.keys) {
if ($Pscmdlet.ShouldProcess($file, "Starting Sql File Transfer")) {
$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
$remotefilename
} 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(SupportsShouldProcess)]
param (
[object]$sourceServer,
[object]$destServer,
[object]$fileStructure,
[string]$dbName
)
if ($Pscmdlet.ShouldProcess($dbname, "Starting detaching and re-attaching from $sourceServer to $destServer")) {
$destfilestructure = New-Object System.Collections.Specialized.StringCollection
$sourceFileStructure = New-Object System.Collections.Specialized.StringCollection
$dbOwner = $sourceServer.databases[$dbName].owner
$destDbName = $fileStructure.databases[$dbName].destinationDbName
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-Message -Level Verbose -Message "Could not copy files."
return "Could not copy files."
}
$attachresult = Mount-SqlDatabase $destServer $destDbName $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 $SharedPath -and -not $UseLastBackup)) {
Stop-Function -Message "When using -BackupRestore, you must specify -SharedPath or -UseLastBackup"
return
}
if ($SharedPath -and $UseLastBackup) {
Stop-Function -Message "-SharedPath cannot be used with -UseLastBackup 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 ((Test-Bound 'NewName') -and (Test-Bound 'Prefix')) {
Stop-Function -Message "NewName and Prefix are exclusive options, cannot specify both"
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 {
$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)."
if ($sourceserver.Databases.IsSystemObject -notcontains $false) {
Stop-Function -Message "No user databases to migrate"
return
}
foreach ($destinstance in $Destination) {
try {
$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 ($SharedPath) {
if ($(Test-DbaPath -SqlInstance $sourceServer -Path $SharedPath) -eq $false) {
Write-Message -Level Verbose -Message "$Source may not be able to access $SharedPath. Trying anyway."
}
if ($(Test-DbaPath -SqlInstance $destServer -Path $SharedPath) -eq $false) {
Write-Message -Level Verbose -Message "$destinstance may not be able to access $SharedPath. Trying anyway."
}
if ($SharedPath.StartsWith('\\')) {
try {
$shareServer = ($SharedPath -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 ($SharedPath) {
Write-Message -Level Verbose -Message "Checking to ensure network path is valid."
if (-not ($SharedPath.StartsWith("\\")) -and -not $script:sameserver) {
Stop-Function -Message "Network share must be a valid UNC path (\\server\share)." -Continue
}
if (-not $script:sameserver) {
try {
if ((Test-Path $SharedPath -ErrorAction Stop)) {
Write-Message -Level Verbose -Message "$SharedPath share can be accessed."
}
} catch {
Write-Message -Level Verbose -Message "$SharedPath 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
if ((Test-Bound 'NewName') -and $dbCount -gt 1) {
Stop-Function -Message "Cannot use NewName when copying multiple databases"
return
}
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 -or $IncludeSupportDbs -or $Database) {
foreach ($currentdb in $databaseList) {
$dbName = $currentdb.Name
$dbOwner = $currentdb.Owner
$destinationDbName = $dbName
if ((Test-Bound "NewName")) {
Write-Message -Level Verbose -Message "NewName specified, copying $dbname as $NewName"
$destinationDbName = $NewName
$replaceInFile = $True
}
if ($(Test-Bound "Prefix")) {
$destinationDbName = $prefix + $destinationDbName
Write-Message -Level Verbose -Message "Prefix supplied, copying $dbname as $destinationDbName"
}
$filestructure.databases[$dbname].Add('destinationDbName', $destinationDbName)
ForEach ($key in $filestructure.databases[$dbname].Destination.Keys) {
$splitFileName = Split-Path $fileStructure.databases[$dbname].Destination[$key].remotefilename -Leaf
$SplitPath = Split-Path $fileStructure.databases[$dbname].Destination[$key].remotefilename
if ($replaceInFile) {
$splitFileName = $splitFileName.replace($dbname, $destinationDbName)
}
$splitFileName = $prefix + $splitFileName
$filestructure.databases[$dbname].Destination.$key.remotefilename = Join-Path $SplitPath $splitFileName
$splitFileName = Split-Path $filestructure.databases[$dbname].Destination[$key].physical -Leaf
$SplitPath = Split-Path $fileStructure.databases[$dbname].Destination[$key].physical
if ($replaceInFile) {
$splitFileName = $splitFileName.replace($dbname, $destinationDbName)
}
$splitFileName = $prefix + $splitFileName
$filestructure.databases[$dbname].Destination.$key.physical = Join-Path $SplitPath $splitFileName
}
$copyDatabaseStatus = [pscustomobject]@{
SourceServer = $sourceServer.Name
DestinationServer = $destServer.Name
Name = $dbName
DestinationDatabase = $DestinationDbname
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-DbaPath -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 -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[$DestinationdbName]) -and !$force -and !$WithReplace) {
if ($Pscmdlet.ShouldProcess($destinstance, "$DestinationdbName exists at destination. Use -Force to drop and migrate. Aborting routine for this database.")) {
Write-Message -Level Verbose -Message "$DestinationdbName exists at destination. Use -Force to drop and migrate. Aborting routine for this database."
$copyDatabaseStatus.Status = "Skipped"
$copyDatabaseStatus.Notes = "Already exists on destination"
$copyDatabaseStatus | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
}
continue
} elseif ($null -ne $destServer.Databases[$DestinationdbName] -and $force) {
if ($Pscmdlet.ShouldProcess($destinstance, "DROP DATABASE $DestinationdbName")) {
Write-Message -Level Verbose -Message "$DestinationdbName already exists. -Force was specified. Dropping $DestinationdbName on $destinstance."
$removeresult = Remove-DbaDatabase -SqlInstance $destserver -Database $DestinationdbName -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."
try {
$result = Set-DbaDbState -SqlInstance $sourceServer -Database $dbName -ReadOnly -EnableException
} catch {
Stop-Function -Continue -Message "Couldn't set database to read-only. Aborting routine for this database" -ErrorRecord $_
}
}
}
if ($BackupRestore) {
if ($UseLastBackup) {
$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 ($UseLastBackup) {
$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 $SharedPath -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 $($SharedPath)? Aborting routine for this database."
$copyDatabaseStatus.Status = "Failed"
$copyDatabaseStatus.Notes = "Backup failed. Verify service account access to $SharedPath."
$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 $DestinationdbName -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -NoRecovery:$NoRecovery -TrustDbBackupHistory -WithReplace:$WithReplace -Continue:$Continue -EnableException -ReplaceDbNameInFile
} 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-DbaDbOwner -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 -or $sourceDbReadOnly) {
try {
$result = Set-DbaDbState -SqlInstance $sourceServer -Database $dbName -ReadOnly -EnableException
} catch {
Stop-Function -Message "Couldn't set database to read-only" -ErrorRecord $_
}
}
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
}
}
}
$NewDatabase = Get-DbaDatabase -SqlInstance $destServer -database $DestinationdbName
# restore potentially lost settings
if ($destServer.VersionMajor -ge 9 -and $NoRecovery -eq $false) {
if ($sourceDbOwnerChaining -ne $NewDatabase.DatabaseOwnershipChaining) {
if ($Pscmdlet.ShouldProcess($destinstance, "Updating DatabaseOwnershipChaining on $DestinationdbName")) {
try {
$NewDatabase.DatabaseOwnershipChaining = $sourceDbOwnerChaining
$NewDatabase.Alter()
Write-Message -Level Verbose -Message "Successfully updated DatabaseOwnershipChaining for $sourceDbOwnerChaining on $DestinationdbName 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 $DestinationdbName on $destinstance." -Target $destinstance -ErrorRecord $_ -Continue
}
}
}
if ($sourceDbTrustworthy -ne $NewDatabase.Trustworthy) {
if ($Pscmdlet.ShouldProcess($destinstance, "Updating Trustworthy on $DestinationdbName")) {
try {
$NewDatabase.Trustworthy = $sourceDbTrustworthy
$NewDatabase.Alter()
Write-Message -Level Verbose -Message "Successfully updated Trustworthy to $sourceDbTrustworthy for $DestinationdbName 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 $DestinationdbName on $destinstance." -Target $destinstance -ErrorRecord $_ -Continue
}
}
}
if ($sourceDbBrokerEnabled -ne $NewDatabase.BrokerEnabled) {
if ($Pscmdlet.ShouldProcess($destinstance, "Updating BrokerEnabled on $dbName")) {
try {
$NewDatabase.BrokerEnabled = $sourceDbBrokerEnabled
$NewDatabase.Alter()
Write-Message -Level Verbose -Message "Successfully updated BrokerEnabled to $sourceDbBrokerEnabled for $DestinationdbName 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 $DestinationdbName on $destinstance." -Target $destinstance -ErrorRecord $_ -Continue
}
}
}
}
if ($sourceDbReadOnly -ne $NewDatabase.ReadOnly -and -not $NoRecovery) {
if ($Pscmdlet.ShouldProcess($destinstance, "Updating ReadOnly status on $DestinationdbName")) {
try {
if ($sourceDbReadOnly) {
$result = Set-DbaDbState -SqlInstance $destserver -Database $DestinationdbName -ReadOnly -EnableException
} else {
$result = Set-DbaDbState -SqlInstance $destserver -Database $DestinationdbName -ReadWrite -EnableException
}
} catch {
$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 $DestinationdbName." -Target $destinstance -ErrorRecord $_ -Continue
}
}
}
if ($SetSourceOffline -and $sourceServer.databases[$DestinationdbName].status -notlike '*offline*') {
if ($Pscmdlet.ShouldProcess($destinstance, "Setting $DestinationdbName offline on $source")) {
Stop-DbaProcess -SqlInstance $sourceServer -Database $DestinationdbName
Set-DbaDbState -SqlInstance $sourceServer -SqlCredential $SourceSqlCredential -database $DestinationdbName -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 ($SharedPath -and $NoBackupCleanup) {
Write-Message -Level Verbose -Message "Backups still exist at $SharedPath."
}
} 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-DbaDataCollector {
<#
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaDataCollector
.EXAMPLE
PS C:\> Copy-DbaDataCollector -Source sqlserver2014a -Destination sqlcluster
Copies all Data Collector Objects and Configurations from sqlserver2014a to sqlcluster, using Windows credentials.
.EXAMPLE
PS C:\> Copy-DbaDataCollector -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
PS C:\> Copy-DbaDataCollector -Source sqlserver2014a -Destination sqlcluster -WhatIf
Shows what would happen if the command were executed.
.EXAMPLE
PS C:\> Copy-DbaDataCollector -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)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]
$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]
$DestinationSqlCredential,
[object[]]$CollectionSet,
[object[]]$ExcludeCollectionSet,
[switch]$NoServerReconfig,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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 on destination"
$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 {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlDataCollector
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-DbaSqlDataCollector
if (Test-FunctionInterrupt) { return }
}
}
function Copy-DbaDbAssembly {
<#
.SYNOPSIS
Copy-DbaDbAssembly 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
http://dbatools.io/Get-SqlDatabaseAssembly
.EXAMPLE
PS C:\> Copy-DbaDbAssembly -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
PS C:\> Copy-DbaDbAssembly -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
PS C:\> Copy-DbaDbAssembly -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[object[]]$Assembly,
[object[]]$ExcludeAssembly,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
#here to avoid an empty catch
$null = 1
}
}
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($destinstance in $Destination) {
try {
$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 {
#here to avoid an empty catch
$null = 1
}
}
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 on destination"
$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
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-DbaDatabaseAssembly
}
}
function Copy-DbaDbMail {
<#
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaDbMail
.EXAMPLE
PS C:\> Copy-DbaDbMail -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
PS C:\> Copy-DbaDbMail -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
PS C:\> Copy-DbaDbMail -Source sqlserver2014a -Destination sqlcluster -WhatIf
Shows what would happen if the command were executed.
.EXAMPLE
PS C:\> Copy-DbaDbMail -Source sqlserver2014a -Destination sqlcluster -EnableException
Performs execution of function, and will throw a terminating exception if something breaks
#>
[cmdletbinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[parameter(Mandatory)]
[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-DbaDbMailConfig {
[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.Notes = "Already exists on destination"
$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-DbaDbMailProfile {
$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 on destination"
$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-DbaDbMailServer {
[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 on destination"
$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 {
$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 {
$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-DbaDbMailConfig
$destServer.Mail.ConfigurationValues.Refresh()
}
"Profiles" {
Copy-DbaDbMailProfile
$destServer.Mail.Profiles.Refresh()
}
"Accounts" {
Copy-DbaDatabaseAccount
$destServer.Mail.Accounts.Refresh()
}
"mailServers" {
Copy-DbaDbMailServer
}
}
continue
}
if (($profiles.count + $accounts.count + $mailServers.count) -gt 0) {
if ($profiles.count -gt 0) {
Copy-DbaDbMailProfile -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-DbaDbMailServer -mailServers $mailServers
}
continue
}
Copy-DbaDbMailConfig
$destServer.Mail.ConfigurationValues.Refresh()
Copy-DbaDatabaseAccount
$destServer.Mail.Accounts.Refresh()
Copy-DbaDbMailProfile
$destServer.Mail.Profiles.Refresh()
Copy-DbaDbMailServer
$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
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-DbaDatabaseMail
}
}
function Copy-DbaDbQueryStoreOption {
<#
.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
Tags: QueryStore
Author: Enrico van de Laar (@evdlaar)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Copy-QueryStoreConfig
.EXAMPLE
PS C:\> Copy-DbaDbQueryStoreOption -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
PS C:\> Copy-DbaDbQueryStoreOption -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)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory, ValueFromPipeline)]
[object]$SourceDatabase,
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[object[]]$DestinationDatabase,
[object[]]$Exclude,
[switch]$AllDatabases,
[Alias('Silent')]
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Copy-DbaQueryStoreConfig
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-DbaDbQueryStoreOption -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 {
$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 -ExcludeSystem
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
if ($PSCmdlet.ShouldProcess("$db", "Copying QueryStoreConfig")) {
try {
$setDbaDbQueryStoreOptionParameters = @{
SqlInstance = $destinationServer;
SqlCredential = $DestinationSqlCredential;
Database = $db.name;
State = $SourceQSConfig.ActualState;
FlushInterval = $SourceQSConfig.DataFlushIntervalInSeconds;
CollectionInterval = $SourceQSConfig.StatisticsCollectionIntervalInMinutes;
MaxSize = $SourceQSConfig.MaxStorageSizeInMB;
CaptureMode = $SourceQSConfig.QueryCaptureMode;
CleanupMode = $SourceQSConfig.SizeBasedCleanupMode;
StaleQueryThreshold = $SourceQSConfig.StaleQueryThresholdInDays;
}
$null = Set-DbaDbQueryStoreOption @setDbaDbQueryStoreOptionParameters;
$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
}
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Copy-DbaDbTableData {
<#
.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-DbaQuery -SqlInstance 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 a 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 of a table or selected tables, specify the query.
Ensure to select all required columns. Calculated Columns or columns with default values may be excluded.
The tablename should be a full three-part name in form [Database].[Schema].[Table]
.PARAMETER AutoCreateTable
Creates the destination table if it does not already exist, based off of the "Export..." script of the source table.
.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-DbaDbTable
.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: Simone Bizzotto (@niphlod)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Copy-DbaDbTableData
.EXAMPLE
PS C:\> Copy-DbaDbTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table dbo.test_table
Copies all the data from table dbo.test_table in database dbatools_from on sql1 to table test_table in database dbatools_from on sql2.
.EXAMPLE
PS C:\> Copy-DbaDbTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -DestinationDatabase dbatools_dest -Table [Schema].[test table]
Copies all the data from table [Schema].[test table] in database dbatools_from on sql1 to table [Schema].[test table] in database dbatools_dest on sql2
.EXAMPLE
PS C:\> Get-DbaDbTable -SqlInstance sql1 -Database tempdb -Table tb1, tb2 | Copy-DbaDbTableData -DestinationTable tb3
Copies all data from tables tb1 and tb2 in tempdb on sql1 to tb3 in tempdb on sql1
.EXAMPLE
PS C:\> Get-DbaDbTable -SqlInstance sql1 -Database tempdb -Table tb1, tb2 | Copy-DbaDbTableData -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
PS C:\> Copy-DbaDbTableData -SqlInstance sql1 -Destination sql2 -Database dbatools_from -Table test_table -KeepIdentity -Truncate
Copies all the data in table test_table from sql1 to sql2, using the database dbatools_from, keeping identity columns and truncating the destination
.EXAMPLE
PS C:\> $params = @{
>> SourceSqlInstance = 'sql1'
>> DestinationSqlInstance = 'sql2'
>> Database = 'dbatools_from'
>> DestinationDatabase = 'dbatools_dest'
>> Table = '[Schema].[Table]'
>> DestinationTable = '[dbo].[Table.Copy]'
>> KeepIdentity = $true
>> KeepNulls = $true
>> Truncate = $true
>> BatchSize = 10000
>> }
>>
PS C:\> Copy-DbaDbTableData @params
Copies all the data from table [Schema].[Table] in database dbatools_from on sql1 to table [dbo].[Table.Copy] in database dbatools_dest on sql2
Keeps identity columns and Nulls, truncates the destination and processes in BatchSize of 10000.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[Alias("ServerInstance", "SqlServer", "Source")]
[DbaInstanceParameter]$SqlInstance,
[PSCredential]$SqlCredential,
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[string]$Database,
[string]$DestinationDatabase,
[string[]]$Table,
[string]$Query,
[switch]$AutoCreateTable,
[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 Stop
if (-not $script:core) {
try {
Add-Type -ReferencedAssemblies System.Data.dll -TypeDefinition $sourcecode -ErrorAction Stop
} catch {
$null = 1
}
}
$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
}
if ((Test-Bound -Not -ParameterName Destination, DestinationDatabase, DestinationTable)) {
Stop-Function -Message "Cannot copy $Table into itself. One of destination Server, Database or Table must be specified " -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 {
foreach ($tbl in $Table) {
$dbTable = Get-DbaDbTable -SqlInstance $server -Table $tbl -Database $Database -EnableException -Verbose:$false
if ($dbTable.Count -eq 1) {
$InputObject += $dbTable
} else {
Stop-Function -Message "The table $tbl matches $($dbTable.Count) objects. Unable to determine which object to copy" -Continue
}
}
} 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 DestinationTable)) {
$DestinationTable = '[' + $sqltable.Schema + '].[' + $sqltable.Name + ']'
}
$newTableParts = Get-TableNameParts $DestinationTable
#using FQTN to determine database name
if ($newTableParts.Database) {
$DestinationDatabase = $newTableParts.Database
} elseif ((Test-Bound -Not -ParameterName DestinationDatabase)) {
$DestinationDatabase = $Database
}
if (-not $Destination) {
$Destination = $server
}
foreach ($destinationserver in $Destination) {
try {
$destServer = Connect-SqlInstance -SqlInstance $destinationserver -SqlCredential $DestinationSqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinationserver
return
}
if ($DestinationDatabase -notin $destServer.Databases.Name) {
Stop-Function -Message "Database $DestinationDatabase doesn't exist on $destServer"
return
}
$desttable = Get-DbaDbTable -SqlInstance $destServer -Table $DestinationTable -Database $DestinationDatabase -Verbose:$false | Select-Object -First 1
if (-not $desttable -and $AutoCreateTable) {
try {
$tablescript = $sqltable | Export-DbaScript -Passthru | Out-String
#replacing table name
if ($newTableParts.Table) {
$rX = "(CREATE TABLE \[$([regex]::Escape($sqltable.Schema))\]\.\[)$([regex]::Escape($sqltable.Name))(\]\()"
$tablescript = $tablescript -replace $rX, "`$1$($newTableParts.Table)`$2"
}
#replacing table schema
if ($newTableParts.Schema) {
$rX = "(CREATE TABLE \[)$([regex]::Escape($sqltable.Schema))(\]\.\[$([regex]::Escape($newTableParts.Schema))\]\()"
$tablescript = $tablescript -replace $rX, "`$1$($newTableParts.Schema)`$2"
}
if ($PSCmdlet.ShouldProcess($destServer, "Creating new table: $DestinationTable")) {
Write-Message -Message "New table script: $tablescript" -Level VeryVerbose
Invoke-DbaQuery -SqlInstance $destServer -Database $DestinationDatabase -Query "$tablescript" -EnableException # add some string assurance there
#table list was updated, let's grab a fresh one
$destServer.Databases[$DestinationDatabase].Tables.Refresh()
$desttable = Get-DbaDbTable -SqlInstance $destServer -Table $DestinationTable -Database $DestinationDatabase -Verbose:$false
Write-Message -Message "New table created: $desttable" -Level Verbose
}
} catch {
Stop-Function -Message "Unable to determine destination table: $DestinationTable" -ErrorRecord $_
return
}
}
if (-not $desttable) {
Stop-Function -Message "Table $DestinationTable cannot be found in $DestinationDatabase. Use -AutoCreateTable to automatically create the table on the destination." -Continue
}
$connstring = $destServer.ConnectionContext.ConnectionString
if ($server.DatabaseEngineType -eq "SqlAzureDatabase") {
$fqtnfrom = "$sqltable"
} else {
$fqtnfrom = "$($server.Databases[$Database]).$sqltable"
}
if ($destServer.DatabaseEngineType -eq "SqlAzureDatabase") {
$fqtndest = "$desttable"
} else {
$fqtndest = "$($destServer.Databases[$DestinationDatabase]).$desttable"
}
if ($fqtndest -eq $fqtnfrom -and $server.Name -eq $destServer.Name) {
Stop-Function -Message "Cannot copy $fqtnfrom on $($server.Name) into $fqtndest on ($destServer.Name). Source and Destination must be different " -Target $Table
return
}
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")
}
}
if ($Pscmdlet.ShouldProcess($server, "Copy data from $fqtnfrom")) {
$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)
if ($script:core) {
$RowsTotal = "Unsupported in Core"
} else {
$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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-DbaTableData
}
}
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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaEndpoint
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Copy-DbaEndpoint -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]
$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]
$DestinationSqlCredential,
[object[]]$Endpoint,
[object[]]$ExcludeEndpoint,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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 on destination"
$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-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/
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
Limitations: This just copies the SQL portion. It does not copy files (i.e. a local SQLite database, or Microsoft Access DB), nor does it configure ODBC entries.
.LINK
https://dbatools.io/Copy-DbaLinkedServer
.EXAMPLE
PS C:\> Copy-DbaLinkedServer -Source sqlserver2014a -Destination sqlcluster
Copies all SQL Server Linked Servers on sqlserver2014a to sqlcluster. If Linked Server exists on destination, it will be skipped.
.EXAMPLE
PS C:\> Copy-DbaLinkedServer -Source sqlserver2014a -Destination sqlcluster -LinkedServer SQL2K5,SQL2k -Force
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)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Internal functions are ignored")]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[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 {
#here to avoid an empty catch
$null = 1
}
$linkedServerName = $currentLinkedServer.Name
$linkedServerDataSource = $currentLinkedServer.DataSource
$copyLinkedServer = [pscustomobject]@{
SourceServer = $sourceServer.Name
DestinationServer = $destServer.Name
Name = $linkedServerName
DataSource = $linkedServerDataSource
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.Notes = "Already exists on destination"
$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)
if ($copyLinkedServer.Name -ne $copyLinkedServer.DataSource) {
$sql2 = "EXEC sp_setnetname '$($copyLinkedServer.Name)', '$($copyLinkedServer.DataSource)'; "
$destServer.Query($sql2)
}
$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 {
$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 {
$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 ExcludeSystemLogins
If this switch is enabled, NT SERVICE accounts will be skipped.
.PARAMETER ExcludePermissionSync
Skips permission syncs
.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-DbaLogin 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaLogin
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Copy-DbaLogin -Source sqlserver2014a -Destination sqlcluster -ExcludeLogin 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
PS C:\> 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
PS C:\> 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
PS C:\> Copy-DbaLogin -LoginRenameHashtable @{ "PreviousUser" = "newlogin" } -Source $Sql01 -Destination Localhost -SourceSqlCredential $sqlcred
Copies PreviousUser and then renames it to newlogin.
.EXAMPLE
PS C:\> 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)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[object[]]$Login,
[object[]]$ExcludeLogin,
[switch]$ExcludeSystemLogins,
[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,
[switch]$ExcludePermissionSync,
[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 ($ExcludeSystemLogins) {
if ($Pscmdlet.ShouldProcess("console", "$userName was skipped because ExcludeSystemLogins was specified.")) {
Write-Message -Level Verbose -Message "$userName was skipped because ExcludeSystemLogins 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 on destination"
$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 (-not $ExcludePermissionSync) {
if ($Pscmdlet.ShouldProcess($destinstance, "Updating SQL login $userName permissions")) {
Update-SqlPermission -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 {
$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 {
$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-DbaPolicyManagement {
<#
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaPolicyManagement
.EXAMPLE
PS C:\> Copy-DbaPolicyManagement -Source sqlserver2014a -Destination sqlcluster
Copies all policies and conditions from sqlserver2014a to sqlcluster, using Windows credentials.
.EXAMPLE
PS C:\> Copy-DbaPolicyManagement -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
PS C:\> Copy-DbaPolicyManagement -Source sqlserver2014a -Destination sqlcluster -WhatIf
Shows what would happen if the command were executed.
.EXAMPLE
PS C:\> Copy-DbaPolicyManagement -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)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]
$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]
$DestinationSqlCredential,
[object[]]$Policy,
[object[]]$ExcludePolicy,
[object[]]$Condition,
[object[]]$ExcludeCondition,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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 on destination"
$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 on destination"
$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
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-DbaSqlPolicyManagement
}
}
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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaResourceGovernor
.EXAMPLE
PS C:\> Copy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster
Copies all all non-system resource pools from sqlserver2014a to sqlcluster using Windows credentials to connect to the SQL Server instances..
.EXAMPLE
PS C:\> Copy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $cred
Copies all all non-system resource pools from sqlserver2014a to sqlcluster using SQL credentials to connect to sqlserver2014a and Windows credentials to connect to sqlcluster.
.EXAMPLE
PS C:\> Copy-DbaResourceGovernor -Source sqlserver2014a -Destination sqlcluster -WhatIf
Shows what would happen if the command were executed.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[object[]]$ResourcePool,
[object[]]$ExcludeResourcePool,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
process {
try {
$sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 10
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
return
}
$sourceClassifierFunction = Get-DbaRgClassifierFunction -SqlInstance $sourceServer
foreach ($destinstance in $Destination) {
try {
$destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 10
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $destinstance -Continue
}
$destClassifierFunction = Get-DbaRgClassifierFunction -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
$sql = "ALTER RESOURCE GOVERNOR RECONFIGURE;"
Write-Message -Level Debug -Message $sql
Write-Message -Level Verbose -Message "Reconfiguring Resource Governor."
$destServer.Query($sql)
} else {
if ($Force -eq $false) {
$copyResourceGovClassifierFunc.Status = "Skipped"
$copyResourceGovClassifierFunc.Notes = "Already exists on destination"
$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)
$sql = "ALTER RESOURCE GOVERNOR RECONFIGURE;"
Write-Message -Level Debug -Message $sql
Write-Message -Level Verbose -Message "Reconfiguring Resource Governor."
$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
$sql = "ALTER RESOURCE GOVERNOR RECONFIGURE;"
Write-Message -Level Debug -Message $sql
Write-Message -Level Verbose -Message "Reconfiguring Resource Governor."
$destServer.Query($sql)
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 on destination"
$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
$sql = "ALTER RESOURCE GOVERNOR RECONFIGURE;"
Write-Message -Level Debug -Message $sql
Write-Message -Level Verbose -Message "Reconfiguring Resource Governor."
$destServer.Query($sql)
}
}
}
}
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
$sql = "ALTER RESOURCE GOVERNOR RECONFIGURE;"
Write-Message -Level Debug -Message $sql
Write-Message -Level Verbose -Message "Reconfiguring Resource Governor."
$destServer.Query($sql)
}
} 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)
$sql = "ALTER RESOURCE GOVERNOR RECONFIGURE;"
Write-Message -Level Debug -Message $sql
Write-Message -Level Verbose -Message "Reconfiguring Resource Governor."
$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 Path
Destination file path. If not specified, the file path of the source will be used (or the default data directory).
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaServerAudit
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Copy-DbaServerAudit -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
.EXAMPLE
PS C:\> Copy-DbaServerAudit -Source sqlserver-0 -Destination sqlserver-1 -Audit audit1 -Path 'C:\audit1'
Copies audit audit1 from sqlserver-0 to sqlserver-1. The file path on sqlserver-1 will be set to 'C:\audit1'.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]
$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]
$DestinationSqlCredential,
[object[]]$Audit,
[object[]]$ExcludeAudit,
[string]$Path,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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
}
if ($Path) {
$currentAudit.FilePath = $Path
}
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 on destination"
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-DbaPath -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
$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-DbaDirectory -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
$currentAudit.FilePath = $datadir
}
}
} else {
$datadir = Get-SqlDefaultPaths $destServer data
$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."
$sql = $currentAudit.Script() | Out-String
$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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaServerAuditSpecification
.EXAMPLE
PS C:\> 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
PS C:\> Copy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster -AuditSpecification 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
PS C:\> Copy-DbaServerAuditSpecification -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[object[]]$AuditSpecification,
[object[]]$ExcludeAuditSpecification,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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 | Select-DefaultView -Property DateTime, SourceServer, DestinationServer, Name, Type, Status, Notes -TypeName MigrationObject
}
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.Notes = "Already exists on destination"
$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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaServerTrigger
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Copy-DbaServerTrigger -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]
$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]
$DestinationSqlCredential,
[object[]]$ServerTrigger,
[object[]]$ExcludeServerTrigger,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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.Notes = "Already exists on destination"
$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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaSpConfigure
.EXAMPLE
PS C:\> Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster
Copies all sp_configure settings from sqlserver2014a to sqlcluster
.EXAMPLE
PS C:\> Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -ConfigName DefaultBackupCompression, IsSqlClrEnabled -SourceSqlCredential $cred
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
PS C:\> Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -ExcludeConfigName DefaultBackupCompression, IsSqlClrEnabled
Copies all configs except for IsSqlClrEnabled and DefaultBackupCompression, from sqlserver2014a to sqlcluster.
.EXAMPLE
PS C:\> Copy-DbaSpConfigure -Source sqlserver2014a -Destination sqlcluster -WhatIf
Shows what would happen if the command were executed.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[object[]]$ConfigName,
[object[]]$ExcludeConfigName,
[Alias('Silent')]
[switch]$EnableException
)
begin {
try {
$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 {
$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
}
}
#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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Copy-DbaSsisCatalog
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Copy-DbaSsisCatalog -Source sqlserver2014a -Destination sqlcluster -WhatIf -Force
Shows what would happen if the command were executed using force.
.EXAMPLE
PS C:\> $SecurePW = Read-Host "Enter password" -AsSecureString
PS C:\> 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)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[parameter(Mandatory)]
[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-DbaService -ComputerName $Computer -Type SSIS
if ($result) {
#Variable marked as unused by PSScriptAnalyzer
#$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 = $sourceServer.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 {
[CmdletBinding(SupportsShouldProcess)]
param (
[String]$Folder,
[String]$Description,
[Switch]$Force
)
if ($Pscmdlet.ShouldProcess($folder, "Creating new Catalog Folder")) {
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 {
[CmdletBinding(SupportsShouldProcess)]
param (
[String]$Folder,
[String]$Environment,
[Switch]$Force
)
if ($Pscmdlet.ShouldProcess($folder, "Creating new Environment Folder")) {
$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 {
[CmdletBinding(SupportsShouldProcess)]
param (
[System.Security.SecureString]$SecurePassword
)
if ($Pscmdlet.ShouldProcess("Creating New SSISDB Catalog")) {
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()
}
}
try {
$sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 11
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
return
}
try {
$sourceSSIS = New-Object Microsoft.SqlServer.Management.IntegrationServices.IntegrationServices $sourceServer
} catch {
Stop-Function -Message "There was an error connecting to the source integration services." -Target $sourceServer -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 {
$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.ComputerName
} catch {
Stop-Function -Message "An error occurred when checking the destination for Integration Services. Is Integration Services installed?" -Target $destinstance -ErrorRecord $_
}
try {
$destinationSSIS = New-Object Microsoft.SqlServer.Management.IntegrationServices.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 -SecurePassword $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
# here to avoid an empty catch
$null = 1
}
$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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Copy-DbaSysDbUserObject
.EXAMPLE
PS C:\> Copy-DbaSysDbUserObject -Source $sourceServer -Destination $destserver
Copies user objects from source to destination
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[Parameter(Mandatory)]
[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 {
$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 {
$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 = "Already 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 = "Already 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-DbaModule -SqlInstance $sourceserver -Database $systemDb -ExcludeSystemObjects | 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-DbaModule -SqlInstance $destServer -ExcludeSystemObjects -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 = "Already 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
# here to avoid an empty catch
$null = 1
}
}
}
} catch {
# Don't care - long story having to do with duplicate stuff
# here to avoid an empty catch
$null = 1
}
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Copy-SqlSysDbUserObjects
}
}
function Copy-DbaXESession {
<#
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Copy-DbaXESession
.EXAMPLE
PS C:\> Copy-DbaXESession -Source sqlserver2014a -Destination sqlcluster
Copies all Extended Event sessions from sqlserver2014a to sqlcluster using Windows credentials.
.EXAMPLE
PS C:\> Copy-DbaXESession -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
PS C:\> Copy-DbaXESession -Source sqlserver2014a -Destination sqlcluster -WhatIf
Shows what would happen if the command were executed.
.EXAMPLE
PS C:\> Copy-DbaXESession -Source sqlserver2014a -Destination sqlcluster -XeSession CheckQueries, MonitorUserDefinedException
Copies only the Extended Events named CheckQueries and MonitorUserDefinedException from sqlserver2014a to sqlcluster.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter]$Source,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]
$SourceSqlCredential,
[PSCredential]
$DestinationSqlCredential,
[object[]]$XeSession,
[object[]]$ExcludeXeSession,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Copy-DbaExtendedEvent
try {
$sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -MinimumVersion 11
} 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 {
$destServer = Connect-SqlInstance -SqlInstance $destinstance -SqlCredential $DestinationSqlCredential -MinimumVersion 11
} 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 on destination"
$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
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Copy-DbaXESessionTemplate
.EXAMPLE
PS C:\> Copy-DbaXESessionTemplate
Copies non-Microsoft templates from the dbatools template repository (\bin\xetemplates\) to $home\Documents\SQL Server Management Studio\Templates\XEventTemplates.
.EXAMPLE
PS C:\> 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 target SQL Server instance or instances.
.PARAMETER Credential
Credential object used to connect to the Windows server 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://wsmelton.github.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Disable-DbaAgHadr
.EXAMPLE
PS C:\> Disable-DbaAgHadr -SqlInstance sql2016
Sets Hadr service to disabled for the instance sql2016 but changes will not be applied until the next time the server restarts.
.EXAMPLE
PS C:\> Disable-DbaAgHadr -SqlInstance sql2016 -Force
Sets Hadr service to disabled for the instance sql2016, and restart the service to apply the change.
.EXAMPLE
PS C:\> 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, ConfirmImpact = "High")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$Credential,
[switch]$Force,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$computer = $computerFullName = $instance.ComputerName
$instanceName = $instance.InstanceName
if (-not (Test-ElevationRequirement -ComputerName $instance)) {
return
}
$noChange = $false
<#
#Variable marked as unused by PSScriptAnalyzer
switch ($instance.InstanceName) {
'MSSQLSERVER' { $agentName = 'SQLSERVERAGENT' }
default { $agentName = "SQLAgent`$$instanceName" }
}
#>
try {
Write-Message -Level Verbose -Message "Checking current Hadr setting for $computer"
$currentState = Get-WmiHadr -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-DbaService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
$null = Start-DbaService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
} catch {
Stop-Function -Message "Issue restarting $instance" -Target $instance -Continue
}
}
}
$newState = Get-WmiHadr -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#CodeStyle,Messaging,FlowControl,Pipeline#
function Disable-DbaFilestream {
<#
.SYNOPSIS
Sets the status of FileStream on specified SQL Server instances both at the server level and the instance level
.DESCRIPTION
Connects to the specified SQL Server instances, and sets the status of the FileStream feature to the required value
To perform the action, the SQL Server instance must be restarted. By default we will prompt for confirmation for this action, this can be overridden with the -Force switch
.PARAMETER SqlInstance
The target SQL Server instance or instances. Defaults to localhost.
.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Credential
Login to the target 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.
.PARAMETER Force
Restart SQL Instance after changes. Use this parameter with care as it overrides whatif.
.PARAMETER WhatIf
Shows what would happen if the command runs. The command is not run unless Force is specified.
.PARAMETER Confirm
Prompts you for confirmation before running the command.
.NOTES
Tags: Filestream
Author: Stuart Moore ( @napalmgram ) | Chrissy LeMaire ( @cl )
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Disable-DbaFilestream -SqlInstance server1\instance2
Prompts for confirmation. Disables filestream on the service and instance levels.
.EXAMPLE
PS C:\> Disable-DbaFilestream -SqlInstance server1\instance2 -Confirm:$false
Does not prompt for confirmation. Disables filestream on the service and instance levels.
.EXAMPLE
PS C:\> Get-DbaFilestream -SqlInstance server1\instance2, server5\instance5, prod\hr | Where-Object InstanceAccessLevel -gt 0 | Disable-DbaFilestream -Force
Using this pipeline you can scan a range of SQL instances and disable filestream on only those on which it's enabled.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
param (
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[DbaInstance[]]$SqlInstance,
[Parameter(ValueFromPipelineByPropertyName)]
[PSCredential]$SqlCredential,
[Parameter(ValueFromPipelineByPropertyName)]
[PSCredential]$Credential,
[switch]$Force,
[switch]$EnableException
)
begin {
$FileStreamLevel = $level = 0
$OutputLookup = @{
0 = 'Disabled'
1 = 'FileStream enabled for T-Sql access'
2 = 'FileStream enabled for T-Sql and IO streaming access'
3 = 'FileStream enabled for T-Sql, IO streaming, and remote clients'
}
}
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure connecting to $computer" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
# Instance level
$filestreamstate = [int]$server.Configuration.FilestreamAccessLevel.RunningValue
if ($Force -or $PSCmdlet.ShouldProcess($instance, "Changing from '$($OutputLookup[$filestreamstate])' to '$($OutputLookup[$level])' at the instance level")) {
try {
$null = Set-DbaSpConfigure -SqlInstance $server -Name FilestreamAccessLevel -Value $level -EnableException
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
# Server level
if ($server.IsClustered) {
$nodes = Get-DbaWsfcNode -ComputerName $instance -Credential $Credential
foreach ($node in $nodes.Name) {
$result = Set-FileSystemSetting -Instance $node -Credential $Credential -FilestreamLevel $FileStreamLevel
}
} else {
$result = Set-FileSystemSetting -Instance $instance -Credential $Credential -FilestreamLevel $FileStreamLevel
}
if ($Force) {
#$restart replaced with $null as it was identified as a unused variable
$null = Restart-DbaService -ComputerName $instance.ComputerName -InstanceName $server.ServiceName -Type Engine -Force
}
Get-DbaFilestream -SqlInstance $instance -SqlCredential $SqlCredential -Credential $Credential
if ($filestreamstate -ne $level -and -not $Force) {
Write-Message -Level Warning -Message "[$instance] $result"
}
}
}
}
}
#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 instance or instances. 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.
.NOTES
Tags: Certificate
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Disable-DbaForceNetworkEncryption
Disables Force Encryption on the default (MSSQLSERVER) instance on localhost - requires (and checks for) RunAs admin.
.EXAMPLE
PS C:\> 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
PS C:\> Disable-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2 -WhatIf
Shows what would happen if the command were executed.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
param (
[Parameter(ValueFromPipeline)]
[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
}
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 something
# here to avoid an empty catch
$null = 1
}
$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
#Variable marked as unused by PSScriptAnalyzer
#$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
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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Disable-DbaTraceFlag
.EXAMPLE
PS C:\> Disable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 3226
Disable the globally running trace flag 3226 on SQL Server instance sql2016
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(Mandatory)]
[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
}
$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 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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Dismount-DbaDatabase
.EXAMPLE
PS C:\> Detach-DbaDatabase -SqlInstance sql2016b -Database SharePoint_Config, WSS_Logging
Detaches SharePoint_Config and WSS_Logging from sql2016b
.EXAMPLE
PS C:\> 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
PS C:\> 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 target SQL Server instance or instances.
.PARAMETER Credential
Credential object used to connect to the Windows server 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, HA, AG, AvailabilityGroup
Author: Shawn Melton (@wsmelton), http://wsmelton.github.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Enable-DbaAgHadr
.EXAMPLE
PS C:\> Enable-DbaAgHadr -SqlInstance sql2016
Sets Hadr service to enabled for the instance sql2016 but changes will not be applied until the next time the server restarts.
.EXAMPLE
PS C:\> Enable-DbaAgHadr -SqlInstance sql2016 -Force
Sets Hadr service to enabled for the instance sql2016, and restart the service to apply the change.
.EXAMPLE
PS C:\> 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, ConfirmImpact = "High")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$Credential,
[switch]$Force,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$computer = $computerFullName = $instance.ComputerName
$instanceName = $instance.InstanceName
if (-not (Test-ElevationRequirement -ComputerName $instance)) {
return
}
$noChange = $false
<#
#Variable marked as unused by PSScriptAnalyzer
switch ($instance.InstanceName) {
'MSSQLSERVER' { $agentName = 'SQLSERVERAGENT' }
default { $agentName = "SQLAgent`$$instanceName" }
}
#>
try {
Write-Message -Level Verbose -Message "Checking current Hadr setting for $computer"
$currentState = Get-WmiHadr -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-DbaService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
$null = Start-DbaService -ComputerName $computerFullName -InstanceName $instanceName -Type Agent, Engine
} catch {
Stop-Function -Message "Issue restarting $instance" -Target $instance -Continue
}
}
}
$newState = Get-WmiHadr -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#CodeStyle,Messaging,FlowControl,Pipeline#
function Enable-DbaFilestream {
<#
.SYNOPSIS
Enables FileStream on specified SQL Server instances
.DESCRIPTION
Connects to the specified SQL Server instances, and Enables the FileStream feature to the required value
To perform the action, the SQL Server instance must be restarted. By default we will prompt for confirmation for this action, this can be overridden with the -Force switch
.PARAMETER SqlInstance
The target SQL Server instance or instances. Defaults to localhost.
.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Credential
Login to the target server using alternative credentials.
.PARAMETER FileStreamLevel
The level to of FileStream to be enabled:
1 or TSql - T-Sql Access Only
2 or TSqlIoStreaming - T-Sql and Win32 access enabled
3 or TSqlIoStreamingRemoteClient T-Sql, Win32 and Remote access enabled
.PARAMETER ShareName
Specifies the Windows file share name to be used for storing the FILESTREAM data.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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
Restart SQL Instance after changes. Use this parameter with care as it overrides whatif.
.PARAMETER WhatIf
Shows what would happen if the command runs. The command is not run unless Force is specified.
.PARAMETER Confirm
Prompts you for confirmation before running the command.
.NOTES
Tags: Filestream
Author: Stuart Moore ( @napalmgram ) | Chrissy LeMaire ( @cl )
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Enable-DbaFilestream -SqlInstance server1\instance2 -FileStreamLevel TSql
PS C:\> Enable-DbaFilestream -SqlInstance server1\instance2 -FileStreamLevel 1
These commands are functionally equivalent, both will set Filestream level on server1\instance2 to T-Sql Only
.EXAMPLE
PS C:\> Get-DbaFilestream -SqlInstance server1\instance2, server5\instance5, prod\hr | Where-Object InstanceAccessLevel -eq 0 | Enable-DbaFilestream -FileStreamLevel TSqlIoStreamingRemoteClient -Force
Using this pipeline you can scan a range of SQL instances and enable filestream on only those on which it's disabled.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Medium")]
param (
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[DbaInstance[]]$SqlInstance,
[Parameter(ValueFromPipelineByPropertyName)]
[PSCredential]$SqlCredential,
[Parameter(ValueFromPipelineByPropertyName)]
[PSCredential]$Credential,
[ValidateSet("TSql", "TSqlIoStreaming", "TSqlIoStreamingRemoteClient", 1, 2, 3)]
[string]$FileStreamLevel = 1,
[string]$ShareName,
[switch]$Force,
[switch]$EnableException
)
begin {
if ($FileStreamLevel -notin (1, 2, 3)) {
$FileStreamLevel = switch ($FileStreamLevel) {
"TSql" {
1
}
"TSqlIoStreaming" {
2
}
"TSqlIoStreamingRemoteClient" {
3
}
}
}
# = $finallevel removed as it was identified as a unused variable
$level = [int]$FileStreamLevel
$OutputLookup = @{
0 = 'Disabled'
1 = 'FileStream enabled for T-Sql access'
2 = 'FileStream enabled for T-Sql and IO streaming access'
3 = 'FileStream enabled for T-Sql, IO streaming, and remote clients'
}
}
process {
if ($ShareName -and $level -lt 2) {
Stop-Function -Message "Filestream must be at least level 2 when using ShareName"
return
}
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure connecting to $computer" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$filestreamstate = [int]$server.Configuration.FilestreamAccessLevel.ConfigValue
if ($Force -or $PSCmdlet.ShouldProcess($instance, "Changing from '$($OutputLookup[$filestreamstate])' to '$($OutputLookup[$level])' at the instance level")) {
# Server level
if ($server.IsClustered) {
$nodes = Get-DbaWsfcNode -ComputerName $instance
foreach ($node in $nodes.Name) {
$result = Set-FileSystemSetting -Instance $node -Credential $Credential -ShareName $ShareName -FilestreamLevel $level
}
} else {
$result = Set-FileSystemSetting -Instance $instance -Credential $Credential -ShareName $ShareName -FilestreamLevel $level
}
# Instance level
if ($level -eq 3) {
$level = 2
}
try {
$null = Set-DbaSpConfigure -SqlInstance $server -Name FilestreamAccessLevel -Value $level -EnableException
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
if ($Force) {
#$restart replaced with $null as it was identified as a unused variable
$null = Restart-DbaService -ComputerName $server.ComputerName -InstanceName $server.ServiceName -Type Engine -Force
}
Get-DbaFilestream -SqlInstance $instance -SqlCredential $SqlCredential -Credential $Credential
if ($filestreamstate -ne $level -and -not $Force) {
Write-Message -Level Warning -Message "[$instance] $result"
}
}
}
}
}
#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 instance or instances.
.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.
.NOTES
Tags: Certificate, Encryption
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Enable-DbaForceNetworkEncryption
Enables Force Encryption on the default (MSSQLSERVER) instance on localhost. Requires (and checks for) RunAs admin.
.EXAMPLE
PS C:\> 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
PS C:\> Enable-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2 -WhatIf
Shows what would happen if the command were executed.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low", DefaultParameterSetName = 'Default')]
param (
[Parameter(ValueFromPipeline)]
[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
}
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 something
# here to avoid an empty catch
$null = 1
}
$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
#Variable marked as unused by PSScriptAnalyzer
#$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
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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Enable-DbaTraceFlag
.EXAMPLE
PS C:\> Enable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 3226
Enable the trace flag 3226 on SQL Server instance sql2016
.EXAMPLE
PS C:\> Enable-DbaTraceFlag -SqlInstance sql2016 -TraceFlag 1117, 1118
Enable multiple trace flags on SQL Server instance sql2016
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(Mandatory)]
[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
}
$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-DbaDbLogFile {
<#
.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 or instances.
.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 TargetLogSize
Specifies the target size of the transaction log file in megabytes.
.PARAMETER IncrementSize
Specifies the amount the transaction log should grow in megabytes. If this value differs from the suggested value based on your TargetLogSize, 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 ShrinkSize
Specifies the target size of the transaction log file for the shrink operation in megabytes.
.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 ExcludeDiskSpaceValidation
If this switch is enabled, the validation for enough disk space using Get-DbaDiskSpace command will be skipped.
This can be useful when you know that you have enough space to grow your TLog but you don't have PowerShell Remoting enabled to validate it.
.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
Author: Claudio Silva (@ClaudioESSilva)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: ALTER DATABASE permission
Limitations: Freespace cannot be validated on the directory where the log file resides in SQL Server 2005.
This script uses Get-DbaDiskSpace dbatools command to get the TLog's drive free space
.LINK
https://dbatools.io/Expand-DbaDbLogFile
.EXAMPLE
PS C:\> Expand-DbaDbLogFile -SqlInstance sqlcluster -Database db1 -TargetLogSize 50000
Grows the transaction log for database db1 on sqlcluster to 50000 MB and calculates the increment size.
.EXAMPLE
PS C:\> Expand-DbaDbLogFile -SqlInstance sqlcluster -Database db1, db2 -TargetLogSize 10000 -IncrementSize 200
Grows the transaction logs for databases db1 and db2 on sqlcluster to 1000MB and sets the growth increment to 200MB.
.EXAMPLE
PS C:\> Expand-DbaDbLogFile -SqlInstance sqlcluster -Database db1 -TargetLogSize 10000 -LogFileId 9
Grows the transaction log file with FileId 9 of the db1 database on sqlcluster instance to 10000MB.
.EXAMPLE
PS C:\> Expand-DbaDbLogFile -SqlInstance sqlcluster -Database (Get-Content D:\DBs.txt) -TargetLogSize 50000
Grows the transaction log of the databases specified in the file 'D:\DBs.txt' on sqlcluster instance to 50000MB.
.EXAMPLE
PS C:\> Expand-DbaDbLogFile -SqlInstance SqlInstance -Database db1,db2 -TargetLogSize 100 -IncrementSize 10 -ShrinkLogFile -ShrinkSize 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)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter]$SqlInstance,
[parameter(Position = 3)]
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[parameter(Position = 4)]
[object[]]$ExcludeDatabase,
[parameter(Position = 5, Mandatory)]
[Alias('TargetLogSizeMB')]
[int]$TargetLogSize,
[parameter(Position = 6)]
[Alias('IncrementSizeMB')]
[int]$IncrementSize = -1,
[parameter(Position = 7)]
[int]$LogFileId = -1,
[parameter(Position = 8, ParameterSetName = 'Shrink', Mandatory)]
[switch]$ShrinkLogFile,
[parameter(Position = 9, ParameterSetName = 'Shrink', Mandatory)]
[Alias('ShrinkSizeMB')]
[int]$ShrinkSize,
[parameter(Position = 10, ParameterSetName = 'Shrink')]
[AllowEmptyString()]
[string]$BackupDirectory,
[switch]$ExcludeDiskSpaceValidation,
[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 = $TargetLogSize * 1024
[int]$LogIncrementSize = $IncrementSize * 1024
[int]$ShrinkSizeKB = $ShrinkSize * 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-DbaPath -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) {
$databases = $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/$($Databases.Count)"
if ($server.Databases[$db]) {
Write-Progress `
-Id 1 `
-Activity "Using database: $db on Instance: '$SqlInstance'" `
-PercentComplete ($databaseProgressbar / $Databases.Count * 100) `
-Status "Processing - $databaseProgressbar of $($Databases.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)
if ($ExcludeDiskSpaceValidation -eq $false) {
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"
try {
[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))"
} catch {
#Could not validate the disk space. Will ask if we want to continue.
$TotalTLogFreeDiskSpaceKB = 0
}
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
}
} elseif ($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
}
}
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 ($TargetLogSize) {
{ $_ -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 ($TargetLogSize) {
{ $_ -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 (($IncrementSize % 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)
$ShrinkSize = $ShrinkSizeKB / 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 -ShrinkSize 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 $ShrinkSize) {
$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 -eq $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($ShrinkSize, [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 $ShrinkSize -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 ($IncrementSize -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 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 * 1024)
CurrentSize = [dbasize]($TargetLogSize * 1024 * 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
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter TargetLogSizeMB
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter IncrementSizeMB
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Parameter ShrinkSizeMB
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Expand-DbaTLogResponsibly
}
}
#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 target SQL Server instance or instances. SQL Server 2012 and above supported.
.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Path
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 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, HA, AG, AvailabilityGroup
Author: Chris Sommer (@cjsommer), cjsommer.com
dbatools PowerShell module (https://dbatools.io)
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaAvailabilityGroup
.EXAMPLE
PS C:\> Export-DbaAvailabilityGroup -SqlInstance sql2012
Exports all Availability Groups from SQL server "sql2012". Output scripts are written to the Documents\SqlAgExports directory by default.
.EXAMPLE
PS C:\> Export-DbaAvailabilityGroup -SqlInstance sql2012 -Path 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
PS C:\> Export-DbaAvailabilityGroup -SqlInstance sql2012 -Path 'C:\dir with spaces\availability_group_exports' -AvailabilityGroup 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
PS C:\> Export-DbaAvailabilityGroup -SqlInstance sql2014 -Path 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)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$AvailabilityGroup,
[object[]]$ExcludeAvailabilityGroup,
[Alias("OutputLocation", "FilePath")]
[string]$Path = "$([Environment]::GetFolderPath("MyDocuments"))\SqlAgExport",
[switch]$NoClobber,
[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.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 = "$Path\$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"
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Export-DbaCmsRegServer {
<#
.SYNOPSIS
Exports registered servers and registered server groups to file
.DESCRIPTION
Exports registered servers and registered server groups to file
.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 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-DbaCmsRegServer, Get-DbaCmsRegServerGroup, 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
Tags: RegisteredServer, CMS
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaCmsRegServer
.EXAMPLE
PS C:\> Export-DbaCmsRegServer -SqlInstance sql2008
Exports all Registered Server and Registered Server Groups on sql2008 to an automatically generated file name in the current directory
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sql2008, sql2012 | Export-DbaCmsRegServer
Exports all registered servers on sql2008 and sql2012. Warning - each one will have its own individual file. Consider piping groups.
.EXAMPLE
PS C:\> Get-DbaCmsRegServerGroup -SqlInstance sql2008, sql2012 | Export-DbaCmsRegServer
Exports all registered servers on sql2008 and sql2012, organized by group.
#>
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "", Justification = "For Parameter CredentialPersistenceType")]
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-DbaCmsRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Id 1
}
foreach ($object in $InputObject) {
try {
if ($object -is [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore]) {
$object = Get-DbaCmsRegServerGroup -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 $_
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Export-DbaRegisteredServer
}
}
function Export-DbaCredential {
<#
.SYNOPSIS
Exports credentials INCLUDING PASSWORDS, unless specified otherwise, to sql file.
.DESCRIPTION
Exports credentials INCLUDING PASSWORDS, unless specified otherwise, to sql file.
Requires remote Windows access if exporting the password.
.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 Credential
Login to the target OS using alternative credentials. Accepts credential objects (Get-Credential)
.PARAMETER Path
The path to the exported sql file.
.PARAMETER Identity
The credentials to export. If unspecified, all credentials will be exported.
.PARAMETER InputObject
Allow credentials to be piped in from Get-DbaCredential
.PARAMETER ExcludePassword
Exports the SQL credential without any sensitive information.
.PARAMETER InputObject
Allow credentials to be piped in from Get-DbaCredential
.PARAMETER Append
Append to 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: Credential
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Export-DbaCredential -SqlInstance sql2017 -Path C:\temp\cred.sql
Exports credentials, including passwords, from sql2017 to the file C:\temp\cred.sql
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[string[]]$Identity,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[string]$Path,
[switch]$ExcludePassword,
[switch]$Append,
[Microsoft.SqlServer.Management.Smo.Credential[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
$InputObject += $server.Credentials
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if ($Identity) {
$InputObject = $InputObject | Where-Object Identity -in $Identity
}
if (!(Test-SqlSa -SqlInstance $instance -SqlCredential $sqlcredential)) {
Stop-Function -Message "Not a sysadmin on $instance. Quitting." -Target $instance -Continue
}
Write-Message -Level Verbose -Message "Getting NetBios name for $instance."
$sourceNetBios = Resolve-NetBiosName $server
Write-Message -Level Verbose -Message "Checking if Remote Registry is enabled on $instance."
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 $instance." -Target $sourceNetBios -ErrorRecord $_
return
}
if (-not (Test-Bound -ParameterName Path)) {
$timenow = (Get-Date -uformat "%m%d%Y%H%M%S")
$mydocs = [Environment]::GetFolderPath('MyDocuments')
$path = "$mydocs\$($server.name.replace('\', '$'))-$timenow-credential.sql"
}
$sql = @()
if ($ExcludePassword) {
Stop-Function -Message "So sorry, there's no other way around it for now. The password has to be exported in plain text."
return
} else {
try {
$creds = Get-DecryptedObject -SqlInstance $server -Type Credential
} catch {
Stop-Function -Continue -Message "Failure" -ErrorRecord $_
}
foreach ($currentCred in $creds) {
$name = $currentCred.Name.Replace("'", "''")
$identity = $currentCred.Identity.Replace("'", "''")
$password = $currentCred.Password.Replace("'", "''")
$sql += "CREATE CREDENTIAL $name WITH IDENTITY = N'$identity', SECRET = N'$password'"
}
}
try {
if ($Append) {
Add-Content -Path $path -Value $sql
} else {
Set-Content -Path $path -Value $sql
}
Get-ChildItem -Path $path
} catch {
Stop-Function -Message "Can't write to $path" -ErrorRecord $_ -Continue
}
Write-Message -Level Verbose -Message "Attempting to migrate $credentialName"
Get-ChildItem -Path $path
}
}
}
function Export-DbaDacPackage {
<#
.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
The target SQL Server instance or instances.
.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 Type
Selecting the type of the export: Dacpac (default) or Bacpac.
.PARAMETER Table
List of the tables to include into the export. Should be provided as an array of strings: dbo.Table1, Table2, Schema1.Table3.
.PARAMETER DacOption
Export options for a corresponding export type. Can be created by New-DbaDacOption -Type Dacpac | Bacpac
.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 (@richiebzzzt)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaDacPackage
.EXAMPLE
PS C:\> Export-DbaDacPackage -SqlInstance sql2016 -Database SharePoint_Config
Exports the dacpac for SharePoint_Config on sql2016 to $home\Documents\SharePoint_Config.dacpac
.EXAMPLE
PS C:\> $options = New-DbaDacOption -Type Dacpac -Action Export
PS C:\> $options.ExtractAllTableData = $true
PS C:\> $options.CommandTimeout = 0
PS C:\> Export-DbaDacPackage -SqlInstance sql2016 -Database DB1 -Options $options
Uses DacOption object to set the CommandTimeout to 0 then extracts the dacpac for DB1 on sql2016 to $home\Documents\DB1.dacpac including all table data.
.EXAMPLE
PS C:\> Export-DbaDacPackage -SqlInstance sql2016 -AllUserDatabases -ExcludeDatabase "DBMaintenance","DBMonitoring" C:\temp
Exports dacpac packages for all USER databases, excluding "DBMaintenance" & "DBMonitoring", on sql2016 and saves them to C:\temp
.EXAMPLE
PS C:\> $moreparams = "/OverwriteFiles:$true /Quiet:$true"
PS C:\> Export-DbaDacPackage -SqlInstance sql2016 -Database SharePoint_Config -Path C:\temp -ExtendedParameters $moreparams
Using extended parameters to over-write the files and performs the extraction in quiet mode. Uses command line instead of SMO behind the scenes.
#>
[CmdletBinding(DefaultParameterSetName = 'SMO')]
param
(
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstance[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[object[]]$Database,
[object[]]$ExcludeDatabase,
[switch]$AllUserDatabases,
[string]$Path = "$home\Documents",
[parameter(ParameterSetName = 'SMO')]
[Alias('ExtractOptions', 'ExportOptions', 'DacExtractOptions', 'DacExportOptions', 'Options', 'Option')]
[object]$DacOption,
[parameter(ParameterSetName = 'CMD')]
[string]$ExtendedParameters,
[parameter(ParameterSetName = 'CMD')]
[string]$ExtendedProperties,
[ValidateSet('Dacpac', 'Bacpac')]
[string]$Type = 'Dacpac',
[parameter(ParameterSetName = 'SMO')]
[string[]]$Table,
[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"
return
}
if (-not (Test-Path $Path)) {
Write-Message -Level Verbose "Assuming that $Path is a file path"
$parentFolder = Split-Path $path -Parent
if (-not (Test-Path $parentFolder)) {
Stop-Function -Message "$parentFolder doesn't exist or access denied"
return
}
$leaf = Split-Path $path -Leaf
$fileName = Join-Path (Get-Item $parentFolder) $leaf
} else {
$fileItem = Get-Item $Path
if ($fileItem -is [System.IO.DirectoryInfo]) {
$parentFolder = $fileItem.FullName
} elseif ($fileItem -is [System.IO.FileInfo]) {
$fileName = $fileItem.FullName
}
}
if (-not $script:core) {
$dacfxPath = Resolve-Path -Path "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Dac.dll"
if ((Test-Path $dacfxPath) -eq $false) {
Stop-Function -Message 'Dac Fx library not found.' -EnableException $EnableException
return
} 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.' -ErrorRecord $_
return
}
}
}
#check that at least one of the DB selection parameters was specified
if (!$AllUserDatabases -and !$Database) {
Stop-Function -Message "Either -Database or -AllUserDatabases should be specified" -Continue
}
#Check Option object types - should have a specific type
if ($Type -eq 'Dacpac') {
if ($DacOption -and $DacOption -isnot [Microsoft.SqlServer.Dac.DacExtractOptions]) {
Stop-Function -Message "Microsoft.SqlServer.Dac.DacExtractOptions object type is expected - got $($DacOption.GetType())."
return
}
} elseif ($Type -eq 'Bacpac') {
if ($DacOption -and $DacOption -isnot [Microsoft.SqlServer.Dac.DacExportOptions]) {
Stop-Function -Message "Microsoft.SqlServer.Dac.DacExportOptions object type is expected - got $($DacOption.GetType())."
return
}
}
#Create a tuple to be used as a table filter
if ($Table) {
$tblList = New-Object 'System.Collections.Generic.List[Tuple[String, String]]'
foreach ($tableItem in $Table) {
$tableSplit = $tableItem.Split('.')
if ($tableSplit.Count -gt 1) {
$tblName = $tableSplit[-1]
$schemaName = $tableSplit[-2]
} else {
$tblName = [string]$tableSplit
$schemaName = 'dbo'
}
$tblList.Add((New-Object "tuple[String, String]" -ArgumentList $schemaName, $tblName))
}
} else {
$tblList = $null
}
foreach ($instance in $sqlinstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$cleaninstance = $instance.ToString().Replace('\', '-')
$dbs = Get-DbaDatabase -SqlInstance $server -OnlyAccessible -ExcludeSystem -Database $Database -ExcludeDatabase $ExcludeDatabase
if (-not $dbs) {
Stop-Function -Message "Databases not found on $instance" -Target $instance -Continue
}
foreach ($db in $dbs) {
$resultstime = [diagnostics.stopwatch]::StartNew()
$dbname = $db.name
$connstring = $server.ConnectionContext.ConnectionString
if ($connstring -notmatch 'Database=') {
$connstring = "$connstring;Database=$dbname"
}
if ($fileName) {
$currentFileName = $fileName
} else {
if ($Type -eq 'Dacpac') { $ext = 'dacpac' }
elseif ($Type -eq 'Bacpac') { $ext = 'bacpac' }
$currentFileName = Join-Path $parentFolder "$cleaninstance-$dbname.$ext"
}
Write-Message -Level Verbose -Message "Using connection string $connstring"
#using SMO by default
if ($PsCmdlet.ParameterSetName -eq 'SMO') {
try {
$dacSvc = New-Object -TypeName Microsoft.SqlServer.Dac.DacServices -ArgumentList $connstring -ErrorAction Stop
} catch {
Stop-Function -Message "Could not connect to the connection string $connstring" -Target $instance -Continue
}
if (!$DacOption) {
$opts = New-DbaDacOption -Type $Type -Action Export
} else {
$opts = $DacOption
}
$null = $output = Register-ObjectEvent -InputObject $dacSvc -EventName "Message" -SourceIdentifier "msg" -Action { $EventArgs.Message.Message }
if ($Type -eq 'Dacpac') {
Write-Message -Level Verbose -Message "Initiating Dacpac extract to $currentFileName"
#not sure how to extract that info from the existing DAC application, leaving 1.0.0.0 for now
$version = New-Object System.Version -ArgumentList '1.0.0.0'
try {
$dacSvc.Extract($currentFileName, $dbname, $dbname, $version, $null, $tblList, $opts, $null)
} catch {
Stop-Function -Message "DacServices extraction failure" -ErrorRecord $_ -Continue
} finally {
Unregister-Event -SourceIdentifier "msg"
}
} elseif ($Type -eq 'Bacpac') {
Write-Message -Level Verbose -Message "Initiating Bacpac export to $currentFileName"
try {
$dacSvc.ExportBacpac($currentFileName, $dbname, $opts, $tblList, $null)
} catch {
Stop-Function -Message "DacServices export failure" -ErrorRecord $_ -Continue
} finally {
Unregister-Event -SourceIdentifier "msg"
}
}
$finalResult = ($output.output -join "`r`n" | Out-String).Trim()
} elseif ($PsCmdlet.ParameterSetName -eq 'CMD') {
if ($Type -eq 'Dacpac') { $action = 'Extract' }
elseif ($Type -eq 'Bacpac') { $action = 'Export' }
$cmdConnString = $connstring.Replace('"', "'")
$sqlPackageArgs = "/action:$action /tf:""$currentFileName"" /SourceConnectionString:""$cmdConnString"" $ExtendedParameters $ExtendedProperties"
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
$stdout = $process.StandardOutput.ReadToEnd()
$stderr = $process.StandardError.ReadToEnd()
$process.WaitForExit()
Write-Message -level Verbose -Message "StandardOutput: $stdout"
$finalResult = $stdout
} catch {
Stop-Function -Message "SQLPackage Failure" -ErrorRecord $_ -Continue
}
if ($process.ExitCode -ne 0) {
Stop-Function -Message "Standard output - $stderr" -Continue
}
}
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Database = $dbname
Path = $currentFileName
Elapsed = [prettytimespan]($resultstime.Elapsed)
Result = $finalResult
} | Select-DefaultView -ExcludeProperty ComputerName, InstanceName
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Export-DbaDacpac
}
}
function Export-DbaDiagnosticQuery {
<#
.SYNOPSIS
Export-DbaDiagnosticQuery can convert output 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 Finke (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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaDiagnosticQuery
.EXAMPLE
PS C:\> Invoke-DbaDiagnosticQuery -SqlInstance sql2016 | Export-DbaDiagnosticQuery -Path c:\temp
Converts output from Invoke-DbaDiagnosticQuery to multiple CSV files
.EXAMPLE
PS C:\> $output = Invoke-DbaDiagnosticQuery -SqlInstance sql2016
PS C:\> Export-DbaDiagnosticQuery -InputObject $output -ConvertTo Excel
Converts output from Invoke-DbaDiagnosticQuery to Excel worksheet(s) in the Documents folder
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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
}
}
}
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
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,FlowControl,Pipeline,CodeStyle#
function Export-DbaExecutionPlan {
<#
.SYNOPSIS
Exports execution plans to disk.
.DESCRIPTION
Exports execution plans to disk. Can pipe from Get-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 target SQL Server instance or instances.
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaExecutionPlan
.EXAMPLE
PS C:\> Export-DbaExecutionPlan -SqlInstance sqlserver2014a -Path C:\Temp
Exports all execution plans for sqlserver2014a. Files saved in to C:\Temp
.EXAMPLE
PS C:\> Export-DbaExecutionPlan -SqlInstance sqlserver2014a -Database db1, db2 -SinceLastExecution '2016-07-01 10:47:00' -Path C:\Temp
Exports all execution plans for databases db1 and db2 on sqlserver2014a since July 1, 2016 at 10:47 AM. Files saved in to C:\Temp
.EXAMPLE
PS C:\> Get-DbaExecutionPlan -SqlInstance sqlserver2014a | Export-DbaExecutionPlan -Path C:\Temp
Gets all execution plans for sqlserver2014a. Using Pipeline exports them all to C:\Temp
.EXAMPLE
PS C:\> Get-DbaExecutionPlan -SqlInstance sqlserver2014a | Export-DbaExecutionPlan -Path C:\Temp -WhatIf
Gets all execution plans for sqlserver2014a. Then shows what would happen if the results where piped to Export-DbaExecutionPlan
#>
[cmdletbinding(SupportsShouldProcess, 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 {
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) {
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 -or $SinceLastExecution -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.ToString("yyyy-MM-dd HH:mm:ss") + "' "
}
if (Test-Bound 'SinceLastExecution') {
Write-Message -Level Verbose -Message "Adding last execution time"
$whereArray += " last_execution_time >= '" + $SinceLastExecution.ToString("yyyy-MM-dd HH:mm:ss") + "' "
}
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-DbaInstance {
<#
.SYNOPSIS
Exports SQL Server *ALL* database restore scripts, 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 -Exclude parameters and use the other functions available within the dbatools module.
.DESCRIPTION
Export-DbaInstance consolidates most of the export scripts in dbatools into one command.
This is useful when you're looking to Export entire instances. It less flexible than using the underlying functions.
Think of it as an easy button. Unless an -Exclude is specified, it exports:
All database restore scripts.
All logins.
All database mail objects.
All credentials.
All objects within the Job Server (SQL Agent).
All linked servers.
All groups and servers within Central Management Server.
All SQL Server configuration objects (everything in sp_configure).
All user objects in system databases.
All system triggers.
All system backup devices.
All Audits.
All Endpoints.
All Extended Events.
All Policy Management objects.
All Resource Governor objects.
All Server Audit Specifications.
All Custom Errors (User Defined Messages).
All Server Roles.
All Availability Groups.
.PARAMETER SqlInstance
The target SQL Server instances
.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Credential
Alternative Windows credentials for exporting Linked Servers and Credentials. Accepts credential objects (Get-Credential)
.PARAMETER Path
The path to the export file
.PARAMETER SharedPath
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 used, databases are restored from backup using WITH REPLACE. This is useful if you want to stage some complex file paths.
.PARAMETER NoRecovery
If this switch is used, databases will be left in the No Recovery state to enable further backups to be added.
.PARAMETER IncludeDbMasterKey
Exports the db master key then logs into the server to copy it to the $Path
.PARAMETER Exclude
Exclude one or more objects to export
Databases
Logins
AgentServer
Credentials
LinkedServers
SpConfigure
CentralManagementServer
DatabaseMail
SysDbUserObjects
SystemTriggers
BackupDevices
Audits
Endpoints
ExtendedEvents
PolicyManagement
ResourceGovernor
ServerAuditSpecifications
CustomErrors
ServerRoles
AvailabilityGroups
ReplicationSettings
.PARAMETER BatchSeparator
Batch separator for scripting output. "GO" by default.
.PARAMETER ScriptingOption
Add scripting options to scripting output for all objects except Registered Servers and Extended Events.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Export
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaInstance
.EXAMPLE
PS C:\> Export-DbaInstance -SqlInstance sqlserver\instance
All databases, logins, job objects and sp_configure options will be exported from
sqlserver\instance to an automatically generated folder name in Documents.
.EXAMPLE
PS C:\> Export-DbaInstance -SqlInstance sqlcluster -Exclude Databases, Logins -Path C:\dr\sqlcluster
Exports everything but logins and database restore scripts to C:\dr\sqlcluster
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[string]$Path,
[switch]$NoRecovery,
[switch]$IncludeDbMasterKey,
[ValidateSet('Databases', 'Logins', 'AgentServer', 'Credentials', 'LinkedServers', 'SpConfigure', 'CentralManagementServer', 'DatabaseMail', 'SysDbUserObjects', 'SystemTriggers', 'BackupDevices', 'Audits', 'Endpoints', 'ExtendedEvents', 'PolicyManagement', 'ResourceGovernor', 'ServerAuditSpecifications', 'CustomErrors', 'ServerRoles', 'AvailabilityGroups', 'ReplicationSettings')]
[string[]]$Exclude,
[string]$BatchSeparator = 'GO',
[Microsoft.SqlServer.Management.Smo.ScriptingOptions]$ScriptingOption,
[switch]$EnableException
)
begin {
if ((Test-Bound -ParameterName Path)) {
if (-not ((Get-Item $Path -ErrorAction Ignore) -is [System.IO.DirectoryInfo])) {
Stop-Function -Message "Path must be a directory"
}
}
if (-not $ScriptingOption) {
$ScriptingOption = New-DbaScriptingOption
}
$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
$started = Get-Date
$ScriptingOptions = New-Object Microsoft.SqlServer.Management.Smo.ScriptingOptions
$ScriptingOptions.ScriptBatchTerminator = $true
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($instance in $SqlInstance) {
$stepCounter = $filecounter = 0
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 10
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if (-not (Test-Bound -ParameterName Path)) {
$timenow = (Get-Date -uformat "%m%d%Y%H%M%S")
$mydocs = [Environment]::GetFolderPath('MyDocuments')
$path = "$mydocs\$($server.name.replace('\', '$'))-$timenow"
}
if (-not (Test-Path $Path)) {
try {
$null = New-Item -ItemType Directory -Path $Path -ErrorAction Stop
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
return
}
}
if ($Exclude -notcontains 'SpConfigure') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting SQL Server Configuration"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting SQL Server Configuration"
Export-DbaSpConfigure -SqlInstance $server -Path "$Path\$fileCounter-sp_configure.sql"
if (-not (Test-Path "$Path\$fileCounter-sp_configure.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'CustomErrors') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting custom errors (user defined messages)"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting custom errors (user defined messages)"
$null = Get-DbaCustomError -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-customererrors.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-customererrors.sql"
if (-not (Test-Path "$Path\$fileCounter-customererrors.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'ServerRoles') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting server roles"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting server roles"
$null = Get-DbaServerRole -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-serverroles.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-serverroles.sql"
if (-not (Test-Path "$Path\$fileCounter-serverroles.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'Credentials') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting SQL credentials"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting SQL credentials"
$null = Export-DbaCredential -SqlInstance $server -Credential $Credential -Path "$Path\$fileCounter-credentials.sql" -Append
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-credentials.sql"
if (-not (Test-Path "$Path\$fileCounter-credentials.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'DatabaseMail') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting database mail"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting database mail"
$null = Get-DbaDbMailConfig -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-dbmail.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaDbMailAccount -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-dbmail.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaDbMailProfile -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-dbmail.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaDbMailServer -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-dbmail.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaDbMail -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-dbmail.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-dbmail.sql"
if (-not (Test-Path "$Path\$fileCounter-dbmail.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'CentralManagementServer') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting Central Management Server"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting Central Management Server"
$null = Get-DbaCmsRegServerGroup -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-regserver.sql" -Append -BatchSeparator 'GO'
$null = Get-DbaCmsRegServer -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-regserver.sql" -Append -BatchSeparator 'GO'
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-regserver.sql"
if (-not (Test-Path "$Path\$fileCounter-regserver.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'BackupDevices') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting Backup Devices"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting Backup Devices"
$null = Get-DbaBackupDevice -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-backupdevices.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-backupdevices.sql"
if (-not (Test-Path "$Path\$fileCounter-backupdevices.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'LinkedServers') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting linked servers"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting linked servers"
Export-DbaLinkedServer -SqlInstance $server -Path "$Path\$fileCounter-linkedservers.sql" -Credential $Credential -Append
if (-not (Test-Path "$Path\$fileCounter-linkedservers.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'SystemTriggers') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting System Triggers"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting System Triggers"
$null = Get-DbaServerTrigger -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-servertriggers.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$triggers = Get-Content -Path "$Path\$fileCounter-servertriggers.sql" -Raw -ErrorAction Ignore
if ($triggers) {
$triggers = $triggers.ToString() -replace 'CREATE TRIGGER', "GO`r`nCREATE TRIGGER"
$triggers = $triggers.ToString() -replace 'ENABLE TRIGGER', "GO`r`nENABLE TRIGGER"
$null = $triggers | Set-Content -Path "$Path\$fileCounter-servertriggers.sql" -Force
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-servertriggers.sql"
}
if (-not (Test-Path "$Path\$fileCounter-servertriggers.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'Databases') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting database restores"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting database restores"
Get-DbaBackupHistory -SqlInstance $server -Last | Restore-DbaDatabase -SqlInstance $server -NoRecovery:$NoRecovery -WithReplace -OutputScriptOnly -WarningAction SilentlyContinue | Out-File -FilePath "$Path\$fileCounter-databases.sql" -Append
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-databases.sql"
if (-not (Test-Path "$Path\$fileCounter-databases.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'Logins') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting logins"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting logins"
Export-DbaLogin -SqlInstance $server -Path "$Path\$fileCounter-logins.sql" -Append -WarningAction SilentlyContinue
if (-not (Test-Path "$Path\$fileCounter-logins.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'Audits') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting Audits"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting Audits"
$null = Get-DbaServerAudit -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-audits.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-audits.sql"
if (-not (Test-Path "$Path\$fileCounter-audits.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'ServerAuditSpecifications') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting Server Audit Specifications"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting Server Audit Specifications"
$null = Get-DbaServerAuditSpecification -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-auditspecs.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-auditspecs.sql"
if (-not (Test-Path "$Path\$fileCounter-auditspecs.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'Endpoints') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting Endpoints"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting Endpoints"
$null = Get-DbaEndpoint -SqlInstance $server | Where-Object IsSystemObject -eq $false | Export-DbaScript -Path "$Path\$fileCounter-endpoints.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-endpoints.sql"
if (-not (Test-Path "$Path\$fileCounter-endpoints.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'PolicyManagement') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting Policy Management"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting Policy Management"
$null = Get-DbaPbmCondition -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-policymanagement.sql" -Append -BatchSeparator $BatchSeparator
$null = Get-DbaPbmObjectSet -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-policymanagement.sql" -Append -BatchSeparator $BatchSeparator
$null = Get-DbaPbmPolicy -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-policymanagement.sql" -Append -BatchSeparator $BatchSeparator
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-policymanagement.sql"
if (-not (Test-Path "$Path\$fileCounter-policymanagement.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'ResourceGovernor') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting Resource Governor"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting Resource Governor"
$null = Get-DbaResourceGovernor -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-resourcegov.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaRgClassifierFunction -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-resourcegov.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaRgResourcePool -SqlInstance $server | Where-Object Name -notin 'default', 'internal' | Export-DbaScript -Path "$Path\$fileCounter-resourcegov.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaRgWorkloadGroup -SqlInstance $server | Where-Object Name -notin 'default', 'internal' | Export-DbaScript -Path "$Path\$fileCounter-resourcegov.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Add-Content -Value "ALTER RESOURCE GOVERNOR RECONFIGURE" -Path "$Path\$stepCounter-resourcegov.sql"
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-resourcegov.sql"
if (-not (Test-Path "$Path\$fileCounter-resourcegov.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'ExtendedEvents') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting Extended Events"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting Extended Events"
$null = Get-DbaXESession -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-extendedevents.sql" -Append -BatchSeparator 'GO'
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-extendedevents.sql"
if (-not (Test-Path "$Path\$fileCounter-extendedevents.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'AgentServer') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting job server"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting job server"
$null = Get-DbaAgentJobCategory -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-sqlagent.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaAgentOperator -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-sqlagent.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaAgentAlert -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-sqlagent.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaAgentProxy -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-sqlagent.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaAgentSchedule -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-sqlagent.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
$null = Get-DbaAgentJob -SqlInstance $server | Export-DbaScript -Path "$Path\$fileCounter-sqlagent.sql" -Append -BatchSeparator $BatchSeparator -ScriptingOptionsObject $ScriptingOption
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-sqlagent.sql"
if (-not (Test-Path "$Path\$fileCounter-sqlagent.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'ReplicationSettings') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting replication settings"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting replication settings"
$null = Export-DbaRepServerSetting -SqlInstance $instance -SqlCredential $SqlCredential -Path "$Path\$fileCounter-replication.sql"
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-replication.sql"
if (-not (Test-Path "$Path\$fileCounter-replication.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'SysDbUserObjects') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting user objects in system databases (this can take a minute)."
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting user objects in system databases (this can take a minute)."
$null = Get-DbaSysDbUserObjectScript -SqlInstance $server | Out-File -FilePath "$Path\$fileCounter-userobjectsinsysdbs.sql" -Append
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-userobjectsinsysdbs.sql"
if (-not (Test-Path "$Path\$fileCounter-userobjectsinsysdbs.sql")) {
$fileCounter--
}
}
if ($Exclude -notcontains 'AvailabilityGroups') {
$fileCounter++
Write-Message -Level Verbose -Message "Exporting availability group"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting availability groups"
$null = Get-DbaAvailabilityGroup -SqlInstance $server -WarningAction SilentlyContinue | Export-DbaScript -Path "$Path\$fileCounter-DbaAvailabilityGroups.sql" -Append -BatchSeparator $BatchSeparator #-ScriptingOptionsObject $ScriptingOption
Get-ChildItem -ErrorAction Ignore -Path "$Path\$fileCounter-DbaAvailabilityGroups.sql"
if (-not (Test-Path "$Path\$fileCounter-DbaAvailabilityGroups.sql")) {
$fileCounter--
}
}
Write-Progress -Activity "Performing Instance Export for $instance" -Completed
}
}
end {
$totaltime = ($elapsed.Elapsed.toString().Split(".")[0])
Write-Message -Level Verbose -Message "SQL Server export complete."
Write-Message -Level Verbose -Message "Export started: $started"
Write-Message -Level Verbose -Message "Export completed: $(Get-Date)"
Write-Message -Level Verbose -Message "Total Elapsed time: $totaltime"
}
}
function Export-DbaLinkedServer {
<#
.SYNOPSIS
Exports linked servers INCLUDING PASSWORDS, unless specified otherwise, to sql file.
.DESCRIPTION
Exports linked servers INCLUDING PASSWORDS, unless specified otherwise, to sql file.
Requires remote Windows access if exporting the password.
.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 linked servers. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Credential
Login to the target OS using alternative linked servers. Accepts credential objects (Get-Credential)
.PARAMETER Path
The path to the exported sql file.
.PARAMETER LinkedServer
The linked server(s) to export. If unspecified, all linked servers will be processed.
.PARAMETER InputObject
Allow credentials to be piped in from Get-DbaLinkedServer
.PARAMETER ExcludePassword
Exports the linked server without any sensitive information.
.PARAMETER Append
Append to 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: LinkedServer
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Export-DbaLinkedServer -SqlInstance sql2017 -Path C:\temp\ls.sql
Exports the linked servers, including passwords, from sql2017 to the file C:\temp\ls.sql
.EXAMPLE
PS C:\> Export-DbaLinkedServer -SqlInstance sql2017 -Path C:\temp\ls.sql -ExcludePassword
Exports the linked servers, without passwords, from sql2017 to the file C:\temp\ls.sql
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[string[]]$LinkedServer,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[string]$Path,
[switch]$ExcludePassword,
[switch]$Append,
[Microsoft.SqlServer.Management.Smo.LinkedServer[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 9
$InputObject += $server.LinkedServers
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if ($LinkedServer) {
$InputObject = $InputObject | Where-Object Name -in $LinkedServer
}
if (-not $InputObject) {
Write-Message -Level Verbose -Message "Nothing to export"
continue
}
if (!(Test-SqlSa -SqlInstance $instance -SqlCredential $sqlcredential)) {
Stop-Function -Message "Not a sysadmin on $instance. Quitting." -Target $instance -Continue
}
Write-Message -Level Verbose -Message "Getting NetBios name for $instance."
$sourceNetBios = Resolve-NetBiosName $server
Write-Message -Level Verbose -Message "Checking if Remote Registry is enabled on $instance."
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 $instance." -Target $sourceNetBios -ErrorRecord $_
return
}
if (-not (Test-Bound -ParameterName Path)) {
$timenow = (Get-Date -uformat "%m%d%Y%H%M%S")
$mydocs = [Environment]::GetFolderPath('MyDocuments')
$path = "$mydocs\$($server.name.replace('\', '$'))-$timenow-linkedserver.sql"
}
$sql = @()
if ($ExcludePassword) {
$sql += $InputObject.Script()
} else {
try {
$decrypted = Get-DecryptedObject -SqlInstance $server -Type LinkedServer
} catch {
Stop-Function -Continue -Message "Failure" -ErrorRecord $_
}
foreach ($ls in $InputObject) {
$currentls = $decrypted | Where-Object Name -eq $ls.Name
if ($currentls.Password) {
$password = $currentls.Password.Replace("'", "''")
$tempsql = $ls.Script()
$tempsql = $tempsql.Replace(' /* For security reasons the linked server remote logins password is changed with ######## */', '')
$tempsql = $tempsql.Replace("rmtpassword='########'", "rmtpassword='$password'")
$sql += $tempsql
} else {
$sql += $ls.Script()
}
}
}
try {
if ($Append) {
Add-Content -Path $path -Value $sql
} else {
Set-Content -Path $path -Value $sql
}
Get-ChildItem -Path $path
} catch {
Stop-Function -Message "Can't write to $path" -ErrorRecord $_ -Continue
}
}
}
}
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 target SQL Server instance or instances. 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 Path
The file to write to.
.PARAMETER NoClobber
If this switch is enabled, a file already existing at the path specified by Path 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 Path. If the file does not exist, it will be created.
.PARAMETER ExcludeJobs
If this switch is enabled, Agent job ownership will not be exported.
.PARAMETER ExcludeDatabases
If this switch is enabled, mappings for databases will not be exported.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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.
.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: Export, Login
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaLogin
.EXAMPLE
PS C:\> Export-DbaLogin -SqlInstance sql2005 -Path C:\temp\sql2005-logins.sql
Exports the logins for SQL Server "sql2005" and writes them to the file "C:\temp\sql2005-logins.sql"
.EXAMPLE
PS C:\> Export-DbaLogin -SqlInstance sqlserver2014a -ExcludeLogin realcajun -SqlCredential $scred -Path 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
PS C:\> Export-DbaLogin -SqlInstance sqlserver2014a -Login realcajun, netnerds -Path C:\temp\logins.sql
Exports ONLY logins netnerds and realcajun FROM sqlserver2014a to the file C:\temp\logins.sql
.EXAMPLE
PS C:\> 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
PS C:\> Export-DbaLogin -SqlInstance sqlserver2008 -Login realcajun, netnerds -Path 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
PS C:\> Export-DbaLogin -SqlInstance sqlserver2008 -Login realcajun -Path C:\temp\users.sql -DestinationVersion SQLServer2016
Exports login realcajun from sqlserver2008 to the file C:\temp\users.sql with syntax to run on SQL Server 2016
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter]$SqlInstance,
[Alias("Credential")]
[PSCredential]
$SqlCredential,
[object[]]$Login,
[object[]]$ExcludeLogin,
[Alias("Databases")]
[object[]]$Database,
[Alias("OutFile", "FilePath", "FileName")]
[string]$Path,
[Alias("NoOverwrite")]
[switch]$NoClobber,
[switch]$Append,
[switch]$ExcludeDatabases,
[switch]$ExcludeJobs,
[Alias('Silent')]
[switch]$EnableException,
[switch]$ExcludeGoBatchSeparator,
[ValidateSet('SQLServer2000', 'SQLServer2005', 'SQLServer2008/2008R2', 'SQLServer2012', 'SQLServer2014', 'SQLServer2016', 'SQLServer2017')]
[string]$DestinationVersion
)
begin {
if ($Path) {
if ($Path -notlike "*\*") {
$Path = ".\$Path"
}
$directory = Split-Path $Path
$exists = Test-Path $directory
if ($exists -eq $false) {
Write-Message -Level Warning -Message "Parent directory $directory does not exist"
}
}
$outsql = @()
}
process {
if (Test-FunctionInterrupt) {
return
}
$server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $sqlcredential
if ($ExcludeDatabases -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 ($Path) {
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 ($ExcludeJobs -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 ($ExcludeDatabases -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 ($Path) {
$sql | Out-File -Encoding UTF8 -FilePath $Path -Append:$Append -NoClobber:$NoClobber
Get-ChildItem $Path
} else {
$sql
}
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Export-SqlLogin
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaPfDataCollectorSetTemplate
.EXAMPLE
PS C:\> Export-DbaPfDataCollectorSetTemplate -ComputerName sql2017 -Path C:\temp\pf
Exports all data collector sets from to the C:\temp\pf folder.
.EXAMPLE
PS C:\> 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-DbaRepServerSetting {
<#
.SYNOPSIS
Exports replication server settings to file.
.DESCRIPTION
Exports replication server settings to file.
.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 Path
Specifies the path to a file which will contain the output.
.PARAMETER Passthru
Output script to console
.PARAMETER NoClobber
Do not overwrite file
.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 Append
Append to file
.PARAMETER ScriptOption
Not real sure how to use this yet
.PARAMETER InputObject
Allows piping from Get-DbaRepServer
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Replication
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Export-DbaRepServerSetting -SqlInstance sql2017 -Path C:\temp\replication.sql
Exports the replication settings on sql2017 to the file C:\temp\replication.sql
.EXAMPLE
PS C:\> Get-DbaRepServer -SqlInstance sql2017 | Export-DbaRepServerSettings -Path C:\temp\replication.sql
Exports the replication settings on sql2017 to the file C:\temp\replication.sql
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string]$Path,
[object[]]$ScriptOption,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Replication.ReplicationServer[]]$InputObject,
[ValidateSet('ASCII', 'BigEndianUnicode', 'Byte', 'String', 'Unicode', 'UTF7', 'UTF8', 'Unknown')]
[string]$Encoding = 'UTF8',
[switch]$Passthru,
[switch]$NoClobber,
[switch]$Append,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaRepServer -SqlInstance $instance -SqlCredential $sqlcredential
}
foreach ($repserver in $InputObject) {
$server = $repserver.SqlServerName
if (-not (Test-Bound -ParameterName Path)) {
$timenow = (Get-Date -uformat "%m%d%Y%H%M%S")
$mydocs = [Environment]::GetFolderPath('MyDocuments')
$path = "$mydocs\$($server.replace('\', '$'))-$timenow-replication.sql"
}
try {
if (-not $ScriptOption) {
$out = $repserver.Script([Microsoft.SqlServer.Replication.ScriptOptions]::Creation `
-bor [Microsoft.SqlServer.Replication.ScriptOptions]::IncludeAll `
-bor [Microsoft.SqlServer.Replication.ScriptOptions]::EnableReplicationDB `
-bor [Microsoft.SqlServer.Replication.ScriptOptions]::IncludeInstallDistributor
)
} else {
$out = $repserver.Script($scriptOption)
}
} catch {
Stop-Function -ErrorRecord $_ -Message "Replication export failed. Is it setup?" -Continue
}
if ($Passthru) {
"exec sp_dropdistributor @no_checks = 1, @ignore_distributor = 1" | Out-String
$out | Out-String
}
if ($Path) {
"exec sp_dropdistributor @no_checks = 1, @ignore_distributor = 1" | Out-File -FilePath $path -Encoding $encoding -Append
$out | Out-File -FilePath $path -Encoding $encoding -Append
}
}
}
}
function Export-DbaScript {
<#
.SYNOPSIS
Exports scripts from SQL Management Objects (SMO)
.DESCRIPTION
Exports scripts from SQL Management Objects
.PARAMETER InputObject
A SQL Management 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 BatchSeparator
Specifies the Batch Separator to use. Default is None
.PARAMETER NoPrefix
Do not include a Prefix
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaScript
.EXAMPLE
PS C:\> 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
PS C:\> 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
Script does not include Batch Separator and will not compile
.EXAMPLE
PS C:\> Get-DbaDbTable -SqlInstance sql2016 -Database MyDatabase -Table 'dbo.Table1', 'dbo.Table2' -SqlCredential sqladmin | Export-DbaScript -Path C:\temp\export.sql
Exports only script for 'dbo.Table1' and 'dbo.Table2' in MyDatabase to C:temp\export.sql and uses the SQL login "sqladmin" to login to sql2016
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance sql2016 -Job syspolicy_purge_history, 'Hourly Log Backups' -SqlCredential sqladmin | Export-DbaScript -Path C:\temp\export.sql -NoPrefix
Exports only syspolicy_purge_history and 'Hourly Log Backups' to C:temp\export.sql and uses the SQL login "sqladmin" to login to sql2016
Suppress the output of a Prefix
.EXAMPLE
PS C:\> $options = New-DbaScriptingOption
PS C:\> $options.ScriptSchema = $true
PS C:\> $options.IncludeDatabaseContext = $true
PS C:\> $options.IncludeHeaders = $false
PS C:\> $Options.NoCommandTerminator = $false
PS C:\> $Options.ScriptBatchTerminator = $true
PS C:\> $Options.AnsiFile = $true
PS C:\> Get-DbaAgentJob -SqlInstance sql2016 -Job syspolicy_purge_history, 'Hourly Log Backups' -SqlCredential sqladmin | Export-DbaScript -Path C:\temp\export.sql -ScriptingOptionsObject $options
Exports only syspolicy_purge_history and 'Hourly Log Backups' to C:temp\export.sql and uses the SQL login "sqladmin" to login to sql2016
Appends a batch separator at end of each script.
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance sql2014 | Export-DbaScript -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
PS C:\> $options = New-DbaScriptingOption
PS C:\> $options.ScriptSchema = $true
PS C:\> $options.IncludeDatabaseContext = $true
PS C:\> $options.IncludeHeaders = $false
PS C:\> $Options.NoCommandTerminator = $false
PS C:\> $Options.ScriptBatchTerminator = $true
PS C:\> $Options.AnsiFile = $true
PS C:\> $Databases = Get-DbaDatabase -SqlInstance sql2016 -ExcludeDatabase master, model, msdb, tempdb
PS C:\> foreach ($db in $Databases) {
>> Export-DbaScript -InputObject $db -Path C:\temp\export.sql -Append -Encoding UTF8 -ScriptingOptionsObject $options -NoPrefix
>> }
Exports Script for each database on sql2016 excluding system databases
Uses Scripting options to ensure Batch Terminator is set
Will append the output to the file C:\temp\export.sql if it already exists
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[object[]]$InputObject,
[Alias("ScriptingOptionObject")]
[Microsoft.SqlServer.Management.Smo.ScriptingOptions]$ScriptingOptionsObject,
[string]$Path,
[ValidateSet('ASCII', 'BigEndianUnicode', 'Byte', 'String', 'Unicode', 'UTF7', 'UTF8', 'Unknown')]
[string]$Encoding = 'UTF8',
[string]$BatchSeparator = '',
[switch]$NoPrefix,
[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 -and -not (Get-Member -InputObject $object -Name ScriptCreate) ) {
Stop-Function -Message "Failed to find valid SMO server object in input: $object." -Category InvalidData -Target $object -Continue
}
try {
$server = $parent
if (-not $server) {
$server = $object.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"
}
}
if ($NoPrefix) {
$prefix = ""
} else {
$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)) {
if ($BatchSeparator -ne "") {
$script = "$script`r`n$BatchSeparator`r`n"
}
$script | Out-String
}
} else {
if (Get-Member -Name ScriptCreate -InputObject $object) {
$script = $object.ScriptCreate().GetScript()
} else {
$script = $object.Script()
}
if ($BatchSeparator -ne "") {
$script = "$script`r`n$BatchSeparator`r`n"
}
$script | Out-String
}
} else {
if ($ScriptingOptionsObject) {
if ($ScriptingOptionsObject.ScriptBatchTerminator) {
$ScriptingOptionsObject.AppendToFile = $true
$ScriptingOptionsObject.ToFileOnly = $true
$ScriptingOptionsObject.FileName = $actualPath
$object.Script($ScriptingOptionsObject)
} else {
foreach ($script in $scripter.EnumScript($object)) {
if ($BatchSeparator -ne "") {
$script = "$script`r`n$BatchSeparator`r`n"
}
$script | Out-File -FilePath $actualPath -Encoding $encoding -Append
}
}
} else {
if (Get-Member -Name ScriptCreate -InputObject $object) {
$script = $object.ScriptCreate().GetScript()
} else {
$script = $object.Script()
}
if ($BatchSeparator -ne "") {
$script = "$script`r`n$BatchSeparator`r`n"
}
$script | Out-File -FilePath $actualPath -Encoding $encoding -Append
}
}
if (-not $passthru) {
Write-Message -Level Verbose -Message "Exported $object on $($server.Name) to $actualPath"
Get-ChildItem -Path $actualPath
}
}
} catch {
$message = $_.Exception.InnerException.InnerException.InnerException.Message
if (-not $message) {
$message = $_.Exception
}
Stop-Function -Message "Failure on $($server.Name) | $message" -Target $server
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. This can be a collection and receive pipeline input.
You must have sysadmin access if needs to set 'show advanced options' to 1 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.
If not specified will output to My Documents folder with default name of ServerName-MMDDYYYYhhmmss-sp_configure.sql
If a directory is passed then uses default name of ServerName-MMDDYYYYhhmmss-sp_configure.sql
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaSpConfigure
.INPUTS
A DbaInstanceParameter representing an array of SQL Server instances.
.OUTPUTS
Creates a new file for each SQL Server Instance
.EXAMPLE
PS C:\> Export-DbaSpConfigure -SqlInstance sourceserver
Exports the SPConfigure settings on sourceserver. As no Path was defined outputs to My Documents folder with default name format of Servername-MMDDYYYYhhmmss-sp_configure.sql
.EXAMPLE
PS C:\> Export-DbaSpConfigure -SqlInstance sourceserver -Path C:\temp
Exports the SPConfigure settings on sourceserver to the directory C:\temp using the default name format
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Export-DbaSpConfigure -SqlInstance sourceserver -SqlCredential $cred -Path C:\temp\sp_configure.sql
Exports the SPConfigure settings on sourceserver to the file C:\temp\sp_configure.sql. Uses SQL Authentication to connect. Will require SysAdmin rights if needs to set 'show advanced options'
.EXAMPLE
PS C:\> 'Server1', 'Server2' | Export-DbaSpConfigure -Path C:\temp\configure.sql
Exports the SPConfigure settings for Server1 and Server2 using pipeline. As more than 1 Server adds prefix of Servername and date to the file name and saves to file like C:\temp\Servername-MMDDYYYYhhmmss-configure.sql
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string]$Path,
[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
}
if (-not (Test-Bound -ParameterName Path)) {
$timenow = (Get-Date -uformat "%m%d%Y%H%M%S")
$mydocs = [Environment]::GetFolderPath('MyDocuments')
$filepath = "$mydocs\$($server.name.replace('\', '$'))-$timenow-sp_configure.sql"
}
if (Test-Path $Path -PathType Container) {
$timenow = (Get-Date -uformat "%m%d%Y%H%M%S")
$filepath = Join-Path -Path $Path -ChildPath "$($server.name.replace('\', '$'))-$timenow-sp_configure.sql"
} elseif (Test-Path $Path -PathType Leaf) {
if ($SqlInstance.Count -gt 1) {
$timenow = (Get-Date -uformat "%m%d%Y%H%M%S")
$PathData = Get-ChildItem $Path
$filepath = "$($PathData.DirectoryName)\$($server.name.replace('\', '$'))-$timenow-$($PathData.Name)"
} else {
$filepath = $Path
}
}
If (-not $filepath) {
$filepath = $Path
}
$topdir = Split-Path -Path $filepath
if (-not (Test-Path -Path $topdir)) {
New-Item -Path $topdir -ItemType Directory
}
$ShowAdvancedOptions = $server.Configuration.ShowAdvancedOptions.ConfigValue
if ($ShowAdvancedOptions -eq 0) {
try {
$server.Configuration.ShowAdvancedOptions.ConfigValue = $true
$server.Configuration.Alter($true)
} catch {
Stop-Function -Message "Can't set 'show advanced options' to 1 on instance $instance" -ErrorRecord $_ -Continue
}
}
try {
Set-Content -Path $filepath "EXEC sp_configure 'show advanced options' , 1; RECONFIGURE WITH OVERRIDE"
} catch {
Stop-Function -Message "Can't write to $filepath" -ErrorRecord $_ -Continue
}
foreach ($sourceprop in $server.Configuration.Properties) {
$displayname = $sourceprop.DisplayName
$configvalue = $sourceprop.ConfigValue
Add-Content -Path $filepath "EXEC sp_configure '$displayname' , $configvalue;"
}
if ($ShowAdvancedOptions -eq 0) {
Add-Content -Path $filepath "EXEC sp_configure 'show advanced options' , 0;"
Add-Content -Path $filepath "RECONFIGURE WITH OVERRIDE"
$server.Configuration.ShowAdvancedOptions.ConfigValue = $false
$server.Configuration.Alter($true)
}
Get-ChildItem -Path $filepath
}
}
end {
Write-Message -Level Verbose -Message "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 target SQL Server instance or instances. 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 Path
Specifies the full path of a file to write the script 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaUser
.EXAMPLE
PS C:\> Export-DbaUser -SqlInstance sql2005 -Path 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
PS C:\> Export-DbaUser -SqlInstance sqlserver2014a $scred -Path 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
PS C:\> Export-DbaUser -SqlInstance sqlserver2014a -User User1, User2 -Path C:\temp\users.sql
Exports ONLY users User1 and User2 from sqlserver2014a to the file C:\temp\users.sql
.EXAMPLE
PS C:\> Export-DbaUser -SqlInstance sqlserver2008 -User User1 -Path C:\temp\users.sql -DestinationVersion SQLServer2016
Exports user User1 from sqlserver2008 to the file C:\temp\users.sql with syntax to run on SQL Server 2016
.EXAMPLE
PS C:\> Export-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -Path C:\temp\users.sql
Exports ONLY users from db1 and db2 database on sqlserver2008 server, to the C:\temp\users.sql file.
.EXAMPLE
PS C:\> $options = New-DbaScriptingOption
PS C:\> $options.ScriptDrops = $false
PS C:\> $options.WithDependencies = $true
PS C:\> Export-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -Path 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
PS C:\> Export-DbaUser -SqlInstance sqlserver2008 -Database db1,db2 -Path 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, ValueFromPipeline)]
[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", "FilePath", "FileName")]
[string]$Path,
[Alias("NoOverwrite")]
[switch]$NoClobber,
[switch]$Append,
[Alias('Silent')]
[switch]$EnableException,
[Microsoft.SqlServer.Management.Smo.ScriptingOptions]$ScriptingOptionsObject = $null,
[switch]$ExcludeGoBatchSeparator
)
begin {
if ($Path) {
if ($Path -notlike "*\*") { $Path = ".\$Path" }
$directory = Split-Path $Path
$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 {
$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) {
$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) {
$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 don't 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 ($Path) {
$sql | Out-File -Encoding UTF8 -FilePath $Path -Append:$Append -NoClobber:$NoClobber
} else {
$sql
}
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Export-SqlUser
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaXECsv
.EXAMPLE
PS C:\> 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
PS C:\> 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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Export-DbaXESessionTemplate {
<#
.SYNOPSIS
Exports an XESession XML Template using XE Session(s) output by Get-DbaXESession
.DESCRIPTION
Exports an XESession XML Template either from the Target SQL Server or XE Session(s) output by Get-DbaXESession. Exports to "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates" by default
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Export-DbaXESessionTemplate
.EXAMPLE
PS C:\> Export-DbaXESessionTemplate -SqlInstance sql2017 -Path C:\temp\xe
Exports an XESession XML Template for all Extended Event Sessions on sql2017 to the C:\temp\xe folder.
.EXAMPLE
PS C:\> Get-DbaXESession -SqlInstance sql2017 -Session system_health | Export-DbaXESessionTemplate -Path C:\temp\xe
Gets the system_health Extended Events Session from sql2017 and then exports as an XESession XML Template to C:\temp\xe
#>
[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 {
$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 target 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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaAgentJob
.EXAMPLE
PS C:\> Find-DbaAgentJob -SqlInstance Dev01 -JobName *backup*
Returns all agent job(s) that have backup in the name
.EXAMPLE
PS C:\> Find-DbaAgentJob -SqlInstance Dev01, Dev02 -JobName Mybackup
Returns all agent job(s) that are named exactly Mybackup
.EXAMPLE
PS C:\> Find-DbaAgentJob -SqlInstance Dev01 -LastUsed 10
Returns all agent job(s) that have not ran in 10 days
.EXAMPLE
PS C:\> 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
PS C:\> $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
PS C:\> Find-DbaAgentJob -SqlInstance Dev01 -LastUsed 10 -Exclude "Yearly - RollUp Workload", "SMS - Notification"
Returns all agent jobs that have not ran in the last 10 days ignoring jobs "Yearly - RollUp Workload" and "SMS - Notification"
.EXAMPLE
PS C:\> Find-DbaAgentJob -SqlInstance Dev01 -Category "REPL-Distribution", "REPL-Snapshot" | Format-Table -AutoSize -Wrap
Returns all job/s on Dev01 that are in either category "REPL-Distribution" or "REPL-Snapshot"
.EXAMPLE
PS C:\> Find-DbaAgentJob -SqlInstance Dev01, Dev02 -IsFailed -Since '2016-07-01 10:47:00'
Returns all agent job(s) on Dev01 and Dev02 that have failed since July of 2016 (and still have history in msdb)
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -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, ValueFromPipeline)]
[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 {
$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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaBackup
.EXAMPLE
PS C:\> Find-DbaBackup -Path 'C:\MSSQL\SQL Backup\' -BackupFileExtension trn -RetentionPeriod 48h
Searches for all trn files in C:\MSSQL\SQL Backup\ and all subdirectories that are more than 48 hours old will be included.
.EXAMPLE
PS C:\> Find-DbaBackup -Path 'C:\MSSQL\Backup\' -BackupFileExtension bak -RetentionPeriod 7d -CheckArchiveBit
Searches for all 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, HelpMessage = "Full path to the root level backup folder (ex. 'C:\SQL\Backups'")]
[Alias("BackupFolder")]
[string]$Path,
[parameter(Mandatory, HelpMessage = "Backup File extension to remove (ex. bak, trn, dif)")]
[string]$BackupFileExtension ,
[parameter(Mandatory, HelpMessage = "Backup retention period. (ex. 24h, 7d, 4w, 6m)")]
[string]$RetentionPeriod ,
[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) {
#Variable marked as unused by PSScriptAnalyzer
'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,FlowControl,Pipeline,CodeStyle#
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 (@niphold)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaCommand
.EXAMPLE
PS C:\> Find-DbaCommand "snapshot"
For lazy typers: finds all commands searching the entire help for "snapshot"
.EXAMPLE
PS C:\> Find-DbaCommand -Pattern "snapshot"
For rigorous typers: finds all commands searching the entire help for "snapshot"
.EXAMPLE
PS C:\> Find-DbaCommand -Tag copy
Finds all commands tagged with "copy"
.EXAMPLE
PS C:\> Find-DbaCommand -Tag copy,user
Finds all commands tagged with BOTH "copy" and "user"
.EXAMPLE
PS C:\> Find-DbaCommand -Author chrissy
Finds every command whose author contains our beloved "chrissy"
.EXAMPLE
PS C:\> Find-DbaCommand -Author chrissy -Tag copy
Finds every command whose author contains our beloved "chrissy" and it tagged as "copy"
.EXAMPLE
PS C:\> Find-DbaCommand -Pattern snapshot -Rebuild
Finds all commands searching the entire help for "snapshot", rebuilding the index (good for developers)
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[String]$Pattern,
[String[]]$Tag,
[String]$Author,
[String]$MinimumVersion,
[String]$MaximumVersion,
[switch]$Rebuild,
[Alias('Silent')]
[switch]$EnableException
)
begin {
function Get-DbaTrimmedString($Text) {
return $Text.Trim() -replace '(\r\n){2,}', "`n"
}
$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
$alias = Get-Alias -Definition $commandName -ErrorAction SilentlyContinue
$thebase.Alias = $alias.Name -Join ','
## fetch the description
$thebase.Description = $thishelp.Description.Text
## fetch examples
$thebase.Examples = Get-DbaTrimmedString -Text ($thishelp.Examples | Out-String -Width 200)
## fetch help link
$thebase.Links = ($thishelp.relatedLinks).NavigationLink.Uri
## fetch the synopsis
$thebase.Synopsis = $thishelp.Synopsis
## fetch the syntax
$thebase.Syntax = Get-DbaTrimmedString -Text ($thishelp.Syntax | Out-String -Width 600)
## store notes
$as = $thishelp.AlertSet | Out-String -Width 600
## 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()
}
## fetch Parameters
$parameters = $thishelp.parameters.parameter
$command = Get-Command $commandName
$params = @()
foreach ($p in $parameters) {
$paramAlias = $command.parameters[$p.Name].Aliases
$paramDescr = Get-DbaTrimmedString -Text ($p.Description | Out-String -Width 200)
$params += , @($p.Name, $paramDescr, ($paramAlias -Join ','), ($p.Required -eq $true), $p.PipelineInput, $p.DefaultValue)
}
$thebase.Params = $params
[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-DbatoolsConfigValue -Name 'Path.TagCache' -Fallback "$(Resolve-Path $PSScriptRoot\..)\dbatools-index.json"
$dest = "$moduleDirectory\bin\dbatools-index.json"
$helpcoll | ConvertTo-Json -Depth 4 | 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 target SQL Server instance or instances.
.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/
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaDatabase
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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 detailed output
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[ValidateSet('Name', 'ServiceBrokerGuid', 'Owner')]
[string]$Property = 'Name',
[parameter(Mandatory)]
[string]$Pattern,
[switch]$Exact,
[switch]$Detailed,
[Alias('Silent')]
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
}
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 ($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
Size = [dbasize]($db.Size * 1024 * 1024)
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
}
}
}
}
}
#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
The target SQL Server instance or instances. 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 values: 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Query Extracted from SQL Server Management Studio (SSMS) 2016.
.LINK
https://dbatools.io/Find-DbaDatabaseGrowthEvent
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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, ValueFromPipeline)]
[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 ","
$sqlTemplate = "
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) {
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 'db1', 'db2' format
$dbsList = "'$($($dbs | ForEach-Object {$_.Name}) -join "','")'"
Write-Message -Level Verbose -Message "Executing query against $dbsList on $instance"
$sql = $sqlTemplate -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
}
}
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Find-DbaDbUnusedIndex {
<#
.SYNOPSIS
Find unused indexes
.DESCRIPTION
This command will help you to find Unused indexes on a database or a list of databases
For now only supported for CLUSTERED and NONCLUSTERED indexes
.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 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 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: Index
Author: Aaron Nelson (@SQLvariant), SQLvariant.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaDbUnusedIndex
.EXAMPLE
PS C:\> Find-DbaDbUnusedIndex -SqlInstance sql2016 -Database db1, db2
Finds unused databases on db1 and db2 on sql2016
.EXAMPLE
PS C:\> Find-DbaDbUnusedIndex -SqlInstance sql2016 -SqlCredential $cred
Finds unused databases on db1 and db2 on sql2016 using SQL Authentication to connect to the server
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2016 | Find-DbaDbUnusedIndex
Finds unused databases on all databases on sql2016
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$Database,
[object[]]$ExcludeDatabase,
[switch]$IgnoreUptime,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
begin {
# Support Compression 2008+
$sql = "SELECT SERVERPROPERTY('MachineName') AS ComputerName,
ISNULL(SERVERPROPERTY('InstanceName'), 'MSSQLSERVER') AS InstanceName,
SERVERPROPERTY('ServerName') AS SqlInstance, DB_NAME(database_id) AS 'Database'
,s.name AS 'Schema'
,t.name AS 'Table'
,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')"
}
process {
if ($SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase
}
foreach ($db in $InputObject) {
if ($db.Parent.Databases[$db].IsAccessible -eq $false) {
Write-Message -Level Warning -Message "Database [$db] is not accessible."
continue
}
$server = $db.Parent
$instance = $server.Name
if ($server.VersionMajor -lt 9) {
Stop-Function -Message "This function does not support versions lower than SQL Server 2005 (v9)." -Continue
}
$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) {
Write-Message -Level Verbose -Message "The SQL Service was restarted on $lastRestart, which is not long enough for a solid evaluation."
} else {
Stop-Function -Message "The SQL Service on $instance was restarted on $lastRestart, which is not long enough for a solid evaluation." -Continue
}
}
<#
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" -Continue
}
if ($diffDays -le 33) {
Write-Message -Level Verbose -Message "The SQL Service on $instance was restarted on $lastRestart, which may not be long enough for a solid evaluation."
}
try {
$db.Query($sql)
} catch {
Stop-Function -Message "Issue gathering indexes" -Category InvalidOperation -ErrorRecord $_ -Target $db
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-SqlUnusedIndex
}
}
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 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. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbadisabledIndex
.EXAMPLE
PS C:\> Find-DbaDisabledIndex -SqlInstance sql2005
Generates the SQL statements to drop the selected disabled indexes on server "sql2005".
.EXAMPLE
PS C:\> 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
PS C:\> Find-DbaDisabledIndex -SqlInstance sqlserver2016 -Database db1, db2
Generates the SQL Statement to drop selected indexes in databases db1 & db2 on server "sqlserver2016".
.EXAMPLE
PS C:\> Find-DbaDisabledIndex -SqlInstance sqlserver2016
Generates the SQL statements to drop selected indexes on all user databases.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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) {
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
Only supports CLUSTERED and NONCLUSTERED indexes.
Output:
TableName
IndexName
KeyColumns
IncludedColumns
IndexSizeMB
IndexType
CompressionDescription (When 2008+)
[RowCount]
IsDisabled
IsFiltered (When 2008+)
.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. 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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaDuplicateIndex
.EXAMPLE
PS C:\> Find-DbaDuplicateIndex -SqlInstance sql2005
Returns duplicate indexes found on sql2005
.EXAMPLE
PS C:\> Find-DbaDuplicateIndex -SqlInstance sql2017 -SqlCredential sqladmin
Finds exact duplicate indexes on all user databases present on sql2017, using SQL authentication.
.EXAMPLE
PS C:\> Find-DbaDuplicateIndex -SqlInstance sql2017 -Database db1, db2
Finds exact duplicate indexes on the db1 and db2 databases.
.EXAMPLE
PS C:\> Find-DbaDuplicateIndex -SqlInstance sql2017 -IncludeOverlapping
Finds both duplicate and overlapping indexes on all user databases.
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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
,COALESCE(CSPC.IndexSizeMB,0) AS 'IndexSizeMB'
,COALESCE(CSPC.[RowCount],0) AS 'RowCount'
,CI1.IsDisabled
FROM CTE_IndexCols AS CI1
LEFT 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
,COALESCE(CSPC.IndexSizeMB,0) AS 'IndexSizeMB'
,COALESCE(CSPC.[RowCount],0) AS 'RowCount'
,CI1.IsDisabled
FROM CTE_IndexCols AS CI1
LEFT 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
,COALESCE(CSPC.IndexSizeMB,0) AS 'IndexSizeMB'
,COALESCE(CSPC.CompressionDescription, 'NONE') AS 'CompressionDescription'
,COALESCE(CSPC.[RowCount],0) AS 'RowCount'
,CI1.IsDisabled
,CI1.IsFiltered
FROM CTE_IndexCols AS CI1
LEFT 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
,COALESCE(CSPC.IndexSizeMB,0) AS 'IndexSizeMB'
,COALESCE(CSPC.CompressionDescription, 'NONE') AS 'CompressionDescription'
,COALESCE(CSPC.[RowCount],0) AS 'RowCount'
,CI1.IsDisabled
,CI1.IsFiltered
FROM CTE_IndexCols AS CI1
LEFT 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 {
$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 un-problematic 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:
Browser
- Tries discovering all instances via the browser service
- This scan detects instances.
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).
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
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
DNSResolve
- Tries resolving the computername in DNS
Ping
- Tries pinging the computer. Failure will NOT terminate scans.
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).
All
- All of the above
.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 | Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Outside resources used and modified:
https://gallery.technet.microsoft.com/scriptcenter/List-the-IP-addresses-in-a-60c5bb6b
.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.
then scans each of them for instances using the browser service and SqlService
.EXAMPLE
PS C:\> Find-DbaInstance -ComputerName localhost | Get-DbaDatabase | Format-Table -Wrap
Scans localhost for instances using the browser service, traverses all instances for all databases and displays all information in a formatted table.
.EXAMPLE
PS C:\> Find-DbaInstance -ComputerName localhost | Get-DbaDatabase | Select-Object SqlInstance, Name, Status, RecoveryModel, SizeMB, Compatibility, Owner, LastFullBackup, LastDiffBackup, LastLogBackup | Format-Table -Wrap
Scans localhost for instances using the browser service, traverses all instances for all databases and displays a subset of the important information in a formatted table.
Using this method reguarly is not recommended. Use Get-DbaService or Get-DbaCmsRegServer instead.
#>
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "", Justification = "Internal functions are ignored")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[Parameter(Mandatory, ParameterSetName = 'Computer', ValueFromPipeline)]
[DbaInstance[]]$ComputerName,
[Parameter(Mandatory, ParameterSetName = 'Discover')]
[Sqlcollaborative.Dbatools.Discovery.DbaInstanceDiscoveryType]$DiscoveryType,
[System.Management.Automation.PSCredential]$Credential,
[System.Management.Automation.PSCredential]$SqlCredential,
[ValidateSet('Default', 'SQLService', 'Browser', 'TCPPort', 'All', 'SPN', 'Ping', 'SqlConnect', 'DNSResolve')]
[Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType[]]$ScanType = "Default",
[Parameter(ParameterSetName = 'Discover')]
[string[]]$IpAddress,
[string]$DomainController,
[int[]]$TCPPort = 1433,
[Sqlcollaborative.Dbatools.Discovery.DbaInstanceConfidenceLevel]$MinimumConfidence = 'Low',
[switch]$EnableException
)
begin {
#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)][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 = @()
#Variable marked as unused by PSScriptAnalyzer
#$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 {
# here to avoid an empty catch
$null = 1
}
}
if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::Ping) {
$ping = New-Object System.Net.NetworkInformation.Ping
try { $pingReply = $ping.Send($computer.ComputerName) }
catch {
# here to avoid an empty catch
$null = 1
}
}
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 {
# here to avoid an empty catch
$null = 1
}
}
}
# $ports required for all scans
$ports = $TCPPort | Test-TcpPort -ComputerName $computer
if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::Browser) {
try {
$browseResult = Get-SQLInstanceBrowserUDP -ComputerName $computer -EnableException
} catch {
# here to avoid an empty catch
$null = 1
}
}
if ($ScanType -band [Sqlcollaborative.Dbatools.Discovery.DbaInstanceScanType]::SqlService) {
if ($Credential) { $services = Get-DbaService -ComputerName $computer -Credential $Credential -EnableException -ErrorAction Ignore -WarningAction SilentlyCOntinue }
else { $services = Get-DbaService -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 {
# here to avoid an empty catch
$null = 1
}
}
} 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,
[Pscredential]$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, ValueFromPipeline)][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 {
# here to avoid an empty catch
$null = 1
}
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)][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
The target SQL Server instance or instances. 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, Group, Security
Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/ | Simone Bizzotto (@niphlod)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaLoginInGroup
.EXAMPLE
PS C:\> 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
PS C:\> Find-DbaLoginInGroup -SqlInstance DEV01
Returns all active directory users within all windows AD groups that have logins on the instance.
.EXAMPLE
PS C:\> 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()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Internal functions are ignored")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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) {
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) {
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 target SQL Server instance or instances. 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
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
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> Find-DbaOrphanedFile -SqlInstance sql2014 -LocalOnly
Returns only the local file paths for orphaned files.
.EXAMPLE
PS C:\> Find-DbaOrphanedFile -SqlInstance sql2014 -RemoteOnly
Returns only the remote file path for orphaned files.
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[pscredential]$SqlCredential,
[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, 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 {
$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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaSimilarTable
.EXAMPLE
PS C:\> 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
PS C:\> Find-DbaSimilarTable -SqlInstance DEV01 -Database AdventureWorks
Searches AdventureWorks database and lists tables/views and their corresponding matching tables/views with match percent
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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, ValueFromPipeline)]
[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
The target SQL Server instance or instances. 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 text body
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaStoredProcedure
.EXAMPLE
PS C:\> Find-DbaStoredProcedure -SqlInstance DEV01 -Pattern whatever
Searches all user databases stored procedures for "whatever" in the text body
.EXAMPLE
PS C:\> Find-DbaStoredProcedure -SqlInstance sql2016 -Pattern '\w+@\w+\.\w+'
Searches all databases for all stored procedures that contain a valid email pattern in the text body
.EXAMPLE
PS C:\> Find-DbaStoredProcedure -SqlInstance DEV01 -Database MyDB -Pattern 'some string' -Verbose
Searches in "mydb" database stored procedures for "some string" in the text body
.EXAMPLE
PS C:\> Find-DbaStoredProcedure -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects
Searches in "mydb" database stored procedures for "runtime" in the text body
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[parameter(Mandatory)]
[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 {
$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
The target SQL Server instance or instances. 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 text body
.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: Claudio Silva (@ClaudioESSilva)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaTrigger
.EXAMPLE
PS C:\> Find-DbaTrigger -SqlInstance DEV01 -Pattern whatever
Searches all user databases triggers for "whatever" in the text body
.EXAMPLE
PS C:\> Find-DbaTrigger -SqlInstance sql2016 -Pattern '\w+@\w+\.\w+'
Searches all databases for all triggers that contain a valid email pattern in the text body
.EXAMPLE
PS C:\> Find-DbaTrigger -SqlInstance DEV01 -Database MyDB -Pattern 'some string' -Verbose
Searches in "mydb" database triggers for "some string" in the text body
.EXAMPLE
PS C:\> Find-DbaTrigger -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects
Searches in "mydb" database triggers for "runtime" in the text body
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[parameter(Mandatory)]
[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 {
$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"
}
}
#ValidationTags#Messaging#
function Find-DbaUserObject {
<#
.SYNOPSIS
Searches SQL Server to find user-owned objects (i.e. 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
The target SQL Server instance or instances. 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/
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaUserObject
.EXAMPLE
PS C:\> Find-DbaUserObject -SqlInstance DEV01 -Pattern ad\stephen
Searches user objects for owner ad\stephen
.EXAMPLE
PS C:\> Find-DbaUserObject -SqlInstance DEV01 -Verbose
Shows all user owned (non-sa, non-dbo) objects and verbose output
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[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) {
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"
#Variable marked as unused by PSScriptAnalyzer
#$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"
#Variable marked as unused by PSScriptAnalyzer
#$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
The target SQL Server instance or instances. 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 text body
.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: Claudio Silva (@ClaudioESSilva)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Find-DbaView
.EXAMPLE
PS C:\> Find-DbaView -SqlInstance DEV01 -Pattern whatever
Searches all user databases views for "whatever" in the text body
.EXAMPLE
PS C:\> Find-DbaView -SqlInstance sql2016 -Pattern '\w+@\w+\.\w+'
Searches all databases for all views that contain a valid email pattern in the text body
.EXAMPLE
PS C:\> Find-DbaView -SqlInstance DEV01 -Database MyDB -Pattern 'some string' -Verbose
Searches in "mydb" database views for "some string" in the text body
.EXAMPLE
PS C:\> Find-DbaView -SqlInstance sql2016 -Database MyDB -Pattern RUNTIME -IncludeSystemObjects
Searches in "mydb" database views for "runtime" in the text body
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[parameter(Mandatory)]
[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 {
$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 data files, 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 database name 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 PathSep
By default is Windows's style (`\`) but you can pass also, e.g., `/` for Unix's style paths
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Format-DbaBackupInformation
.EXAMPLE
PS C:\> $History | Format-DbaBackupInformation -ReplaceDatabaseName NewDb
Changes as database name 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
PS C:\> $History | Format-DbaBackupInformation -ReplaceDatabaseName @{'OldB'='NewDb';'ProdHr'='DevHr'}
Will change all occurrences of original database name in the backup history (names and restore paths) using the mapping in the hashtable.
In this example any occurrence of OldDb will be replaced with NewDb and ProdHr with DevPR
.EXAMPLE
PS C:\> $History | Format-DbaBackupInformation -DataFileDirectory 'D:\DataFiles\' -LogFileDirectory 'E:\LogFiles\
This example with change the restore path for all data files (everything that is not a log file) to d:\datafiles
And all Transaction Log files will be restored to E:\Logfiles
.EXAMPLE
PS C:\> $History | Format-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, ValueFromPipeline)]
[object[]]$BackupHistory,
[object]$ReplaceDatabaseName,
[switch]$ReplaceDbNameInFile,
[string]$DataFileDirectory,
[string]$LogFileDirectory,
[string]$DestinationFileStreamDirectory,
[string]$DatabaseNamePrefix,
[string]$DatabaseFilePrefix,
[string]$DatabaseFileSuffix,
[string]$RebaseBackupFolder,
[switch]$Continue,
[hashtable]$FileMapping,
[string]$PathSep = '\',
[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.EndsWith($PathSep)) {
$DataFileDirectory = $DataFileDirectory -Replace '.$'
}
if ((Test-Bound -Parameter DestinationFileStreamDirectory) -and $DestinationFileStreamDirectory.EndsWith($PathSep) ) {
$DestinationFileStreamDirectory = $DestinationFileStreamDirectory -Replace '.$'
}
if ((Test-Bound -Parameter LogFileDirectory) -and $LogFileDirectory.EndsWith($PathSep) ) {
$LogFileDirectory = $LogFileDirectory -Replace '.$'
}
if ((Test-Bound -Parameter RebaseBackupFolder) -and $RebaseBackupFolder.EndsWith($PathSep) ) {
$RebaseBackupFolder = $RebaseBackupFolder -Replace '.$'
}
}
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
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]
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 + $PathSep + $DatabaseFilePrefix + $Pname.BaseName + $DatabaseFileSuffix + $Pname.extension
Write-Message -Message "PhysicalName = $($_.PhysicalName) " -Level Verbose
}
}
}
if ('' -ne $RebaseBackupFolder -and $History.FullName[0] -notmatch 'http') {
Write-Message -Message 'Rebasing backup files' -Level Verbose
for ($j = 0; $j -lt $History.fullname.count; $j++) {
$file = [System.IO.FileInfo]($History.fullname[$j])
$History.fullname[$j] = $RebaseBackupFolder + $PathSep + $file.BaseName + $file.Extension
}
}
$History
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaAgDatabase {
<#
.SYNOPSIS
Gets availability group databases from a SQL Server instance.
.DESCRIPTION
Gets availability group databases from a SQL Server instance.
Default view provides most common set of properties for information on the database in an availability group.
Information returned on the database will be specific to that replica, whether it is primary or a secondary.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER AvailabilityGroup
Specify the availability groups to query.
.PARAMETER Database
Specify the database or databases to return. 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 InputObject
Enables piped input from Get-DbaAvailabilityGroup.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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, HA, AG, AvailabilityGroup, Replica
Author: Shawn Melton (@wsmelton), https://wsmelton.github.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgDatabase
.EXAMPLE
PS C:\> Get-DbaAgDatabase -SqlInstance sql2017a
Returns all the databases in each availability group found on sql2017a
.EXAMPLE
PS C:\> Get-DbaAgDatabase -SqlInstance sql2017a -AvailabilityGroup AG101
Returns all the databases in the availability group AG101 on sql2017a
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sqlcluster -AvailabilityGroup SharePoint -Database Sharepoint_Config | Get-DbaAgDatabase
Returns the database Sharepoint_Config found in the availability group SharePoint on server sqlcluster
#>
[CmdletBinding()]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[string[]]$Database,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup[]]$InputObject,
[switch]$EnableException
)
process {
if ($SqlInstance) {
$InputObject += Get-DbaAvailabilityGroup -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup
}
foreach ($db in $InputObject.AvailabilityDatabases) {
if ($Database) {
if ($db.Name -notin $Database) { continue }
}
$server = $db.Parent.Parent
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 Replica -value $server.ComputerName
Add-Member -Force -InputObject $db -MemberType NoteProperty -Name DatabaseName -value $db.Name # for backwards compat
Add-Member -Force -InputObject $db -MemberType NoteProperty -Name AvailabilityGroup -value $db.Parent.Name
$defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'AvailabilityGroup', 'Replica', 'Name', 'SynchronizationState', 'IsFailoverReady', 'IsJoined', 'IsSuspended'
Select-DefaultView -InputObject $db -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
The target SQL Server instance or instances. 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: Agent, SMO
Author: Klaas Vandenberghe (@PowerDBAKlaas)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgentAlert
.EXAMPLE
PS C:\> Get-DbaAgentAlert -SqlInstance ServerA,ServerB\instanceB
Returns all SQL Agent alerts on serverA and serverB\instanceB
.EXAMPLE
PS C:\> 'serverA','serverB\instanceB' | Get-DbaAgentAlert
Returns all SQL Agent alerts on serverA and serverB\instanceB
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "Instance", "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
}
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
The target SQL Server instance or instances. 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 ExcludeDisabledJobs
Switch will exclude disabled jobs from the output.
.PARAMETER Database
Return jobs with T-SQL job steps associated with specific databases
.PARAMETER Category
Return jobs associated with specific category
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgentJob
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance localhost
Returns all SQL Agent Jobs on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance localhost, sql2016
Returns all SQl Agent Jobs for the local and sql2016 SQL Server instances
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance localhost -Job BackupData, BackupDiff
Returns all SQL Agent Jobs named BackupData and BackupDiff from the local SQL Server instance.
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance localhost -ExcludeJob BackupDiff
Returns all SQl Agent Jobs for the local SQL Server instances, except the BackupDiff Job.
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance localhost -ExcludeDisabledJobs
Returns all SQl Agent Jobs for the local SQL Server instances, excluding the disabled jobs.
.EXAMPLE
PS C:\> $servers | Get-DbaAgentJob | Out-GridView -PassThru | Start-DbaAgentJob -WhatIf
Find all of your Jobs from SQL Server instances in the $servers collection, select the jobs you want to start then see jobs would start if you ran Start-DbaAgentJob
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Job,
[string[]]$ExcludeJob,
[string[]]$Database,
[string[]]$Category,
[switch]$ExcludeDisabledJobs,
[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
}
$jobs = $server.JobServer.Jobs
if ($Job) {
$jobs = $jobs | Where-Object Name -In $Job
}
if ($ExcludeJob) {
$jobs = $jobs | Where-Object Name -NotIn $ExcludeJob
}
if ($ExcludeDisabledJobs) {
$jobs = $Jobs | Where-Object IsEnabled -eq $true
}
if ($Database) {
$jobs = $jobs | Where-Object {
$_.JobSteps.DatabaseName -in $Database
}
}
if ($Category) {
$jobs = $jobs | Where-Object Category -in $Category
}
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, 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
The target SQL Server instance or instances. 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 categories will be returned.
.PARAMETER CategoryType
The type of category. This can be "LocalJob", "MultiServerJob" or "None".
If no category is used all categories 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgentJobCategory
.EXAMPLE
PS C:\> Get-DbaAgentJobCategory -SqlInstance sql1
Return all the job categories.
.EXAMPLE
PS C:\> Get-DbaAgentJobCategory -SqlInstance sql1 -Category 'Log Shipping'
Return all the job categories that have the name 'Log Shipping'.
.EXAMPLE
PS C:\> Get-DbaAgentJobCategory -SqlInstance sstad-pc -CategoryType MultiServerJob
Return all the job categories that have a type MultiServerJob.
#>
[CmdletBinding(DefaultParameterSetName = "Default")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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) {
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
The target SQL Server instance or instances. 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 ExcludeJobSteps
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) | Simone Bizzotto (@niphold)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgentJobHistory
.EXAMPLE
PS C:\> Get-DbaAgentJobHistory -SqlInstance localhost
Returns all SQL Agent Job execution results on the local default SQL Server instance.
.EXAMPLE
PS C:\> Get-DbaAgentJobHistory -SqlInstance localhost, sql2016
Returns all SQL Agent Job execution results for the local and sql2016 SQL Server instances.
.EXAMPLE
PS C:\> 'sql1','sql2\Inst2K17' | Get-DbaAgentJobHistory
Returns all SQL Agent Job execution results for sql1 and sql2\Inst2K17.
.EXAMPLE
PS C:\> Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 | Select-Object *
Returns all properties for all SQl Agent Job execution results on sql2\Inst2K17.
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaAgentJobHistory -SqlInstance sql2\Inst2K17 -ExcludeJobSteps
Returns the SQL Agent Job execution results for the whole jobs on sql2\Inst2K17, leaving out job step execution results.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaAgentJob -SqlInstance sql2016 | Where-Object 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]$ExcludeJobSteps,
[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 ($ExcludeJobSteps -and $WithOutputFile) {
Stop-Function -Message "You can't use -ExcludeJobSteps 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-Object -Process { $_.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 ($ExcludeJobSteps) {
$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
# Add this in for easier ConvertTo-DbaTimeline Support
Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name TypeName -value AgentJobHistory
Select-DefaultView -InputObject $execution -Property ComputerName, InstanceName, SqlInstance, 'JobName as Job', StepName, RunDate, StartDate, EndDate, Duration, Status, OperatorEmailed, Message, OutputFileName, RemoteOutputFileName -TypeName AgentJobHistory
} else {
Add-Member -Force -InputObject $execution -MemberType NoteProperty -Name TypeName -value AgentJobHistory
Select-DefaultView -InputObject $execution -Property ComputerName, InstanceName, SqlInstance, 'JobName as Job', StepName, RunDate, StartDate, EndDate, Duration, Status, OperatorEmailed, Message -TypeName AgentJobHistory
}
}
} 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) {
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
The target SQL Server instance or instances. 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 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(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) | Simone Bizzotto (@niphold)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> (Get-DbaAgentJobOutputFile -SqlInstance SERVERNAME | Out-GridView -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
PS C:\> 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, HelpMessage = 'The SQL Server Instance',
ValueFromPipeline,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false,
Position = 0)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Parameter(HelpMessage = 'SQL Credential',
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false,
Position = 1)]
[PSCredential]$SqlCredential,
[object[]]$Job,
[object[]]$ExcludeJob,
[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
}
$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
The target SQL Server instance or instances. 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 ExcludeDisabledJobs
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgentJobStep
.EXAMPLE
PS C:\> Get-DbaAgentJobStep -SqlInstance localhost
Returns all SQL Agent Job Steps on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaAgentJobStep -SqlInstance localhost, sql2016
Returns all SQL Agent Job Steps for the local and sql2016 SQL Server instances
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaAgentJobStep -SqlInstance localhost -ExcludeJob BackupDiff
Returns all SQL Agent Job Steps for the local SQL Server instances, except for the BackupDiff Job.
.EXAMPLE
PS C:\> Get-DbaAgentJobStep -SqlInstance localhost -ExcludeDisabledJobs
Returns all SQL Agent Job Steps for the local SQL Server instances, excluding the disabled jobs.
.EXAMPLE
PS C:\> $servers | Get-DbaAgentJobStep
Find all of your Job Steps from SQL Server instances in the $servers collection
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]
$SqlCredential,
[object[]]$Job,
[object[]]$ExcludeJob,
[switch]$ExcludeDisabledJobs,
[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
}
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 ($ExcludeDisabledJobs) {
$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
The target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgentLog
.EXAMPLE
PS C:\> Get-DbaAgentLog -SqlInstance sql01\sharepoint
Returns the entire error log for the SQL Agent on sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaAgentLog -SqlInstance sql01\sharepoint -LogNumber 3, 6
Returns log numbers 3 and 6 for the SQL Agent on sql01\sharepoint
.EXAMPLE
PS C:\> $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
PS C:\> $servers | Get-DbaAgentLog -LogNumber 0
Returns the most recent SQL Agent error logs for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]
$SqlCredential,
[ValidateRange(0, 9)]
[int[]]$LogNumber,
[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 ($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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgentOperator
.EXAMPLE
PS C:\> Get-DbaAgentOperator -SqlInstance ServerA,ServerB\instanceB
Returns any SQL Agent operators on serverA and serverB\instanceB
.EXAMPLE
PS C:\> 'ServerA','ServerB\instanceB' | Get-DbaAgentOperator
Returns all SQL Agent operators on serverA and serverB\instanceB
.EXAMPLE
PS C:\> Get-DbaAgentOperator -SqlInstance ServerA -Operator Dba1,Dba2
Returns only the SQL Agent Operators Dba1 and Dba2 on ServerA.
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]
$SqlCredential,
[object[]]$Operator,
[object[]]$ExcludeOperator,
[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
}
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
}
$alerts = $server.JobServer.alerts
foreach ($operat in $operators) {
$jobs = $server.JobServer.jobs | Where-Object { $_.OperatorToEmail, $_.OperatorToNetSend, $_.OperatorToPage -contains $operat.Name }
$lastemail = [dbadatetime]$operat.LastEmailDate
$operatAlerts = @()
foreach ($alert in $alerts) {
$dtAlert = $alert.EnumNotifications($operat.Name)
if ($dtAlert.Rows.Count -gt 0) {
$operatAlerts += $alert.Name
$alertlastemail = [dbadatetime]$alert.LastOccurrenceDate
}
}
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
Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name RelatedAlerts -Value $operatAlerts
Add-Member -Force -InputObject $operat -MemberType NoteProperty -Name AlertLastEmail -Value $alertlastemail
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
The target SQL Server instance or instances. 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 Proxy
The proxy to process - this list is auto-populated from the server. If unspecified, all proxies 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: Agent, SMO
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgentProxy
.EXAMPLE
PS C:\> Get-DbaAgentProxy -SqlInstance ServerA,ServerB\instanceB
Returns all SQL Agent proxies on serverA and serverB\instanceB
.EXAMPLE
PS C:\> 'serverA','serverB\instanceB' | Get-DbaAgentProxy
Returns all SQL Agent proxies on serverA and serverB\instanceB
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "Instance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Proxy,
[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
}
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
if ($proxy) {
$proxies = $proxies | Where-Object Name -In $proxy
}
foreach ($px in $proxies) {
Add-Member -Force -InputObject $px -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
Add-Member -Force -InputObject $px -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
Add-Member -Force -InputObject $px -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
Select-DefaultView -InputObject $px -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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgentSchedule
.EXAMPLE
PS C:\> Get-DbaAgentSchedule -SqlInstance localhost
Returns all SQL Agent Shared Schedules on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaAgentSchedule -SqlInstance localhost, sql2016
Returns all SQL Agent Shared Schedules for the local and sql2016 SQL Server instances
.EXAMPLE
PS C:\> Get-DbaAgentSchedule -SqlInstance sql2016 -Schedule "Maintenance10min","Maintenance60min"
Returns the "Maintenance10min" & "Maintenance60min" schedules from the sql2016 SQL Server instance
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "Instance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Schedules")]
[object[]]$Schedule,
[PSCredential]$SqlCredential,
[Alias('Silent')]
[switch]$EnableException
)
begin {
function Get-ScheduleDescription {
param (
[Parameter(Mandatory)]
[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) {
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-DbaAgentServer {
<#
.SYNOPSIS
Gets SQL Agent Server information for each instance(s) of SQL Server.
.DESCRIPTION
The Get-DbaAgentServer returns connected SMO object for SQL Agent Server information for each instance(s) of SQL Server.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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: Job, Agent
Author: Cláudio Silva (@claudioessilva), https://claudioessilva.eu
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgentServer
.EXAMPLE
PS C:\> Get-DbaAgentServer -SqlInstance localhost
Returns SQL Agent Server on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaAgentServer -SqlInstance localhost, sql2016
Returns SQL Agent Servers for the localhost and sql2016 SQL Server instances
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[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
}
$jobServer = $server.JobServer
$defaultView = 'ComputerName', 'InstanceName', 'SqlInstance', 'AgentDomainGroup', 'AgentLogLevel', 'AgentMailType', 'AgentShutdownWaitTime', 'ErrorLogFile', 'IdleCpuDuration', 'IdleCpuPercentage', 'IsCpuPollingEnabled', 'JobServerType', 'LoginTimeout', 'JobHistoryIsEnabled', 'MaximumHistoryRows', 'MaximumJobHistoryRows', 'MsxAccountCredentialName', 'MsxAccountName', 'MsxServerName', 'Name', 'NetSendRecipient', 'ServiceAccount', 'ServiceStartMode', 'SqlAgentAutoStart', 'SqlAgentMailProfile', 'SqlAgentRestart', 'SqlServerRestart', 'State', 'SysAdminOnly'
Add-Member -Force -InputObject $jobServer -MemberType NoteProperty -Name ComputerName -Value $jobServer.Parent.ComputerName
Add-Member -Force -InputObject $jobServer -MemberType NoteProperty -Name InstanceName -value $jobServer.Parent.ServiceName
Add-Member -Force -InputObject $jobServer -MemberType NoteProperty -Name SqlInstance -Value $jobServer.Parent.DomainInstanceName
Add-Member -Force -InputObject $jobServer -MemberType ScriptProperty -Name JobHistoryIsEnabled -Value { switch ( $jobServer.MaximumHistoryRows ) { -1 { $false } default { $true } } }
Select-DefaultView -InputObject $jobServer -Property $defaultView
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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, HA, AG, AvailabilityGroup
Author: Shawn Melton (@wsmelton), http://wsmelton.github.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgHadr
.EXAMPLE
PS C:\> Get-DbaAgHadr -SqlInstance sql2016
Returns a status of the Hadr setting for sql2016 SQL Server instance.
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[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
}
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'
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaAgListener {
<#
.SYNOPSIS
Returns availability group listeners.
.DESCRIPTION
Returns availability group listeners.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER AvailabilityGroup
Specify the availability groups to query.
.PARAMETER Listener
Return only specific listeners.
.PARAMETER InputObject
Enables piped input from Get-DbaAvailabilityGroup.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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, HA, AvailabilityGroup, Listener
Author: Viorel Ciucu (@viorelciucu)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgListener
.EXAMPLE
PS C:\> Get-DbaAgListener -SqlInstance sql2017a
Returns all listeners found on sql2017a
.EXAMPLE
PS C:\> Get-DbaAgListener -SqlInstance sql2017a -AvailabilityGroup AG-a
Returns all listeners found on sql2017a on sql2017a for the availability group AG-a
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql2017a -AvailabilityGroup OPP | Get-DbaAgListener
Returns all listeners found on sql2017a on sql2017a for the availability group OPP
#>
[CmdletBinding()]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[string[]]$Listener,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup[]]$InputObject,
[switch]$EnableException
)
process {
if ($SqlInstance) {
$InputObject += Get-DbaAvailabilityGroup -SqlInstance $SqlInstance -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
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaAgReplica {
<#
.SYNOPSIS
Returns the availability group replica object found on the server.
.DESCRIPTION
Returns the availability group replica object found on the server.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER AvailabilityGroup
Specify the availability groups to query.
.PARAMETER Replica
Return only specific replicas.
.PARAMETER InputObject
Enables piped input from Get-DbaAvailabilityGroup.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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, HA, AvailabilityGroup, Replica
Author: Shawn Melton (@wsmelton) | Chrissy LeMaire (@cl)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAgReplica
.EXAMPLE
PS C:\> Get-DbaAgReplica -SqlInstance sql2017a
Returns basic information on all the availability group replicas found on sql2017a
.EXAMPLE
PS C:\> Get-DbaAgReplica -SqlInstance sql2017a -AvailabilityGroup SharePoint
Shows basic information on the replicas found on availability group SharePoint on sql2017a
.EXAMPLE
PS C:\> Get-DbaAgReplica -SqlInstance sql2017a | Select-Object *
Returns full object properties on all availability group replicas found on sql2017a
#>
[CmdletBinding()]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[string[]]$Replica,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup[]]$InputObject,
[switch]$EnableException
)
process {
if ($SqlInstance) {
$InputObject += Get-DbaAvailabilityGroup -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup
}
$availabilityReplicas = $InputObject.AvailabilityReplicas
if ($Replica) {
$availabilityReplicas = $InputObject.AvailabilityReplicas | Where-Object { $_.Name -in $Replica }
}
$defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'AvailabilityGroup', 'Name', 'Role', 'ConnectionState', 'RollupSynchronizationState', 'AvailabilityMode', 'BackupPriority', 'EndpointUrl', 'SessionTimeout', 'FailoverMode', 'ReadonlyRoutingList'
foreach ($agreplica in $availabilityReplicas) {
Add-Member -Force -InputObject $agreplica -MemberType NoteProperty -Name ComputerName -value $agreplica.Parent.ComputerName
Add-Member -Force -InputObject $agreplica -MemberType NoteProperty -Name InstanceName -value $agreplica.Parent.InstanceName
Add-Member -Force -InputObject $agreplica -MemberType NoteProperty -Name SqlInstance -value $agreplica.Parent.SqlInstance
Add-Member -Force -InputObject $agreplica -MemberType NoteProperty -Name AvailabilityGroup -value $agreplica.Parent.Name
Add-Member -Force -InputObject $agreplica -MemberType NoteProperty -Name Replica -value $agreplica.Name # backwards compat
Select-DefaultView -InputObject $agreplica -Property $defaults
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaAvailabilityGroup {
<#
.SYNOPSIS
Returns availability group objects from a SQL Server instance.
.DESCRIPTION
Returns availability group objects from a SQL Server instance.
Default view provides most common set of properties for information on the Availability Group(s).
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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
Return only specific availability groups.
.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, HA, AG, AvailabilityGroup
Author: Shawn Melton (@wsmelton) | Chrissy LeMaire (@cl)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAvailabilityGroup
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a
Returns basic information on all the Availability Group(s) found on sqlserver2014a.
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a -AvailabilityGroup AG-a
Shows basic information on the Availability Group AG-a on sqlserver2014a.
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a | Select *
Returns full object properties on all Availability Group(s) on sqlserver2014a.
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sqlserver2014a | Select-Object -ExpandProperty PrimaryReplicaServerName
Returns the SQL Server instancename of the primary replica as a string
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[switch]$IsPrimary,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
} catch {
Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if (-not $server.IsHadrEnabled) {
Stop-Function -Message "Availability Group (HADR) is not configured for the instance: $instance." -Target $instance -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'
Add-Member -Force -InputObject $ag -MemberType NoteProperty -Name IsPrimary -Value ($ag.PrimaryReplicaServerName -eq $server.Name)
Select-DefaultView -InputObject $ag -Property $defaults
} else {
$defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'LocalReplicaRole', 'Name as AvailabilityGroup', 'PrimaryReplicaServerName as PrimaryReplica', 'ClusterType', 'DtcSupportEnabled', '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
TThe target 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaAvailableCollation
.EXAMPLE
PS C:\> Get-DbaAvailableCollation -SqlInstance sql2016
Gets all the collations from server sql2016 using NT authentication
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[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 {
$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
The target SQL Server instance or instances. 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaBackupDevice
.EXAMPLE
PS C:\> Get-DbaBackupDevice -SqlInstance localhost
Returns all Backup Devices on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaBackupDevice -SqlInstance localhost, sql2016
Returns all Backup Devices for the local and sql2016 SQL Server instances
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[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
}
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
The target SQL Server instance or instances. 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 RecoveryFork
Specifies the Recovery Fork you want backup history for
.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
Author: Chrissy LeMaire (@cl) | Stuart Moore (@napalmgram)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaBackupHistory
.EXAMPLE
PS C:\> Get-DbaBackupHistory -SqlInstance SqlInstance2014a
Returns server name, database, username, backup type, date for all database backups still in msdb history on SqlInstance2014a. This may return many rows; consider using filters that are included in other examples.
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
Get-DbaBackupHistory -SqlInstance SqlInstance2014a -SqlCredential $cred
Does the same as above but connect to SqlInstance2014a as SQL user "sqladmin"
.EXAMPLE
PS C:\> Get-DbaBackupHistory -SqlInstance SqlInstance2014a -Database db1, db2 -Since '2016-07-01 10:47:00'
Returns backup information only for databases db1 and db2 on SqlInstance2014a since July 1, 2016 at 10:47 AM.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Last
Returns information about the most recent full, differential and log backups for AdventureWorks2014 on sql2014.
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -LastFull
Returns information about the most recent full backup for AdventureWorks2014 on sql2014.
.EXAMPLE
PS C:\> Get-DbaBackupHistory -SqlInstance sql2014 -Database AdventureWorks2014 -Type Full
Returns information about all Full backups for AdventureWorks2014 on sql2014.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sql2016 | Get-DbaBackupHistory
Returns database backup information for every database on every server listed in the Central Management Server on sql2016.
.EXAMPLE
PS C:\> Get-DbaBackupHistory -SqlInstance SqlInstance2014a, sql2016 -Force
Returns detailed backup history for all databases on SqlInstance2014a and sql2016.
.EXAMPLE
PS C:\> Get-DbaBackupHistory -SqlInstance sql2016 -Database db1 -RecoveryFork 38e5e84a-3557-4643-a5d5-eed607bef9c6 -Last
If db1 has multiple recovery forks, specifying the RecoveryFork GUID will restrict the search to that fork.
#>
[CmdletBinding(DefaultParameterSetName = "Default")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]
$SqlInstance,
[Alias("Credential")]
[PsCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[switch]$IncludeCopyOnly,
[Parameter(ParameterSetName = "NoLast")]
[switch]$Force,
[DateTime]$Since = (Get-Date '01/01/1970'),
[ValidateScript( {($_ -match '^(\{){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}$') -or ('' -eq $_)})]
[string]$RecoveryFork,
[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 {
$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) {
if ($since) {
$sinceSqlFilter = "AND backupset.backup_finish_date >= '$($Since.ToString("yyyy-MM-ddTHH:mm:ss"))'"
}
if ($RecoveryFork) {
$recoveryForkSqlFilter = "AND backupset.last_recovery_fork_guid ='$RecoveryFork'"
}
if ($null -eq (Get-PsCallStack)[1].Command) {
$forkCheckSql = "
SELECT
database_name,
MIN(database_backup_lsn) as 'FirstLsn',
MAX(database_backup_lsn) as 'FinalLsn',
MIN(backup_start_date) as 'MinDate',
MAX(backup_finish_date) as 'MaxDate',
last_recovery_fork_guid 'RecFork',
count(1) as 'backupcount'
FROM msdb.dbo.backupset
WHERE database_name='$($db.name)'
$sinceSqlFilter
$recoveryForkSqlFilter
GROUP by database_name, last_recovery_fork_guid
"
$results = $server.ConnectionContext.ExecuteWithResults($forkCheckSql).Tables.Rows
if ($results.count -gt 1) {
Write-Message -Message "Found backups from multiple recovery forks for $($db.name) on $($server.name), this may affect your results" -Level Warning
foreach ($result in $results) {
Write-Message -Message "Between $($result.MinDate)/$($result.FirstLsn) and $($result.MaxDate)/$($result.FinalLsn) $($result.name) was on Recovery Fork GUID $($result.RecFork) ($($result.backupcount) backups)" -Level Warning
}
}
}
#Get the full and build upwards
$allBackups = @()
$allBackups += $fullDb = Get-DbaBackupHistory -SqlInstance $server -Database $db.Name -LastFull -raw:$Raw -DeviceType $DeviceType -IncludeCopyOnly:$IncludeCopyOnly -Since:$since -RecoveryFork $RecoveryFork
$diffDb = Get-DbaBackupHistory -SqlInstance $server -Database $db.Name -LastDiff -raw:$Raw -DeviceType $DeviceType -IncludeCopyOnly:$IncludeCopyOnly -Since:$since -RecoveryFork $RecoveryFork
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 -Since:$since -RecoveryFork $RecoveryFork | 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
if ($since) {
$sinceSqlFilter = "AND backupset.backup_finish_date >= '$($Since.ToString("yyyy-MM-ddTHH:mm:ss"))'"
}
if ($RecoveryFork) {
$recoveryForkSqlFilter = "AND backupset.last_recovery_fork_guid ='$RecoveryFork'"
}
if ((Get-PsCallStack)[1].Command -notlike 'Get-DbaBackupHistory*') {
$forkCheckSql = "
SELECT
database_name,
MIN(database_backup_lsn) as 'FirstLsn',
MAX(database_backup_lsn) as 'FinalLsn',
MIN(backup_start_date) as 'MinDate',
MAX(backup_finish_date) as 'MaxDate',
last_recovery_fork_guid 'RecFork',
count(1) as 'backupcount'
FROM msdb.dbo.backupset
WHERE database_name='$($db.name)'
$sinceSqlFilter
$recoveryForkSqlFilter
GROUP by database_name, last_recovery_fork_guid
"
$results = $server.ConnectionContext.ExecuteWithResults($forkCheckSql).Tables.Rows
if ($results.count -gt 1) {
Write-Message -Message "Found backups from multiple recovery forks for $($db.name) on $($server.name), this may affect your results" -Level Warning
foreach ($result in $results) {
Write-Message -Message "Between $($result.MinDate)/$($result.FirstLsn) and $($result.MaxDate)/$($result.FinalLsn) $($result.name) was on Recovery Fork GUID $($result.RecFork) ($($result.backupcount) backups)" -Level Warning
}
}
}
$whereCopyOnly = $null
if ($true -ne $IncludeCopyOnly) {
$whereCopyOnly = " AND is_copy_only='0' "
}
if ($deviceTypeFilter) {
$devTypeFilterWhere = "AND mediafamily.device_type $deviceTypeFilterRight"
}
if ($since) {
$sinceSqlFilter = "AND backupset.backup_finish_date >= '$($Since.ToString("yyyy-MM-ddTHH:mm:ss"))'"
}
# 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,
a.recovery_model
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
$sinceSqlFilter
$recoveryForkSqlFilter
) 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, Paths, and Logical and Physical Names 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.
.NOTES
Tags: DisasterRecovery, Backup, Restore
Author: Chrissy LeMaire (@cl) | Stuart Moore (@napalmgram)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaBackupInformation
.EXAMPLE
PS C:\> 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
.EXAMPLE
PS C:\> Get-DbaBackupInformation -SqlInstance Server1 -Path c:\backups\ -DirectoryRecurse -ExportPath c:\store\BackupHistory.xml
PS C:\> robocopy c:\store\ \\remoteMachine\C$\store\ BackupHistory.xml
PS C:\> Get-DbaBackupInformation -Import -Path c:\store\BackupHistory.xml | Restore-DbaDatabase -SqlInstance Server2 -TrustDbBackupHistory
This example creates backup history output from server1 and copies the file to the remote machine in order to preserve backup history. It is then used to restore the databases onto server2.
.EXAMPLE
PS C:\> 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
PS C:\> Get-ChildItem c:\backups\ -recurse -files | Where-Object {$_.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
PS C:\> $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\network\backups
PS C:\> $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
PS C:\> $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\network\backups -MaintenanceSolution
When MaintenanceSolution is indicated we know we are dealing with the output from Ola Hallengren 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
PS C:\> $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 they WON'T be restored
#>
[CmdletBinding( DefaultParameterSetName = "Create")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "", Justification = "For Parameter AzureCredential")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[object[]]$Path,
[parameter(Mandatory, 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-DbaPath -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) {
$dblsn = $group.Group[0].DatabaseBackupLSN
if (-not $dblsn) {
$dblsn = 0
}
$description = $group.Group[0].BackupTypeDescription
if (-not $description) {
$header = Read-DbaBackupHeader -SqlInstance $server -Path $Path | Select-Object -First 1
$description = switch ($header.BackupType) {
1 { "Full" }
2 { "Differential" }
3 { "Log"}
}
}
$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.Byte | Measure-Object -Sum).Sum
$HistoryObject.CompressedBackupSize = ($Group.Group.CompressedBackupSize.Byte | Measure-Object -Sum).Sum
$historyObject.Type = $description
$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 = $dblsn
$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
$group.FileList = ($group.FileList | Select-Object Type,
@{Name = "LogicalName"; Expression = {Get-HashString -InString $_."LogicalName"}},
@{Name = "PhysicalName"; Expression = {Get-HashString -InString $_."PhysicalName"}})
}
}
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
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaBuildReference {
<#
.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 Life Cycle Policy
.PARAMETER Build
Instead of connecting to a real instance, pass a string identifying the build to get the info back.
.PARAMETER Kb
Get a KB information based on its number. Supported format: KBXXXXXX, or simply XXXXXX.
.PARAMETER MajorVersion
Get a KB information based on SQL Server version. Can be refined further by -ServicePack and -CumulativeUpdate parameters.
Examples: SQL2008 | 2008R2 | 2016
.PARAMETER ServicePack
Get a KB information based on SQL Server Service Pack version. Can be refined further by -CumulativeUpdate parameter.
Examples: SP0 | 2 | RTM
.PARAMETER CumulativeUpdate
Get a KB information based on SQL Server Cumulative Update version.
Examples: CU0 | CU13 | CU0
.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.
.NOTES
Tags: SqlBuild
Author: Simone Bizzotto (@niphold) | Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaBuildReference
.EXAMPLE
PS C:\> Get-DbaBuildReference -Build "12.00.4502"
Returns information about a build identified by "12.00.4502" (which is SQL 2014 with SP1 and CU11)
.EXAMPLE
PS C:\> Get-DbaBuildReference -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
PS C:\> Get-DbaBuildReference -Build "12.0.4502","10.50.4260"
Returns information builds identified by these versions strings
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlserver2014a | Get-DbaBuildReference
Integrate with other cmdlets to have builds checked for all your registered servers on sqlserver2014a
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
[CmdletBinding(DefaultParameterSetName = 'Build')]
param (
[version[]]
$Build,
[string[]]
$Kb,
[ValidateNotNullOrEmpty()]
[string]
$MajorVersion,
[ValidateNotNullOrEmpty()]
[string]
[Alias('SP')]
$ServicePack = 'RTM',
[string]
[Alias('CU')]
$CumulativeUpdate,
[Parameter(ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]
$SqlInstance,
[Alias("Credential")]
[PsCredential]
$SqlCredential,
[switch]
$Update,
[switch]
[Alias('Silent')]
$EnableException
)
begin {
#region verifying parameters
$ComplianceSpec = @()
$ComplianceSpecExclusiveParams = @('Build', 'Kb', @( 'MajorVersion', 'ServicePack', 'CumulativeUpdate'), 'SqlInstance')
foreach ($exclParamGroup in $ComplianceSpecExclusiveParams) {
foreach ($exclParam in $exclParamGroup) {
if (Test-Bound -Parameter $exclParam) {
$ComplianceSpec += $exclParam
break
}
}
}
if ($ComplianceSpec.Length -gt 1) {
Stop-Function -Category InvalidArgument -Message "$($ComplianceSpec -join ', ') are mutually exclusive. Please choose one or the other. Quitting."
return
}
if ($ComplianceSpec.Length -eq 0) {
Stop-Function -Category InvalidArgument -Message "You need to choose at least one parameter."
return
}
if (((Test-Bound -Parameter ServicePack) -or (Test-Bound -Parameter CumulativeUpdate)) -and (Test-Bound -Not -Parameter MajorVersion)) {
Stop-Function -Category InvalidArgument -Message "-MajorVersion is required when specifying SP or CU."
return
}
if ($MajorVersion) {
if ($MajorVersion -match '^(SQL)?(\d{4}(R2)?)$') {
$MajorVersion = $Matches[2]
} else {
Stop-Function -Message "Incorrect SQL Server version format: use SQL2XXX or just 2XXXX - SQL2012, SQL2008R2"
return
}
if (!$ServicePack) {
$ServicePack = 'RTM'
}
if ($ServicePack -match '^(SP)?\s*(\d+)$') {
if ($Matches[2] -eq '0') {
$ServicePack = 'RTM'
} else {
$ServicePack = 'SP' + $Matches[2]
}
} elseif ($ServicePack -notmatch '^RTM$') {
Stop-Function -Message "Incorrect SQL Server service pack format: use SPX, X or RTM, where X is a service pack number"
return
}
if ($CumulativeUpdate) {
if ($CumulativeUpdate -match '^(CU)?\s*(\d+)$') {
if ($Matches[2] -eq '0') {
$CumulativeUpdate = ''
} else {
$CumulativeUpdate = 'CU' + $Matches[2]
}
} else {
Stop-Function -Message "Incorrect SQL Server cumulative update format: use CUX or X, where X is a cumulative update number"
return
}
}
}
#endregion verifying parameters
#region Helper functions
function Get-DbaBuildReferenceIndex {
[CmdletBinding()]
param (
[string]
$Moduledirectory,
[bool]
$Update,
[bool]
$EnableException
)
$orig_idxfile = "$Moduledirectory\bin\dbatools-buildref-index.json"
$DbatoolsData = Get-DbatoolsConfigValue -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-DbaBuildReferenceIndexOnline -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-DbaBuildReferenceIndexOnline {
[CmdletBinding()]
param (
[bool]
$EnableException
)
$url = Get-DbatoolsConfigValue -Name 'assets.sqlbuildreference'
try {
$WebContent = Invoke-TlsWebRequest $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-TlsWebRequest $url -ErrorAction Stop
} catch {
Write-Message -Level Warning -Message "Couldn't download updated index from $url"
return
}
}
return $WebContent
}
function Resolve-DbaBuild {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
[CmdletBinding()]
[OutputType([System.Collections.Hashtable])]
param (
[Parameter(Mandatory, ParameterSetName = 'Build')]
[version]
$Build,
[Parameter(Mandatory, ParameterSetName = 'KB')]
[string]
$Kb,
[Parameter(Mandatory, ParameterSetName = 'HFLevel')]
[string]
$MajorVersion,
[Parameter(ParameterSetName = 'HFLevel')]
[string]
[Alias('SP')]
$ServicePack = 'RTM',
[Parameter(ParameterSetName = 'HFLevel')]
[string]
[Alias('CU')]
$CumulativeUpdate,
$Data,
[bool]
$EnableException
)
if ($Build) {
Write-Message -Level Verbose -Message "Looking for $Build"
$IdxVersion = $Data | Where-Object Version -like "$($Build.Major).$($Build.Minor).*"
} elseif ($Kb) {
Write-Message -Level Verbose -Message "Looking for KB $Kb"
if ($Kb -match '^(KB)?(\d+)$') {
$currentKb = $Matches[2]
$kbVersion = $Data | Where-Object KBList -contains $currentKb
$IdxVersion = $Data | Where-Object Version -like "$($kbVersion.VersionObject.Major).$($kbVersion.VersionObject.Minor).*"
} else {
Stop-Function -Message "Wrong KB name $kb"
return
}
} elseif ($MajorVersion) {
Write-Message -Level Verbose -Message "Looking for SQL $MajorVersion SP $ServicePack CU $CumulativeUpdate"
$kbVersion = $Data | Where-Object Name -eq $MajorVersion
$IdxVersion = $Data | Where-Object Version -like "$($kbVersion.VersionObject.Major).$($kbVersion.VersionObject.Minor).*"
}
$Detected = @{ }
$Detected.MatchType = 'Approximate'
$idxCount = $IdxVersion | Measure-Object | Select-Object -ExpandProperty Count
Write-Message -Level Verbose -Message "We have $idxCount builds in store for this Release"
If ($idxCount -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 ($Build -and $el.VersionObject -gt $Build) {
$Detected.MatchType = 'Approximate'
$Detected.Warning = "$Build not found, closest build we have is $($LastVer.Version)"
break
}
$LastVer = $el
$Detected.BuildLevel = $el.VersionObject
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.Build = $el.Version
$Detected.KB = $el.KBList
if (($Build -and $el.Version -eq $Build) -or ($Kb -and $el.KBList -eq $currentKb)) {
$Detected.MatchType = 'Exact'
break
} elseif ($MajorVersion -and $Detected.SP -contains $ServicePack -and (!$CumulativeUpdate -or ($el.CU -and $el.CU -eq $CumulativeUpdate))) {
$Detected.MatchType = 'Exact'
break
}
}
return $Detected
}
#endregion Helper functions
$moduledirectory = $MyInvocation.MyCommand.Module.ModuleBase
try {
$IdxRef = Get-DbaBuildReferenceIndex -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 {
$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-DbaBuild -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
BuildLevel = $Detected.BuildLevel
SupportedUntil = $Detected.SupportedUntil
MatchType = $Detected.MatchType
Warning = $Detected.Warning
}
}
foreach ($buildstr in $Build) {
$Detected = Resolve-DbaBuild -Build $buildstr -Data $IdxRef -EnableException $EnableException
[PSCustomObject]@{
SqlInstance = $null
Build = $buildstr
NameLevel = $Detected.Name
SPLevel = $Detected.SP
CULevel = $Detected.CU
KBLevel = $Detected.KB
BuildLevel = $Detected.BuildLevel
SupportedUntil = $Detected.SupportedUntil
MatchType = $Detected.MatchType
Warning = $Detected.Warning
} | Select-DefaultView -ExcludeProperty SqlInstance
}
foreach ($kbItem in $Kb) {
$Detected = Resolve-DbaBuild -Kb $kbItem -Data $IdxRef -EnableException $EnableException
[PSCustomObject]@{
SqlInstance = $null
Build = $Detected.Build
NameLevel = $Detected.Name
SPLevel = $Detected.SP
CULevel = $Detected.CU
KBLevel = $Detected.KB
BuildLevel = $Detected.BuildLevel
SupportedUntil = $Detected.SupportedUntil
MatchType = $Detected.MatchType
Warning = $Detected.Warning
} | Select-DefaultView -ExcludeProperty SqlInstance
}
if ($MajorVersion) {
$Detected = Resolve-DbaBuild -MajorVersion $MajorVersion -ServicePack $ServicePack -CumulativeUpdate $CumulativeUpdate -Data $IdxRef -EnableException $EnableException
[PSCustomObject]@{
SqlInstance = $null
Build = $Detected.Build
NameLevel = $Detected.Name
SPLevel = $Detected.SP
CULevel = $Detected.CU
KBLevel = $Detected.KB
BuildLevel = $Detected.BuildLevel
SupportedUntil = $Detected.SupportedUntil
MatchType = $Detected.MatchType
Warning = $Detected.Warning
} | Select-DefaultView -ExcludeProperty SqlInstance
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaSqlBuildReference
}
}
function Get-DbaClientAlias {
<#
.SYNOPSIS
Gets any SQL Server alias for the specified server(s)
.DESCRIPTION
Gets SQL Server alias by reading HKLM:\SOFTWARE\Microsoft\MSSQLServer\Client
.PARAMETER ComputerName
The target computer where the alias has been 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaClientAlias
.EXAMPLE
PS C:\> Get-DbaClientAlias
Gets all SQL Server client aliases on the local computer
.EXAMPLE
PS C:\> Get-DbaClientAlias -ComputerName workstationx
Gets all SQL Server client aliases on Workstationx
.EXAMPLE
PS C:\> Get-DbaClientAlias -ComputerName workstationx -Credential ad\sqldba
Logs into workstationx as ad\sqldba then retrieves all SQL Server client aliases on Workstationx
.EXAMPLE
PS C:\> 'Server1', 'Server2' | Get-DbaClientAlias
Gets all SQL Server client aliases on Server1 and Server2
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[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) {
<# DO NOT use Write-Message as this is inside of a script block #>
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"
}
# "Get SQL Server 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
}
}
}
}
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 target SQL Server instance or 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaClientProtocol
.EXAMPLE
PS C:\> Get-DbaClientProtocol -ComputerName sqlserver2014a
Gets the SQL Server related client protocols on computer sqlserver2014a.
.EXAMPLE
PS C:\> 'sql1','sql2','sql3' | Get-DbaClientProtocol
Gets the SQL Server related client protocols on computers sql1, sql2 and sql3.
.EXAMPLE
PS C:\> 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
PS C:\> (Get-DbaClientProtocol -ComputerName sql2 | Where { $_.DisplayName = 'via' }).Disable()
Disables the VIA ClientNetworkProtocol on computer sql2.
If successful, return code 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-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: Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaCmConnection
.EXAMPLE
PS C:\> Get-DbaCmConnection
List all cached connections.
.EXAMPLE
PS C:\> Get-DbaCmConnection sql2014
List the cached connection - if any - to the server sql2014.
.EXAMPLE
PS C:\> 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)]
[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-DbaCmConnection.
.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: Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaCmObject
.EXAMPLE
PS C:\> Get-DbaCmObject win32_OperatingSystem
Retrieves the common operating system information from the local computer.
.EXAMPLE
PS C:\> 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 Credentials 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")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWMICmdlet", "", Justification = "Using Get-WmiObject is used as a fallback for gathering information")]
param (
[Parameter(Mandatory, Position = 0, ParameterSetName = "Class")]
[Alias('Class')]
[string]
$ClassName,
[Parameter(Mandatory, Position = 0, ParameterSetName = "Query")]
[string]
$Query,
[Parameter(ValueFromPipeline)]
[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 {
# uses cim commands
: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
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaCmsRegServer {
<#
.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
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 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
Tags: RegisteredServer, CMS
Author: Bryan Hamby (@galador)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaCmsRegServer
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlserver2014a
Gets a list of servers from the CMS on sqlserver2014a, using Windows Credentials.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlserver2014a -IncludeSelf
Gets a list of servers from the CMS on sqlserver2014a and includes sqlserver2014a in the output results.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -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
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlserver2014a -Group HR, Accounting
Gets a list of servers in the HR and Accounting groups from the CMS on sqlserver2014a.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -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) {
$groupservers = Get-DbaCmsRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group -ExcludeGroup $ExcludeGroup
if ($groupservers) {
$servers += $groupservers.GetDescendantRegisteredServers()
}
} else {
try {
$serverstore = Get-DbaCmsRegServerStore -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-DbaCmsRegServer $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 {
# here to avoid an empty catch
$null = 1
}
}
}
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
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaCmsRegServerGroup {
<#
.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
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 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
Tags: RegisteredServer, CMS
Author: Tony Wilhelm (@tonywsql)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaCmsRegServerGroup
.EXAMPLE
PS C:\> Get-DbaCmsRegServerGroup -SqlInstance sqlserver2014a
Gets the top level groups from the CMS on sqlserver2014a, using Windows Credentials.
.EXAMPLE
PS C:\> Get-DbaCmsRegServerGroup -SqlInstance sqlserver2014a -SqlCredential $credential
Gets the top level groups from the CMS on sqlserver2014a, using alternative credentials to authenticate to the server.
.EXAMPLE
PS C:\> Get-DbaCmsRegServerGroup -SqlInstance sqlserver2014a -Group HR, Accounting
Gets the HR and Accounting groups from the CMS on sqlserver2014a.
.EXAMPLE
PS C:\> Get-DbaCmsRegServerGroup -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 {
$server = Get-DbaCmsRegServerStore -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 {
# here to avoid an empty catch
$null = 1
}
}
}
} 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-DbaCmsRegServer $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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaRegisteredServerGroup
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaCmsRegServerStore {
<#
.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
The target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaCmsRegServerStore
.EXAMPLE
PS C:\> Get-DbaCmsRegServerStore -SqlInstance sqlserver2014a
Returns a SQL Server Registered Server Store Object from sqlserver2014a
.EXAMPLE
PS C:\> Get-DbaCmsRegServerStore -SqlInstance sqlserver2014a -SqlCredential 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
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaRegisteredServerStore
}
}
#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 instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaComputerCertificate
Gets computer certificates on localhost that are candidates for using with SQL Server's network encryption
.EXAMPLE
PS C:\> Get-DbaComputerCertificate -ComputerName sql2016
Gets computer certificates on sql2016 that are candidates for using with SQL Server's network encryption
.EXAMPLE
PS C:\> 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,
[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
}
function Get-CoreCertStore {
[CmdletBinding()]
param (
[ValidateSet("CurrentUser", "LocalMachine")]
[string]$Store,
[ValidateSet("AddressBook", "AuthRoot, CertificateAuthority", "Disallowed", "My", "Root", "TrustedPeople", "TrustedPublisher")]
[string]$Folder,
[ValidateSet("ReadOnly", "ReadWrite")]
[string]$Flag = "ReadOnly"
)
$storename = [System.Security.Cryptography.X509Certificates.StoreLocation]::$Store
$foldername = [System.Security.Cryptography.X509Certificates.StoreName]::$Folder
$flags = [System.Security.Cryptography.X509Certificates.OpenFlags]::$Flag
$certstore = [System.Security.Cryptography.X509Certificates.X509Store]::New($foldername, $storename)
$certstore.Open($flags)
$certstore
}
function Get-CoreCertificate {
[CmdletBinding()]
param (
[ValidateSet("CurrentUser", "LocalMachine")]
[string]$Store,
[ValidateSet("AddressBook", "AuthRoot, CertificateAuthority", "Disallowed", "My", "Root", "TrustedPeople", "TrustedPublisher")]
[string]$Folder,
[ValidateSet("ReadOnly", "ReadWrite")]
[string]$Flag = "ReadOnly",
[string[]]$Thumbprint,
[System.Security.Cryptography.X509Certificates.X509Store[]]$InputObject
)
if (-not $InputObject) {
$InputObject += Get-CoreCertStore -Store $Store -Folder $Folder -Flag $Flag
}
$certs = ($InputObject).Certificates
if ($Thumbprint) {
$certs = $certs | Where-Object Thumbprint -in $Thumbprint
}
$certs
}
if ($Thumbprint) {
try {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose "Searching Cert:\$Store\$Folder"
Get-CoreCertificate -Store $Store -Folder $Folder -Thumbprint $Thumbprint
} catch {
# don't care - there's a weird issue with remoting where an exception gets thrown for no apparent reason
# here to avoid an empty catch
$null = 1
}
} else {
try {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose "Searching Cert:\$Store\$Folder"
Get-CoreCertificate -Store $Store -Folder $Folder | Where-Object EnhancedKeyUsageList -match '1\.3\.6\.1\.5\.5\.7\.3\.1'
} catch {
# still don't care
# here to avoid an empty catch
$null = 1
}
}
}
#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), https://wsmelton.github.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaComputerSystem
.EXAMPLE
PS C:\> Get-DbaComputerSystem
Returns information about the local computer's computer system
.EXAMPLE
PS C:\> Get-DbaComputerSystem -ComputerName sql2016
Returns information about the sql2016's computer system
.EXAMPLE
PS C:\> 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)]
[Alias("cn", "host", "Server")]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$IncludeAws,
[switch][Alias('Silent')]
$EnableException
)
process {
foreach ($computer in $ComputerName) {
try {
$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-TlsWebRequest -TimeoutSec 15 -Uri 'http://169.254.169.254').StatusCode) -eq 200 } -Raw
if ($isAws) {
$scriptBlock = {
[PSCustomObject]@{
AmiId = (Invoke-TlsWebRequest -Uri 'http://169.254.169.254/latest/meta-data/ami-id').Content
IamRoleArn = ((Invoke-TlsWebRequest -Uri 'http://169.254.169.254/latest/meta-data/iam/info').Content | ConvertFrom-Json).InstanceProfileArn
InstanceId = (Invoke-TlsWebRequest -Uri 'http://169.254.169.254/latest/meta-data/instance-id').Content
InstanceType = (Invoke-TlsWebRequest -Uri 'http://169.254.169.254/latest/meta-data/instance-type').Content
AvailabilityZone = (Invoke-TlsWebRequest -Uri 'http://169.254.169.254/latest/meta-data/placement/availability-zone').Content
PublicHostname = (Invoke-TlsWebRequest -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
}
}
}
}
#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 or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaConnection
.EXAMPLE
PS C:\> Get-DbaConnection -SqlInstance sql2016, sql2017
Returns client connection information from sql2016 and sql2017
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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-DbaCpuRingBuffer {
<#
.SYNOPSIS
Collects CPU data from sys.dm_os_ring_buffers. Works on SQL Server 2005 and above.
.DESCRIPTION
This command is based off of Glen Berry's diagnostic query for average CPU
The sys.dm_os_ring_buffers stores the average CPU utilization history
by the current instance of SQL Server, plus the summed average CPU utilization
by all other processes on your machine are captured in one minute increments
for the past 256 minutes.
Reference: https://www.sqlskills.com/blogs/glenn/sql-server-diagnostic-information-queries-detailed-day-16//
.PARAMETER SqlInstance
Allows you to specify a comma separated list of servers to query.
.PARAMETER SqlCredential
Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted. To use:
$cred = Get-Credential, this pass this $cred to the param.
Windows Authentication will be used if DestinationSqlCredential is not specified. To connect as a different Windows user, run PowerShell as that user.
.PARAMETER CollectionMinutes
Allows you to specify a Collection Period in Minutes. Default is 60 minutes
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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
Author: Patrick Flynn (@sqllensman)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaCpuRingBuffer
.EXAMPLE
PS C:\> Get-DbaCpuRingBuffer -SqlInstance sql2008, sqlserver2012
Gets CPU Statistics from sys.dm_os_ring_buffers for servers sql2008 and sqlserver2012 for last 60 minutes.
.EXAMPLE
PS C:\> Get-DbaCpuRingBuffer -SqlInstance sql2008 -CollectionMinutes 240
Gets CPU Statistics from sys.dm_os_ring_buffers for server sql2008 for last 240 minutes
.EXAMPLE
PS C:\> $output = Get-DbaCpuRingBuffer -SqlInstance sql2008 -CollectionMinutes 240 | Select * | ConvertTo-DbaDataTable
Gets CPU Statistics from sys.dm_os_ring_buffers for server sql2008 for last 240 minutes into a Data Table.
.EXAMPLE
PS C:\> 'sql2008','sql2012' | Get-DbaCpuRingBuffer
Gets CPU Statistics from sys.dm_os_ring_buffers for servers sql2008 and sqlserver2012
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Get-DbaCpuRingBuffer -SqlInstance sql2008 -SqlCredential $cred
Connects using sqladmin credential and returns CPU Statistics from sys.dm_os_ring_buffers from sql2008
#>
[CmdletBinding()]
Param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[int]$CollectionMinutes = 60,
[Alias('Silent')]
[switch]$EnableException
)
process {
if (Test-FunctionInterrupt) { return }
foreach ($instance in $SqlInstance) {
Write-Message -Level Verbose -Message "Attempting to connect 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.VersionMajor -gt 9) {
$currentTimestamp = ($server.Query("SELECT cpu_ticks / CONVERT (float, ( cpu_ticks / ms_ticks )) as TimeStamp FROM sys.dm_os_sys_info"))[0]
} else {
$currentTimestamp = ($server.Query("SELECT cpu_ticks / CONVERT(FLOAT, cpu_ticks_in_ms) as TimeStamp FROM sys.dm_os_sys_info"))[0]
}
Write-Message -Level Verbose -Message "Using current timestampe of $currentTimestamp"
$sql = "With RingBufferSchedulerMonitor as
(
SELECT
timestamp,
CONVERT(xml, record) AS record
FROM sys.dm_os_ring_buffers
WHERE (ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR')
AND (record LIKE '%%')
), RingBufferSchedulerMonitorValues as
(
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,
DATEADD(ss, (-1 * ($currentTimestamp - [timestamp]))/1000, GETDATE()) AS EventTime
FROM RingBufferSchedulerMonitor
)
Select
SERVERPROPERTY('ServerName') as ServerName,
record_id,
EventTime,
SQLProcessUtilization,
SystemIdle,
100 - SystemIdle - SQLProcessUtilization AS OtherProcessUtilization
From RingBufferSchedulerMonitorValues
WHERE EventTime> DATEADD(MINUTE, -$CollectionMinutes, GETDATE()) ;"
Write-Message -Level Verbose -Message "Executing Sql Staement: $sql"
foreach ($row in $server.Query($sql)) {
[PSCustomObject]@{
ComputerName = $server.NetName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
RecordId = $row.record_id
EventTime = $row.EventTime
SQLProcessUtilization = $row.SQLProcessUtilization
OtherProcessUtilization = $row.OtherProcessUtilization
SystemIdle = $row.SystemIdle
}
}
}
}
}
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 destination server but the process
column is specific to -SqlInstance passed.
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaCpuUsage
.EXAMPLE
PS C:\> Get-DbaCpuUsage -SqlInstance sql2017
Logs into the SQL Server instance "sql2017" and also the Computer itself (via WMI) to gather information
.EXAMPLE
PS C:\> $usage = Get-DbaCpuUsage -SqlInstance sql2017
PS C:\> $usage.Process
Explores the processes (from Get-DbaProcess) associated with the usage results
.EXAMPLE
PS C:\> Get-DbaCpuUsage -SqlInstance sql2017 -SqlCredential sqladmin -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 {
$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
The target SQL Server instance or instances. 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaCredential
.EXAMPLE
PS C:\> Get-DbaCredential -SqlInstance localhost
Returns all SQL Credentials on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaCredential -SqlInstance localhost, sql2016 -Name 'PowerShell Proxy'
Returns the SQL Credentials named 'PowerShell Proxy' for the local and sql2016 SQL Server instances
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[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) {
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
The target SQL Server instance or instances. 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: Error, CustomError
Author: Garry Bargsley (@gbargsley), http://blog.garrybargsley.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaCustomError
.EXAMPLE
PS C:\> Get-DbaCustomError -SqlInstance localhost
Returns all Custom Error Message(s) on the local default SQL Server instance
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[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
}
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 target SQL Server instance or instances. Defaults to localhost.
.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 ExcludeUser
If this switch is enabled, only databases which are not User databases will be processed.
This parameter cannot be used with -ExcludeSystem.
.PARAMETER ExcludeSystem
If this switch is enabled, only databases which are not System databases will be processed.
This parameter cannot be used with -ExcludeUser.
.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 | Klaas Vandenberghe (@PowerDbaKlaas) | Simone Bizzotto ( @niphlod )
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDatabase
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance localhost
Returns all databases on the local default SQL Server instance.
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance localhost -ExcludeUser
Returns only the system databases on the local default SQL Server instance.
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance localhost -ExcludeSystem
Returns only the user databases on the local default SQL Server instance.
.EXAMPLE
PS C:\> 'localhost','sql2016' | Get-DbaDatabase
Returns databases on multiple instances piped into the function.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaDatabase -SqlInstance SQL1\SQLExpress -Status Normal
Returns only the user databases with status 'normal' from SQL Server instance SQL1\SQLExpress.
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaDatabase -SqlInstance SQL1\SQLExpress,SQL2 -Encrypted
Returns only databases using TDE from SQL Server instances SQL1\SQLExpress and SQL2.
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance SQL1\SQLExpress,SQL2 -Access ReadOnly
Returns only read only databases from SQL Server instances SQL1\SQLExpress and SQL2.
.EXAMPLE
PS C:\> 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")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Internal functions are ignored")]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[Alias("SystemDbOnly", "NoUserDb", "ExcludeAllUserDb")]
[switch]$ExcludeUser,
[Alias("UserDbOnly", "NoSystemDb", "ExcludeAllSystemDb")]
[switch]$ExcludeSystem,
[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 ($ExcludeUser -and $ExcludeSystem) {
Stop-Function -Message "You cannot specify both ExcludeUser and ExcludeSystem." -Continue -EnableException $EnableException
}
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($instance in $SqlInstance) {
try {
$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 ($ExcludeUser) {
$DBType = @($true)
} elseif ($ExcludeSystem) {
$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 {
try {
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")
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
}
}
$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 | Where-Object Name -ceq $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 -or !$_.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-DbaDbAssembly {
<#
.SYNOPSIS
Gets SQL Database Assembly information for each instance(s) of SQL Server.
.DESCRIPTION
The Get-DbaDbAssembly command gets SQL Database Assembly information for each instance(s) of SQL Server.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbAssembly
.EXAMPLE
PS C:\> Get-DbaDbAssembly -SqlInstance localhost
Returns all Database Assembly on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaDbAssembly -SqlInstance localhost, sql2016
Returns all Database Assembly for the local and sql2016 SQL Server instances
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[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
}
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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDatabaseAssembly
}
}
function Get-DbaDbccHelp {
<#
.SYNOPSIS
Execution of Database Console Command DBCC HELP
.DESCRIPTION
Returns the results of DBCC HELP
Read more:
- https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-help-transact-sql
.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 Statement
Is the name of the DBCC command for which to receive syntax information.
Provide only the part of the DBCC command that follows DBCC,
for example, CHECKDB instead of DBCC CHECKDB.
.PARAMETER IncludeUndocumented
Allows getting help for undocumented DBCC commands. Requires Traceflag 2588
This only works for SQL Server 2005 or Higher
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: DBCC
Author: Patrick Flynn (@sqllensman)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbccUserOptions
.EXAMPLE
PS C:\> Get-DbaDbccHelp -SqlInstance SQLInstance -Statement FREESYSTEMCACHE -Verbose | Format-List
Runs the command DBCC HELP(FREESYSTEMCACHE) WITH NO_INFOMSGS against the SQL Server instance SQLInstance
.EXAMPLE
PS C:\> Get-DbaDbccHelp -SqlInstance LensmanSB -Statement WritePage -IncludeUndocumented | Format-List
Sets TraeFlag 2588 on for session and then runs the command DBCC HELP(WritePage) WITH NO_INFOMSGS against the SQL Server instance SQLInstance
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string]$Statement,
[switch]$IncludeUndocumented,
[switch]$EnableException
)
begin {
if (Test-Bound -Not -ParameterName Statement) {
Stop-Function -Message "You must specify a value for Statement"
return
}
$stringBuilder = New-Object System.Text.StringBuilder
if (Test-Bound -ParameterName IncludeUndocumented) {
$null = $stringBuilder.Append("DBCC TRACEON (2588) WITH NO_INFOMSGS;")
}
Write-Message -Message "Get Help Information for $Statement" -Level Verbose
$null = $stringBuilder.Append("DBCC HELP($Statement) WITH NO_INFOMSGS;")
}
process {
foreach ($instance in $SqlInstance) {
Write-Message -Message "Attempting Connection 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
}
try {
$query = $StringBuilder.ToString()
Write-Message -Message "Query to run: $query" -Level Verbose
$results = $server | Invoke-DbaQuery -Query $query -MessagesToOutput
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
}
[PSCustomObject]@{
Operation = $Statement
Cmd = "DBCC HELP($Statement)"
Output = $results
}
}
}
}
function Get-DbaDbccMemoryStatus {
<#
.SYNOPSIS
Gets the results of DBCC MEMORYSTATUS. Works on SQL Server 2000-2019.
.DESCRIPTION
This command is used to run the DBCC MEMORYSTATUS comand and collect results in a single usable recordset
Reference:
- https://blogs.msdn.microsoft.com/timchapman/2012/08/16/how-to-parse-dbcc-memorystatus-via-powershell/
- https://support.microsoft.com/en-us/help/907877/how-to-use-the-dbcc-memorystatus-command-to-monitor-memory-usage-on-sq
.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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: DBCC, Memory
Author: Patrick Flynn (@sqllensman)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaMemoryStatus
.EXAMPLE
PS C:\> Get-DbaDbccMemoryStatus -SqlInstance sqlcluster, sqlserver2012
Get output of DBCC MEMORYSTATUS for instances "sqlcluster" and "sqlserver2012". Returns results in a single recordset.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlcluster | Get-DbaDbccMemoryStatus
Get output of DBCC MEMORYSTATUS for all servers in Server Central Management Server
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[switch]$EnableException
)
begin {
$query = 'DBCC MEMORYSTATUS'
}
process {
foreach ($instance in $SqlInstance) {
Write-Message -Message "Attempting Connection to $instance" -Level Verbose
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 $query data from server: $instance"
try {
$sqlconnection = New-Object System.Data.SqlClient.SqlConnection
$sqlconnection.ConnectionString = $server.ConnectionContext.ConnectionString
$sqlconnection.Open()
$datatable = New-Object system.Data.DataSet
$dataadapter = New-Object system.Data.SqlClient.SqlDataAdapter($query, $sqlconnection)
$dataadapter.fill($datatable) | Out-Null
$recordset = 0
$rowId = 0
$recordsetId = 0
foreach ($dataset in $datatable.Tables) {
$dataSection = $dataset.Columns[0].ColumnName
$dataType = $dataset.Columns[1].ColumnName
$recordset = $recordset + 1
foreach ($row in $dataset.Rows) {
$rowId = $rowId + 1
$recordsetId = $recordsetId + 1
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
RecordSet = $RecordSet
RowId = $RowId
RecordSetId = $RecordSetId
Type = $dataSection
Name = $Row[0]
Value = $Row[1]
ValueType = $dataType
}
}
$recordsetId = 0
}
} catch {
Stop-Function -Message "Failure Executing $query" -ErrorRecord $_ -Target $instance -Continue
}
}
}
}
function Get-DbaDbccProcCache {
<#
.SYNOPSIS
Execution of Database Console Command DBCC PROCCACHE
.DESCRIPTION
Returns the results of DBCC PROCCACHE
Read more:
- https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-proccache-transact-sql
.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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: DBCC
Author: Patrick Flynn (@sqllensman)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbccProcCache
.EXAMPLE
PS C:\> Get-DbaDbccProcCache -SqlInstance Server1
Get results of DBCC PROCCACHE for Instance Server1
.EXAMPLE
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaDbccProcCache
Get results of DBCC PROCCACHE for Instances Sql1 and Sql2/sqlexpress
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Get-DbaDbccProcCache -SqlInstance Server1 -SqlCredential $cred
Connects using sqladmin credential and gets results of DBCC PROCCACHE for Instance Server1 using
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[switch]$EnableException
)
begin {
$stringBuilder = New-Object System.Text.StringBuilder
$null = $stringBuilder.Append("DBCC PROCCACHE WITH NO_INFOMSGS")
}
process {
$query = $StringBuilder.ToString()
foreach ($instance in $SqlInstance) {
Write-Message -Message "Attempting Connection to $instance" -Level Verbose
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
try {
if ($Pscmdlet.ShouldProcess($server.Name, "Execute the command $query")) {
Write-Message -Message "Query to run: $query" -Level Verbose
$results = $server.Query($query)
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
}
If ($Pscmdlet.ShouldProcess("console", "Outputting object")) {
foreach ($row in $results) {
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Count = $row[0]
Used = $row[1]
Active = $row[2]
CacheSize = $row[3]
CacheUsed = $row[4]
CacheActive = $row[5]
}
}
}
}
}
}
function Get-DbaDbccUserOption {
<#
.SYNOPSIS
Execution of Database Console Command DBCC USEROPTIONS
.DESCRIPTION
Returns the results of DBCC USEROPTIONS
Read more:
- https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-useroptions-transact-sql
.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 Option
Return only specific options. Returns all results if not specified.
Accepts any values in set 'ansi_null_dflt_on', 'ansi_nulls', 'ansi_padding', 'ansi_warnings', 'arithabort', 'concat_null_yields_null', 'datefirst', 'dateformat', 'isolation level', 'language', 'lock_timeout', 'quoted_identifier', 'textsize'
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: DBCC
Author: Patrick Flynn (@sqllensman)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbccUserOptions
.EXAMPLE
PS C:\> Get-DbaDbccUserOptions -SqlInstance Server1
Get results of DBCC USEROPTIONS for Instance Server1
.EXAMPLE
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaDbccUserOptions
Get results of DBCC USEROPTIONS for Instances Sql1 and Sql2/sqlexpress
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Get-DbaDbccUserOptions -SqlInstance Server1 -SqlCredential $cred
Connects using sqladmin credential and gets results of DBCC USEROPTIONS for Instance Server1
.EXAMPLE
PS C:\> Get-DbaDbccUserOptions -SqlInstance Server1 -Option ansi_nulls, ansi_warnings, datefirst
Gets results of DBCC USEROPTIONS for Instance Server1. Only display results for the options ansi_nulls, ansi_warnings, datefirst
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[ValidateSet('ansi_null_dflt_on', 'ansi_nulls', 'ansi_padding', 'ansi_warnings', 'arithabort', 'concat_null_yields_null', 'datefirst', 'dateformat', 'isolation level', 'language', 'lock_timeout', 'quoted_identifier', 'textsize')]
[string[]]$Option,
[switch]$EnableException
)
begin {
$stringBuilder = New-Object System.Text.StringBuilder
$null = $stringBuilder.Append("DBCC USEROPTIONS WITH NO_INFOMSGS")
}
process {
$query = $StringBuilder.ToString()
foreach ($instance in $SqlInstance) {
Write-Message -Message "Attempting Connection to $instance" -Level Verbose
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Error connecting to $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
try {
Write-Message -Message "Query to run: $query" -Level Verbose
$results = $server.Query($query)
} catch {
Stop-Function -Message "Failure running $query against $instance" -ErrorRecord $_ -Target $server -Continue
}
foreach ($row in $results) {
if ((Test-Bound -Not -ParameterName Option) -or ($row[0] -in $Option)) {
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Option = $row[0]
Value = $row[1]
}
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 InputObject
Enables piping from Get-DbaDatabase
.PARAMETER Certificate
Get specific certificate by name
.PARAMETER Certificate
Get specific certificate by subject
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbCertificate -SqlInstance sql2016
Gets all certificates
.EXAMPLE
PS C:\> Get-DbaDbCertificate -SqlInstance Server1 -Database db1
Gets the certificate for the db1 database
.EXAMPLE
PS C:\> Get-DbaDbCertificate -SqlInstance Server1 -Database db1 -Certificate cert1
Gets the cert1 certificate within the db1 database
.EXAMPLE
PS C:\> Get-DbaDbCertificate -SqlInstance Server1 -Database db1 -Subject 'Availability Group Cert'
Gets the cert1 certificate within the db1 database
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[string[]]$ExcludeDatabase,
[object[]]$Certificate, # sometimes it's text, other times cert
[string[]]$Subject,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaDatabaseCertificate
}
process {
if ($SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase
}
foreach ($db in $InputObject) {
if (!$db.IsAccessible) {
Write-Message -Level Warning -Message "$db is not accessible, skipping"
continue
}
#Variable marked as unused by PSScriptAnalyzer
#$dbName = $db.Name
$certs = $db.Certificates
if ($null -eq $certs) {
Write-Message -Message "No certificate exists in the $db database on $instance" -Target $db -Level Verbose
continue
}
if ($Certificate) {
$certs = $certs | Where-Object Name -in $Certificate
}
if ($Subject) {
$certs = $certs | Where-Object Subject -in $Subject
}
foreach ($cert in $certs) {
Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name ComputerName -value $db.ComputerName
Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name InstanceName -value $db.InstanceName
Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name SqlInstance -value $db.SqlInstance
Add-Member -Force -InputObject $cert -MemberType NoteProperty -Name Database -value $db.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 or instances
.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: Claudio Silva (@ClaudioESSilva), https://claudioessilva.eu
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbCheckConstraint -SqlInstance sql2016
Gets all database check constraints.
.EXAMPLE
PS C:\> Get-DbaDbCheckConstraint -SqlInstance Server1 -Database db1
Gets the check constraints for the db1 database.
.EXAMPLE
PS C:\> Get-DbaDbCheckConstraint -SqlInstance Server1 -ExcludeDatabase db1
Gets the check constraints for all databases except db1.
.EXAMPLE
PS C:\> Get-DbaDbCheckConstraint -SqlInstance Server1 -ExcludeSystemTable
Gets the check constraints for all databases that are not system objects.
.EXAMPLE
PS C:\> '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 {
$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-DbaDbCompatibility {
<#
.SYNOPSIS
Displays the compatibility level for SQL Server databases.
.DESCRIPTION
Get the current database compatibility level for all databases on a server or list of databases passed in to the function.
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.PARAMETER SqlCredential
SqlCredential object used to connect to the SQL Server as a different user.
.PARAMETER Database
The database or databases to process. If unspecified, all databases will be processed.
.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: Compatibility, Database
Author: Garry Bargsley, http://blog.garrybargsley.com/
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbCompatibility
.EXAMPLE
PS C:\> Get-DbaDbCompatibility -SqlInstance localhost\sql2017
Displays database compatibility level for all user databases on server localhost\sql2017
.EXAMPLE
PS C:\> Get-DbaDbCompatibility -SqlInstance localhost\sql2017 -Database Test
Displays database compatibility level for database Test on server localhost\sql2017
#>
[CmdletBinding()]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[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 ($SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database
}
foreach ($db in $InputObject) {
$server = $db.Parent
$ServerVersion = $server.VersionMajor
Write-Message -Level Verbose -Message "SQL Server is using Version: $ServerVersion"
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Database = $db.Name
Compatibility = $db.CompatibilityLevel
}
}
}
}
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
The target SQL Server instance or instances. 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
Tags: Compression, Table, Database
Author: Jess Pomfret (@jpomfret), jesspomfret.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbCompression -SqlInstance localhost
Returns objects size and current compression level for all user databases.
.EXAMPLE
PS C:\> Get-DbaDbCompression -SqlInstance localhost -Database TestDatabase
Returns objects size and current compression level for objects within the TestDatabase database.
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$Database,
[object[]]$ExcludeDatabase,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$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-DbaDbEncryption {
<#
.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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbEncryption
.EXAMPLE
PS C:\> Get-DbaDbEncryption -SqlInstance DEV01
List all encryption found on the instance by database
.EXAMPLE
PS C:\> Get-DbaDbEncryption -SqlInstance DEV01 -Database MyDB
List all encryption found for the MyDB database.
.EXAMPLE
PS C:\> Get-DbaDbEncryption -SqlInstance DEV01 -ExcludeDatabase MyDB
List all encryption found for all databases except MyDB.
.EXAMPLE
PS C:\> Get-DbaDbEncryption -SqlInstance DEV01 -IncludeSystemDBs
List all encryption found for all databases including the system databases.
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline, Mandatory)]
[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
try {
$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
}
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDatabaseEncryption
}
}
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, cviorel.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: GNU GPL v3 https://opensource.org/licenses/GPL-3.0
.LINK
http://dbatools.io/Get-DbaDbExtentDiff
.EXAMPLE
PS C:\> Get-DbaDbExtentDiff -SqlInstance SQL2016 -Database DBA
Get the changes for the DBA database.
.EXAMPLE
PS C:\> $Cred = Get-Credential sqladmin
PS C:\> Get-DbaDbExtentDiff -SqlInstance SQL2017N1, SQL2017N2, SQL2016 -Database DB01 -SqlCredential $Cred
Get the changes for the DB01 database on multiple servers.
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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 {
$server = Connect-SqlInstance -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
}
}
#Available from 2016 SP2
if ($server.Version -ge [version]'13.0.5026') {
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-DbaDbFeatureUsage {
<#
.SYNOPSIS
Shows features that are enabled in the database but not supported on all editions of SQL Server. Basically checks for Enterprise feature usage.
.DESCRIPTION
Shows features that are enabled in the database but not supported on all editions of SQL Server.
Basically checks for Enterprise feature usage.
This feature must be removed before the database can be migrated to all available editions of SQL Server.
.PARAMETER SqlInstance
The target SQL Server instance
.PARAMETER SqlCredential
Login to the target instance using alternate Windows or SQL Login Authentication. 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 InputObject
A collection of databases (such as returned by Get-DbaDatabase), to be tested.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Deprecated
Author: Brandon Abshire, netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbFeatureUsage
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2008 -Database testdb, db2 | Get-DbaDbFeatureUsage
Shows features that are enabled in the testdb and db2 databases but
not supported on the all the editions of SQL Server.
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[string[]]$ExcludeDatabase,
[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, feature_id as Id,
feature_name as Feature, DB_NAME() as [Database] FROM sys.dm_db_persisted_sku_features"
}
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $instance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase
}
foreach ($db in $InputObject) {
Write-Message -Level Verbose -Message "Processing $db on $($db.Parent.Name)"
if ($db.IsAccessible -eq $false) {
Stop-Function -Message "The database $db is not accessible. Skipping database." -Continue
}
try {
$db.Query($sql)
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaDbFile {
<#
.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 or instances
.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
Tags: Database
Author: Stuart Moore (@napalmgram), stuart-moore.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbFile -SqlInstance sql2016
Will return an object containing all file groups and their contained files for every database on the sql2016 SQL Server instance
.EXAMPLE
PS C:\> Get-DbaDbFile -SqlInstance sql2016 -Database Impromptu
Will return an object containing all file groups and their contained files for the Impromptu Database on the sql2016 SQL Server instance
.EXAMPLE
PS C:\> Get-DbaDbFile -SqlInstance sql2016 -Database Impromptu, Trading
Will return an object containing all file groups 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 {
$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) {
Write-Message -Level Verbose -Message "Querying database $db"
try {
$version = $server.Query("SELECT compatibility_level FROM sys.databases WHERE name = '$($db.Name)'")
$version = [int]($version.compatibility_level / 10)
} catch {
$version = 8
}
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"
try {
$results = $server.Query($query, $db.Name)
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
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.
if ($server.VersionMajor -gt 8) {
$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
}
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDatabaseFIle
}
}
function Get-DbaDbForeignKey {
<#
.SYNOPSIS
Gets database Foreign Keys.
.DESCRIPTION
Gets database Foreign Keys.
.PARAMETER SqlInstance
The target SQL Server instance or instances
.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: Claudio Silva (@ClaudioESSilva), https://claudioessilva.eu
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbForeignKey -SqlInstance sql2016
Gets all database Foreign Keys.
.EXAMPLE
PS C:\> Get-DbaDbForeignKey -SqlInstance Server1 -Database db1
Gets the Foreign Keys for the db1 database.
.EXAMPLE
PS C:\> Get-DbaDbForeignKey -SqlInstance Server1 -ExcludeDatabase db1
Gets the Foreign Keys for all databases except db1.
.EXAMPLE
PS C:\> Get-DbaDbForeignKey -SqlInstance Server1 -ExcludeSystemTable
Gets the Foreign Keys from all tables that are not system objects from all databases.
.EXAMPLE
PS C:\> '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 {
$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-DbaDbLogShipError {
<#
.SYNOPSIS
Get-DbaDbLogShipError 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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbLogShipError
.EXAMPLE
PS C:\> Get-DbaDbLogShipError -SqlInstance sql1
Get all the log shipping errors that occurred
.EXAMPLE
PS C:\> Get-DbaDbLogShipError -SqlInstance sql1 -Action Backup
Get the errors that have something to do with the backup of the databases
.EXAMPLE
PS C:\> Get-DbaDbLogShipError -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
PS C:\> Get-DbaDbLogShipError -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
PS C:\> Get-DbaDbLogShipError -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, ValueFromPipeline)]
[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
)
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
}
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' }
}
foreach ($result in $results) {
[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
}
}
} else {
Write-Message -Message "No log shipping errors found" -Level Verbose
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaLogShippingError
}
}
function Get-DbaDbMail {
<#
.SYNOPSIS
Gets the database mail from SQL Server
.DESCRIPTION
Gets the database mail from SQL Server
.PARAMETER SqlInstance
TThe 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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: DatabaseMail, DBMail, Mail
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbMail
.EXAMPLE
PS C:\> Get-DbaDbMail -SqlInstance sql01\sharepoint
Returns the db mail server object on sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMail -SqlInstance sql01\sharepoint | Select *
Returns the db mail server object on sql01\sharepoint then return a bunch more columns
.EXAMPLE
PS C:\> $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
PS C:\> $servers | Get-DbaDbMail
Returns the db mail server object for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline, Mandatory)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category Connectiondbmail -dbmailRecord $_ -Target $instance -Continue
}
try {
$mailserver = $server.Mail
Add-Member -Force -InputObject $mailserver -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
Add-Member -Force -InputObject $mailserver -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
Add-Member -Force -InputObject $mailserver -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
$mailserver | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Profiles, Accounts, ConfigurationValues, Properties
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
function Get-DbaDbMailAccount {
<#
.SYNOPSIS
Gets database mail accounts from SQL Server
.DESCRIPTION
Gets database mail accounts from SQL Server
.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 Account
Specifies one or more account(s) to get. If unspecified, all accounts will be returned.
.PARAMETER ExcludeAccount
Specifies one or more account(s) to exclude.
.PARAMETER InputObject
Accepts pipeline input from Get-DbaDbMail
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: DatabaseMail, DbMail, Mail
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MITIT
.LINK
https://dbatools.io/Get-DbaDbMailAccount
.EXAMPLE
PS C:\> Get-DbaDbMailAccount -SqlInstance sql01\sharepoint
Returns Database Mail accounts on sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMailAccount -SqlInstance sql01\sharepoint -Account 'The DBA Team'
Returns The DBA Team Database Mail account from sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMailAccount -SqlInstance sql01\sharepoint | Select *
Returns the Database Mail accounts on sql01\sharepoint then return a bunch more columns
.EXAMPLE
PS C:\> $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
PS C:\> $servers | Get-DbaDbMail | Get-DbaDbMailAccount
Returns the Database Mail accounts for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[string[]]$Account,
[string[]]$ExcludeAccount,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Mail.SqlMail[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDbMail -SqlInstance $SqlInstance -SqlCredential $SqlCredential
}
if (-not $InputObject) {
Stop-Function -Message "No servers to process"
return
}
foreach ($mailserver in $InputObject) {
try {
$accounts = $mailserver.Accounts
if ($Account) {
$accounts = $accounts | Where-Object Name -in $Account
}
If ($ExcludeAccount) {
$accounts = $accounts | Where-Object Name -notin $ExcludeAccount
}
$accounts | Add-Member -Force -MemberType NoteProperty -Name ComputerName -value $mailserver.ComputerName
$accounts | Add-Member -Force -MemberType NoteProperty -Name InstanceName -value $mailserver.InstanceName
$accounts | Add-Member -Force -MemberType NoteProperty -Name SqlInstance -value $mailserver.SqlInstance
$accounts | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, ID, Name, DisplayName, Description, EmailAddress, ReplyToAddress, IsBusyAccount, MailServers
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
function Get-DbaDbMailConfig {
<#
.SYNOPSIS
Gets database mail configs from SQL Server
.DESCRIPTION
Gets database mail configs from SQL Server
.PARAMETER SqlInstance
TThe 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 Name
Specifies one or more config(s) to get. If unspecified, all configs will be returned.
.PARAMETER InputObject
Accepts pipeline input from Get-DbaDbMail
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: DatabaseMail, DBMail, Mail
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbMailConfig
.EXAMPLE
PS C:\> Get-DbaDbMailConfig -SqlInstance sql01\sharepoint
Returns DBMail configs on sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMailConfig -SqlInstance sql01\sharepoint -Name ProhibitedExtensions
Returns the ProhibitedExtensions configuration on sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMailConfig -SqlInstance sql01\sharepoint | Select *
Returns the DBMail configs on sql01\sharepoint then return a bunch more columns
.EXAMPLE
PS C:\> $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
PS C:\> $servers | Get-DbaDbMail | Get-DbaDbMailConfig
Returns the DBMail configs for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[Alias("Config", "ConfigName")]
[string[]]$Name,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Mail.SqlMail[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDbMail -SqlInstance $SqlInstance -SqlCredential $SqlCredential
}
if (-not $InputObject) {
Stop-Function -Message "No servers to process"
return
}
foreach ($mailserver in $InputObject) {
try {
$configs = $mailserver.ConfigurationValues
if ($Name) {
$configs = $configs | Where-Object Name -in $Name
}
$configs | Add-Member -Force -MemberType NoteProperty -Name ComputerName -value $mailserver.ComputerName
$configs | Add-Member -Force -MemberType NoteProperty -Name InstanceName -value $mailserver.InstanceName
$configs | Add-Member -Force -MemberType NoteProperty -Name SqlInstance -value $mailserver.SqlInstance
$configs | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Name, Value, Description
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
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
TThe target 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: DatabaseMail, DBMail, Mail
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbMailHistory
.EXAMPLE
PS C:\> Get-DbaDbMailHistory -SqlInstance sql01\sharepoint
Returns the entire DBMail history on sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMailHistory -SqlInstance sql01\sharepoint | Select *
Returns the entire DBMail history on sql01\sharepoint then return a bunch more columns
.EXAMPLE
PS C:\> $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
PS C:\> $servers | Get-DbaDbMailHistory
Returns the all DBMail history for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[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) {
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
TThe target 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: DatabaseMail, DBMail, Mail
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbMailLog
.EXAMPLE
PS C:\> Get-DbaDbMailLog -SqlInstance sql01\sharepoint
Returns the entire DBMail log on sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMailLog -SqlInstance sql01\sharepoint | Select *
Returns the entire DBMail log on sql01\sharepoint, includes all returned information.
.EXAMPLE
PS C:\> $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
PS C:\> $servers | Get-DbaDbMailLog -Type Error, Information
Returns only the Error and Information DBMail log for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[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) {
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 "Failure" -InnerErrorRecord $_ -Continue
}
}
}
}
function Get-DbaDbMailProfile {
<#
.SYNOPSIS
Gets database mail profiles from SQL Server
.DESCRIPTION
Gets database mail profiles from SQL Server
.PARAMETER SqlInstance
TThe 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 Profile
Specifies one or more profile(s) to get. If unspecified, all profiles will be returned.
.PARAMETER ExcludeProfile
Specifies one or more profile(s) to exclude.
.PARAMETER InputObject
Accepts pipeline input from Get-DbaDbMail
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: DatabaseMail, DBMail, Mail
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbMailProfile
.EXAMPLE
PS C:\> Get-DbaDbMailProfile -SqlInstance sql01\sharepoint
Returns DBMail profiles on sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMailProfile -SqlInstance sql01\sharepoint -Profile 'The DBA Team'
Returns The DBA Team DBMail profile from sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMailProfile -SqlInstance sql01\sharepoint | Select *
Returns the DBMail profiles on sql01\sharepoint then return a bunch more columns
.EXAMPLE
PS C:\> $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
PS C:\> $servers | Get-DbaDbMail | Get-DbaDbMailProfile
Returns the DBMail profiles for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[string[]]$Profile,
[string[]]$ExcludeProfile,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Mail.SqlMail[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDbMail -SqlInstance $SqlInstance -SqlCredential $SqlCredential
}
if (-not $InputObject) {
Stop-Function -Message "No servers to process"
return
}
foreach ($mailserver in $InputObject) {
try {
$profiles = $mailserver.Profiles
if ($Profile) {
$profiles = $profiles | Where-Object Name -in $Profile
}
If ($ExcludeProfile) {
$profiles = $profiles | Where-Object Name -notin $ExcludeProfile
}
$profiles | Add-Member -Force -MemberType NoteProperty -Name ComputerName -value $mailserver.ComputerName
$profiles | Add-Member -Force -MemberType NoteProperty -Name InstanceName -value $mailserver.InstanceName
$profiles | Add-Member -Force -MemberType NoteProperty -Name SqlInstance -value $mailserver.SqlInstance
$profiles | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, ID, Name, Description, ForceDeleteForActiveProfiles, IsBusyProfile
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
function Get-DbaDbMailServer {
<#
.SYNOPSIS
Gets database mail servers from SQL Server
.DESCRIPTION
Gets database mail servers from SQL Server
.PARAMETER SqlInstance
TThe 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 Server
Specifies one or more server(s) to get. If unspecified, all servers will be returned.
.PARAMETER Account
Get only the mail server associated with specific accounts
.PARAMETER InputObject
Accepts pipeline input from Get-DbaDbMail
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: DatabaseMail, DBMail, Mail
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbMailServer
.EXAMPLE
PS C:\> Get-DbaDbMailServer -SqlInstance sql01\sharepoint
Returns all DBMail servers on sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMailServer -SqlInstance sql01\sharepoint -Server DbaTeam
Returns The DBA Team DBMail server from sql01\sharepoint
.EXAMPLE
PS C:\> Get-DbaDbMailServer -SqlInstance sql01\sharepoint | Select *
Returns the DBMail servers on sql01\sharepoint then return a bunch more columns
.EXAMPLE
PS C:\> $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
PS C:\> $servers | Get-DbaDbMail | Get-DbaDbMailServer
Returns the DBMail servers for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[Alias("Name")]
[string[]]$Server,
[string[]]$Account,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Mail.SqlMail[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDbMail -SqlInstance $SqlInstance -SqlCredential $SqlCredential
}
if (-not $InputObject) {
Stop-Function -Message "No servers to process"
return
}
foreach ($mailserver in $InputObject) {
try {
$accounts = $mailserver | Get-DbaDbMailAccount -Account $Account
$servers = $accounts.MailServers
if ($Server) {
$servers = $servers | Where-Object Name -in $Server
}
if ($servers) {
$servers | Add-Member -Force -MemberType NoteProperty -Name ComputerName -value $mailserver.ComputerName
$servers | Add-Member -Force -MemberType NoteProperty -Name InstanceName -value $mailserver.InstanceName
$servers | Add-Member -Force -MemberType NoteProperty -Name SqlInstance -value $mailserver.SqlInstance
$servers | Add-Member -Force -MemberType NoteProperty -Name Account -value $servers[0].Parent.Name
$servers | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Account, Name, Port, EnableSsl, ServerType, UserName, UseDefaultCredentials, NoCredentialChange
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaDbMasterKey {
<#
.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 InputObject
Database object piped in from Get-DbaDatabase
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbMasterKey -SqlInstance sql2016
Gets all master database keys
.EXAMPLE
PS C:\> Get-DbaDbMasterKey -SqlInstance Server1 -Database db1
Gets the master key for the db1 database
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[string[]]$ExcludeDatabase,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
process {
if ($SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -Database $Database -ExcludeDatabase $ExcludeDatabase
}
foreach ($db in $InputObject) {
if (!$db.IsAccessible) {
Write-Message -Level Warning -Message "Database $db on $($db.Parent) 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 $db.Parent.ComputerName
Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name InstanceName -value $db.Parent.ServiceName
Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name SqlInstance -value $db.Parent.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
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDatabaseMasterKey
}
}
function Get-DbaDbMemoryUsage {
<#
.SYNOPSIS
Determine buffer pool usage by database.
.DESCRIPTION
This command can be utilized to determine which databases on a given instance are consuming buffer pool memory.
This command is based on query provided by Aaron Bertrand.
Reference: https://www.mssqltips.com/sqlservertip/2393/determine-sql-server-memory-use-by-database-and-object/
.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.
.PARAMETER IncludeSystemDb
Switch to have the output include system database memory consumption.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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, Database
Author: Shawn Melton (@wsmelton), https://wsmelton.github.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbMemoryUsage
.EXAMPLE
PS C:\> Get-DbaDbMemoryUsage -SqlInstance sqlserver2014a
Returns the buffer pool consumption for all user databases
.EXAMPLE
PS C:\> Get-DbaDbMemoryUsage -SqlInstance sqlserver2014a -IncludeSystemDb
Returns the buffer pool consumption for all user databases and system databases
.EXAMPLE
PS C:\> Get-DbaDbMemoryUsage -SqlInstance sql1 -IncludeSystemDb -Database tempdb
Returns the buffer pool consumption for tempdb database only
.EXAMPLE
PS C:\> Get-DbaDbMemoryUsage -SqlInstance sql2 -IncludeSystemDb -Exclude 'master','model','msdb','ResourceDb'
Returns the buffer pool consumption for all user databases and tempdb database
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(ValueFromPipelineByPropertyName = $true)]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[switch]$IncludeSystemDb,
[switch]$EnableException
)
begin {
$sql = "DECLARE @total_buffer INT;
SELECT @total_buffer = cntr_value
FROM sys.dm_os_performance_counters
WHERE RTRIM([object_name]) LIKE '%Buffer Manager'
AND counter_name = 'Database Pages';
;WITH src AS (
SELECT database_id, page_type, db_buffer_pages = COUNT_BIG(*)
FROM sys.dm_os_buffer_descriptors
GROUP BY database_id, page_type
)
SELECT [DatabaseName] = CASE [database_id] WHEN 32767 THEN 'ResourceDb' ELSE DB_NAME([database_id]) END,
page_type AS 'PageType',
db_buffer_pages AS 'PageCount',
(db_buffer_pages * 8)/1024 AS 'SizeMb',
CAST(db_buffer_pages * 100.0 / @total_buffer AS FLOAT) AS 'PercentUsed'
FROM src
ORDER BY [DatabaseName];"
}
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
}
try {
$results = $server.Query($sql)
} catch {
Stop-Function -Message "Issue collecting data" -Target $instance -ErrorRecord $_
}
foreach ($row in $results) {
if (Test-Bound 'Database') {
if ($row.DatabaseName -notin $Database) { continue }
}
if (Test-Bound 'ExcludeDatabase') {
if ($row.DatabaseName -in $ExcludeDatabase) { continue }
}
if (Test-Bound -Not 'IncludeSystemDb') {
if ($row.DatabaseName -in 'master', 'model', 'msdb', 'tempdb', 'ResourceDb') { continue }
}
if ($row.PercentUsed -is [System.DBNull]) {
$percentUsed = 0
} else {
$percentUsed = [Math]::Round($row.PercentUsed)
}
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Database = $row.DatabaseName
PageType = $row.PageType
PageCount = [int]$row.PageCount
Size = [DbaSize]$row.SizeMb * 1024
PercentUsed = $percentUsed
} | Select-DefaultView -ExcludeProperty 'PageCount'
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaDbMirror {
<#
.SYNOPSIS
Gets properties of database mirrors and mirror witnesses.
.DESCRIPTION
Gets properties of database mirrors and mirror witnesses.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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: Mirror, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbMirror
.EXAMPLE
PS C:\> Get-DbaDbMirror -SqlInstance localhost
Gets properties of database mirrors and mirror witnesses on localhost
.EXAMPLE
PS C:\> Get-DbaDbMirror -SqlInstance localhost, sql2016
Gets properties of database mirrors and mirror witnesses on localhost and sql2016 SQL Server instances
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$dbs = Get-DbaDatabase -SqlInstance $instance -SqlCredential $SqlCredential
$partners = $dbs | Where-Object MirroringPartner
$partners | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Name, MirroringSafetyLevel, MirroringStatus, MirroringPartner, MirroringPartnerInstance, MirroringFailoverLogSequenceNumber, MirroringID, MirroringRedoQueueMaxSize, MirroringRoleSequence, MirroringSafetySequence, MirroringTimeout, MirroringWitness, MirroringWitnessStatus
# The witness is kinda hidden. Go get it manually.
try {
$witnesses = $dbs[0].Parent.Query("select distinct database_name, principal_server_name, safety_level, safety_level_desc, partner_sync_state from master.sys.database_mirroring_witnesses")
} catch { continue }
foreach ($witness in $witnesses) {
$witnessdb = $dbs | Where-Object Name -eq $witness.database_name
$status = switch ($witness.partner_sync_state) {
0 { "None" }
1 { "Suspended" }
2 { "Disconnected" }
3 { "Synchronizing" }
4 { "PendingFailover" }
5 { "Synchronized" }
}
foreach ($db in $witnessdb) {
Add-Member -InputObject $db -Force -MemberType NoteProperty -Name MirroringPartner -Value $witness.principal_server_name
Add-Member -InputObject $db -Force -MemberType NoteProperty -Name MirroringSafetyLevel -Value $witness.safety_level_desc
Add-Member -InputObject $db -Force -MemberType NoteProperty -Name MirroringWitnessStatus -Value $status
Select-DefaultView -InputObject $db -Property ComputerName, InstanceName, SqlInstance, Name, MirroringSafetyLevel, MirroringStatus, MirroringPartner, MirroringPartnerInstance, MirroringFailoverLogSequenceNumber, MirroringID, MirroringRedoQueueMaxSize, MirroringRoleSequence, MirroringSafetySequence, MirroringTimeout, MirroringWitness, MirroringWitnessStatus
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaDbMirrorMonitor {
<#
.SYNOPSIS
Returns status rows for a monitored database from the status table in which database mirroring monitoring history is stored and allows you to choose whether the procedure obtains the latest status beforehand.
.DESCRIPTION
Returns status rows for a monitored database from the status table in which database mirroring monitoring history is stored and allows you to choose whether the procedure obtains the latest status beforehand.
Basically executes sp_dbmmonitorresults.
.PARAMETER SqlInstance
The target SQL Server instance
.PARAMETER SqlCredential
Login to the target instance using alternate Windows or SQL Login Authentication. Accepts credential objects (Get-Credential).
.PARAMETER Database
The target database.
.PARAMETER Database
The target database.
.PARAMETER Update
Updates the status for the database by calling sp_dbmmonitorupdate before computing the results.
However, if the status table has been updated within the previous 15 seconds, or the user is not a member of the sysadmin fixed server role, the command runs without updating the status.
.PARAMETER LimitResults
Limit results. Defaults to last two hours.
Options include:
LastRow
LastTwoHours
LastFourHours
LastEightHours
LastDay
LastTwoDays
Last100Rows
Last500Rows
Last1000Rows
Last1000000Rows
.PARAMETER InputObject
Allows 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: Mirror, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbMirrorMonitor
.EXAMPLE
PS C:\> Get-DbaDbMirrorMonitor -SqlInstance sql2008, sql2012
Returns last two hours' worth of status rows for a monitored database from the status table on sql2008 and sql2012.
.EXAMPLE
PS C:\> Get-DbaDbMirrorMonitor -SqlInstance sql2005 -LimitResults LastDay -Update
Updates monitor stats then returns the last 24 hours worth of status rows for a monitored database from the status table on sql2008 and sql2012.
#>
[CmdletBinding()]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$Update,
[ValidateSet('LastRow', 'LastTwoHours', 'LastFourHours', 'LastEightHours', 'LastDay', 'LastTwoDays', 'Last100Rows', 'Last500Rows', 'Last1000Rows', 'Last1000000Rows')]
[string]$LimitResults = 'LastTwoHours',
[switch]$EnableException
)
begin {
$rows = switch ($LimitResults) {
'LastRow' { 0 }
'LastTwoHours' { 1 }
'LastFourHours' { 2 }
'LastEightHours' { 3 }
'LastDay' { 4 }
'LastTwoDays' { 5 }
'Last100Rows' { 6 }
'Last500Rows' { 7 }
'Last1000000Rows' { 8 }
}
$updatebool = switch ($Update) {
$false { 0 }
$true { 1 }
}
}
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $instance -SqlCredential $SqlCredential -Database $Database
}
foreach ($db in $InputObject) {
if (-not ($db.Parent.Databases['msdb'].Tables['dbm_monitor_data'].Name)) {
Stop-Function -Continue -Message "mdbo.dbo.dbm_monitor_data not found. Please run Add-DbaDbMirrorMonitor then you can get monitor stats."
}
try {
$sql = "msdb.dbo.sp_dbmmonitorresults $db, $rows, $updatebool"
$results = $db.Parent.Query($sql)
foreach ($result in $results) {
[pscustomobject]@{
ComputerName = $db.Parent.ComputerName
InstanceName = $db.Parent.ServiceName
SqlInstance = $db.Parent.DomainInstanceName
DatabaseName = $result.database_name
Role = $result.role
MirroringState = $result.mirroring_state
WitnessStatus = $result.witness_status
LogGenerationRate = $result.log_generation_rate
UnsentLog = $result.unsent_log
SendRate = $result.send_rate
UnrestoredLog = $result.unrestored_log
RecoveryRate = $result.recovery_rate
TransactionDelay = $result.transaction_delay
TransactionsPerSecond = $result.transactions_per_sec
AverageDelay = $result.average_delay
TimeRecorded = $result.time_recorded
TimeBehind = $result.time_behind
LocalTime = $result.local_time
}
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
}
}
}
}
#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 or instances
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbPageInfo
.EXAMPLE
PS C:\> et-DbaDbPageInfo -SqlInstance sql2017
Returns page information for all databases on sql2017
.EXAMPLE
PS C:\> Get-DbaDbPageInfo -SqlInstance sql2017, sql2016 -Database testdb
Returns page information for the testdb on sql2017 and sql2016
.EXAMPLE
PS C:\> $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
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
}
}
}
}
function Get-DbaDbPartitionFunction {
<#
.SYNOPSIS
Gets database Partition Functions
.DESCRIPTION
Gets database Partition Functions
.PARAMETER SqlInstance
The target SQL Server instance or instances
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbPartitionFunction -SqlInstance sql2016
Gets all database Partition Functions
.EXAMPLE
PS C:\> Get-DbaDbPartitionFunction -SqlInstance Server1 -Database db1
Gets the Partition Functions for the db1 database
.EXAMPLE
PS C:\> Get-DbaDbPartitionFunction -SqlInstance Server1 -ExcludeDatabase db1
Gets the Partition Functions for all databases except db1
.EXAMPLE
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaDbPartitionFunction
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 {
$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-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, CreateDate, Name, NumberOfPartitions
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDatabasePartitionFunction
}
}
function Get-DbaDbPartitionScheme {
<#
.SYNOPSIS
Gets database Partition Schemes
.DESCRIPTION
Gets database Partition Schemes
.PARAMETER SqlInstance
The target SQL Server instance or instances
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbPartitionScheme -SqlInstance sql2016
Gets all database Partition Schemes
.EXAMPLE
PS C:\> Get-DbaDbPartitionScheme -SqlInstance Server1 -Database db1
Gets the Partition Schemes for the db1 database
.EXAMPLE
PS C:\> Get-DbaDbPartitionScheme -SqlInstance Server1 -ExcludeDatabase db1
Gets the Partition Schemes for all databases except db1
.EXAMPLE
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaDbPartitionScheme
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 {
$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-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, Name, PartitionFunction
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDatabasePartitionScheme
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaDbQueryStoreOption {
<#
.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 target SQL Server instance or 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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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) | Klaas Vandenberghe (@PowerDBAKlaas)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaQueryStoreOptions
.EXAMPLE
PS C:\> Get-DbaDbQueryStoreOption -SqlInstance ServerA\sql
Returns Query Store configuration settings for every database on the ServerA\sql instance.
.EXAMPLE
PS C:\> Get-DbaDbQueryStoreOption -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
PS C:\> Get-DbaDbQueryStoreOption -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, ValueFromPipeline)]
[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) {
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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDbQueryStoreOptions
}
}
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 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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbRecoveryModel
.EXAMPLE
PS C:\> Get-DbaDbRecoveryModel -SqlInstance sql2014 -RecoveryModel BulkLogged -Verbose
Gets all databases on SQL Server instance sql2014 having RecoveryModel set to BulkLogged.
.EXAMPLE
PS C:\> Get-DbaDbRecoveryModel -SqlInstance sql2014 -Database TestDB
Gets recovery model information for TestDB. If TestDB does not exist on the instance nothing is returned.
#>
[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
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaDbRestoreHistory {
<#
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbRestoreHistory
.EXAMPLE
PS C:\> Get-DbaDbRestoreHistory -SqlInstance sql2016
Returns server name, database, username, restore type, date for all restored databases on sql2016.
.EXAMPLE
PS C:\> Get-DbaDbRestoreHistory -SqlInstance sql2016 -Database db1, db2 -Since '2016-07-01 10:47:00'
Returns restore information only for databases db1 and db2 on sql2016 since July 1, 2016 at 10:47 AM.
.EXAMPLE
PS C:\> Get-DbaDbRestoreHistory -SqlInstance sql2014, sql2016 -Exclude db1
Returns restore information for all databases except db1 on sql2014 and sql2016.
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Get-DbaDbRestoreHistory -SqlInstance sql2014 -Database AdventureWorks2014, pubs -SqlCredential $cred | Format-Table
Returns database restore information for AdventureWorks2014 and pubs database on sql2014, connects using SQL Authentication via sqladmin account. Formats the data as a table.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sql2016 | Get-DbaDbRestoreHistory
Returns database restore 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,
[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
}
}
}
}
#ValidationTags#CodeStyle, Messaging, FlowControl, Pipeline#
function Get-DbaDbRoleMember {
<#
.SYNOPSIS
Get members of database roles for each instance(s) of SQL Server.
.DESCRIPTION
The Get-DbaDbRoleMember returns connected SMO object for database roles for each instance(s) of SQL Server.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 alternate Windows or SQL Login Authentication. 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 Role
The role(s) to process. If unspecified, all roles will be processed.
.PARAMETER ExcludeRole
The role(s) to exclude.
.PARAMETER ExcludeFixedRole
Excludes all members of fixed roles.
.PARAMETER IncludeSystemUser
Includes system users. By default system users are not 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: Role, Database, Security, Login
Author: Klaas Vandenberghe (@PowerDBAKlaas)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbRoleMember
.EXAMPLE
PS C:\> Get-DbaDbRoleMember -SqlInstance localhost
Returns all members of all database roles on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaDbRoleMember -SqlInstance localhost, sql2016
Returns all members of all database roles on the local and sql2016 SQL Server instances
.EXAMPLE
PS C:\> $servers = Get-Content C:\servers.txt
PS C:\> $servers | Get-DbaDbRoleMember
Returns all members of all database roles for every server in C:\servers.txt
.EXAMPLE
PS C:\> Get-DbaDbRoleMember -SqlInstance localhost -Database msdb
Returns non-system members of all roles in the msdb database on localhost.
.EXAMPLE
PS C:\> Get-DbaDbRoleMember -SqlInstance localhost -Database msdb -IncludeSystemUser -ExcludeFixedRole
Returns all members of non-fixed roles in the msdb database on localhost.
.EXAMPLE
PS C:\> Get-DbaDbRoleMember -SqlInstance localhost -Database msdb -Role 'db_owner'
Returns all members of the db_owner role in the msdb database on localhost.
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstance[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[string[]]$Database,
[string[]]$ExcludeDatabase,
[string[]]$Role,
[string[]]$ExcludeRole,
[switch]$ExcludeFixedRole,
[switch]$IncludeSystemUser,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
Write-Message -Level Verbose -Message "Attempting to connect to $instance"
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 -eq $true }
if (Test-Bound -Parameter 'Database') {
$databases = $databases | Where-Object { $_.Name -in $Database }
}
if (Test-Bound -Parameter 'ExcludeDatabase') {
$databases = $databases | Where-Object { $_.Name -notin $ExcludeDatabase}
}
foreach ($db in $databases) {
Write-Message -Level 'Verbose' -Message "Getting Database Roles for $db on $instance"
$dbRoles = $db.roles
if (Test-Bound -Parameter 'Role') {
$dbRoles = $dbRoles | Where-Object { $_.Name -in $Role }
}
if (Test-Bound -Parameter 'ExcludeRole') {
$dbRoles = $dbRoles | Where-Object { $_.Name -notin $ExcludeRole }
}
if (Test-Bound -Parameter 'ExcludeFixedRole') {
$dbRoles = $dbRoles | Where-Object { $_.IsFixedRole -eq $false }
}
foreach ($dbRole in $dbRoles) {
Write-Message -Level 'Verbose' -Message "Getting Database Role Members for $dbRole in $db on $instance"
$members = $dbRole.EnumMembers()
foreach ($member in $members) {
$user = $db.Users | Where-Object { $_.Name -eq $member }
if (Test-Bound -Not -ParameterName 'IncludeSystemUser') {
$user = $user | Where-Object { $_.IsSystemObject -eq $false }
}
if ($user) {
Add-Member -Force -InputObject $user -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
Add-Member -Force -InputObject $user -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
Add-Member -Force -InputObject $user -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
Add-Member -Force -InputObject $user -MemberType NoteProperty -Name Database -Value $db.Name
Add-Member -Force -InputObject $user -MemberType NoteProperty -Name Role -Value $dbRole.Name
Add-Member -Force -InputObject $user -MemberType NoteProperty -Name UserName -Value $user.Name
# Select object because Select-DefaultView causes strange behaviors when assigned to a variable (??)
Select-Object -InputObject $user -Property 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Role', 'UserName', 'Login', 'IsSystemObject'
}
}
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaRoleMember
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaDbSharePoint {
<#
.SYNOPSIS
Returns databases that are part of a SharePoint Farm.
.DESCRIPTION
Returns databases that are part of a SharePoint Farm, as found in the SharePoint Configuration database.
By default, this command checks SharePoint_Config. To use an alternate database, use the ConfigDatabase parameter.
.PARAMETER SqlInstance
The target SQL Server instance
.PARAMETER SqlCredential
Login to the target instance using alternate Windows or SQL Login Authentication. Accepts credential objects (Get-Credential).
.PARAMETER ConfigDatabase
The name of the SharePoint Configuration database. Defaults to SharePoint_Config.
.PARAMETER InputObject
Allows 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: SharePoint
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbSharePoint
.EXAMPLE
PS C:\> Get-DbaDbSharePoint -SqlInstance sqlcluster
Returns databases that are part of a SharePoint Farm, as found in SharePoint_Config on sqlcluster
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sqlcluster -Database SharePoint_Config_2016 | Get-DbaDbSharePoint
Returns databases that are part of a SharePoint Farm, as found in SharePoint_Config_2016 on sqlcluster
#>
[CmdletBinding()]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$ConfigDatabase = "SharePoint_Config",
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $instance -SqlCredential $SqlCredential -Database $ConfigDatabase
}
foreach ($db in $InputObject) {
try {
$guid = $db.Query("SELECT Id FROM Classes WHERE FullName LIKE 'Microsoft.SharePoint.Administration.SPDatabase,%'").Id.Guid
$dbid = $db.Query("[dbo].[proc_getObjectsByBaseClass] @BaseClassId = '$guid', @ParentId = NULL").Id.Guid -join "', '"
$dbname = $db.Query("SELECT [Name] FROM [SharePoint_Config].[dbo].[Objects] WHERE id in ('$dbid')").Name
Get-DbaDatabase -SqlInstance $db.Parent -Database $dbname
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
}
}
}
}
#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 target SQL Server instance or instances.
.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: Simone Bizzotto (@niphlod)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbSnapshot
.EXAMPLE
PS C:\> Get-DbaDbSnapshot -SqlInstance sqlserver2014a
Returns a custom object displaying Server, Database, DatabaseCreated, SnapshotOf, SizeMB, DatabaseCreated
.EXAMPLE
PS C:\> Get-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR, Accounting
Returns information for database snapshots having HR and Accounting as base dbs
.EXAMPLE
PS C:\> Get-DbaDbSnapshot -SqlInstance sqlserver2014a -Snapshot HR_snapshot, Accounting_snapshot
Returns information for database snapshots HR_snapshot and Accounting_snapshot
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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) {
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-DbaDbSpace {
<#
.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
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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbSpace
.EXAMPLE
PS C:\> Get-DbaDbSpace -SqlInstance localhost
Returns all user database files and free space information for the localhost.
.EXAMPLE
PS C:\> Get-DbaDbSpace -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
PS C:\> 'localhost','localhost\namedinstance' | Get-DbaDbSpace
Returns all user database files and free space information for the localhost and localhost\namedinstance SQL Server instances. Processes data via the pipeline.
.EXAMPLE
PS C:\> Get-DbaDbSpace -SqlInstance localhost -Database db1, db2 | Where-Object { $_.SpaceUntilMaxSize.Megabyte -lt 1 }
Returns database files and free space information for the db1 and db2 on localhost where there is only 1MB left until the space is maxed out
.EXAMPLE
PS C:\> Get-DbaDbSpace -SqlInstance localhost -Database db1, db2 | Where-Object { $_.SpaceUntilMaxSize.Gigabyte -lt 1 }
Returns database files and free space information for the db1 and db2 on localhost where there is only 1GB left until the space is maxed out
#>
[CmdletBinding()]
param ([parameter(ValueFromPipeline, Mandatory)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[System.Management.Automation.PSCredential]$SqlCredential,
[Alias("Databases")]
[string[]]$Database,
[string[]]$ExcludeDatabase,
[switch]$IncludeSystemDBs,
[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 {
$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
UsedSpace = [dbasize]($UsedMB * 1024 * 1024)
FreeSpace = [dbasize]($FreeMB * 1024 * 1024)
FileSize = [dbasize]($row.FileSizeMB * 1024 * 1024)
PercentUsed = $PercentUsed
AutoGrowth = [dbasize]($row.GrowthMB * 1024 * 1024)
AutoGrowType = $row.GrowthType
SpaceUntilMaxSize = [dbasize]($SpaceUntilMax * 1024 * 1024)
AutoGrowthPossible = [dbasize]($row.PossibleAutoGrowthMB * 1024 * 1024)
UnusableSpace = [dbasize]($UnusableSpace * 1024 * 1024)
}
}
} catch {
Stop-Function -Message "Unable to query $instance - $db." -Target $db -ErrorRecord $_ -Continue
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaDatabaseFreeSpace
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Get-DbaDatabaseSpace
}
}
#ValidationTags#Messaging,FlowControl,Pipeline#
function Get-DbaDbState {
<#
.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 target SQL Server instance or instances
.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: Simone Bizzotto (@niphold)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbState
.EXAMPLE
PS C:\> Get-DbaDbState -SqlInstance sqlserver2014a
Gets options for all databases of the sqlserver2014a instance
.EXAMPLE
PS C:\> Get-DbaDbState -SqlInstance sqlserver2014a -Database HR, Accounting
Gets options for both HR and Accounting database of the sqlserver2014a instance
.EXAMPLE
PS C:\> Get-DbaDbState -SqlInstance sqlserver2014a -Exclude HR
Gets options for all databases of the sqlserver2014a instance except HR
.EXAMPLE
PS C:\> 'sqlserver2014a', 'sqlserver2014b' | Get-DbaDbState
Gets options for all databases of sqlserver2014a and sqlserver2014b instances
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseLiteralInitializerForHashtable", "")]
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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) {
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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDatabaseState
}
}
function Get-DbaDbStoredProcedure {
<#
.SYNOPSIS
Gets database Stored Procedures
.DESCRIPTION
Gets database Stored Procedures
.PARAMETER SqlInstance
The target SQL Server instance or instances
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbStoredProcedure -SqlInstance sql2016
Gets all database Stored Procedures
.EXAMPLE
PS C:\> Get-DbaDbStoredProcedure -SqlInstance Server1 -Database db1
Gets the Stored Procedures for the db1 database
.EXAMPLE
PS C:\> Get-DbaDbStoredProcedure -SqlInstance Server1 -ExcludeDatabase db1
Gets the Stored Procedures for all databases except db1
.EXAMPLE
PS C:\> Get-DbaDbStoredProcedure -SqlInstance Server1 -ExcludeSystemSp
Gets the Stored Procedures for all databases that are not system objects
.EXAMPLE
PS C:\> '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 {
$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
}
}
}
}
}
function Get-DbaDbTable {
<#
.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
The target SQL Server instance or instances. 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 [ ].
Using 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 by passing dbo.[First.Table]
Any actual usage of the ] must be escaped by duplicating the ] character.
The correct way to find a table Name] in schema Schema.Name is by passing [Schema.Name].[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: Database, Tables
Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbTable
.EXAMPLE
PS C:\> Get-DbaDbTable -SqlInstance DEV01 -Database Test1
Return all tables in the Test1 database
.EXAMPLE
PS C:\> Get-DbaDbTable -SqlInstance DEV01 -Database MyDB -Table MyTable
Return only information on the table MyTable from the database MyDB
.EXAMPLE
PS C:\> Get-DbaDbTable -SqlInstance DEV01 -Table MyTable
Returns information on table called MyTable if it exists in any database on the server, under any schema
.EXAMPLE
PS C:\> Get-DbaDbTable -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
PS C:\> 'localhost','localhost\namedinstance' | Get-DbaDbTable -Database DBA -Table Commandlog
Returns information on the CommandLog table in the DBA database on both instances localhost and the named instance localhost\namedinstance
.EXAMPLE
PS C:\> Get-DbaDbTable -SqlInstance DEV01 -Table "[[DbName]]].[Schema.With.Dots].[`"[Process]]`"]" -Verbose
For the instance Dev01 Returns information for a table named: "[Process]" in schema named: Schema.With.Dots in database named: [DbName]
The Table name, Schema name and Database name must be wrapped in square brackets [ ]
Special charcters like " must be escaped by a ` charcter.
In addition any actual instance of the ] character must be escaped by being duplicated.
#>
[CmdletBinding()]
param ([parameter(ValueFromPipeline, Mandatory)]
[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) {
$fqtn = Get-TableNameParts -Table $t
if (!$fqtn.Parsed) {
Write-Message -Level Warning -Message "Please check you are using proper three-part names. If your search value contains special characters you must use [ ] to wrap the name. The value $t could not be parsed as a valid name."
Continue
}
$fqtns += [PSCustomObject] @{
Database = $fqtn.Database
Schema = $fqtn.Schema
Table = $fqtn.Table
InputValue = $fqtn.InputValue
}
}
if (!$fqtns) {
Stop-Function -Message "No Valid Table specified" -ErrorRecord $_ -Target $instance -Continue
}
}
}
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 {
#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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaTable
}
}
function Get-DbaDbTrigger {
<#
.SYNOPSIS
Get all existing database triggers on one or more SQL instances.
.DESCRIPTION
Get all existing database triggers on one or more SQL instances.
.PARAMETER SqlInstance
The target SQL Server instance or 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 InputObject
Allow pipedline input 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/ca
.NOTES
Tags: Database, Trigger
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbTrigger
.EXAMPLE
PS C:\> Get-DbaDbTrigger -SqlInstance sql2017
Returns all database triggers
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2017 -Database supa | Get-DbaDbTrigger
Returns all triggers for database supa on sql2017
.EXAMPLE
PS C:\> Get-DbaDbTrigger -SqlInstance sql2017 -Database supa
Returns all triggers for database supa on sql2017
#>
[CmdletBinding()]
param (
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[object[]]$Database,
[object[]]$ExcludeDatabase,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($Instance in $SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase
}
foreach ($db in $InputObject) {
try {
foreach ($trigger in ($db.Triggers)) {
Add-Member -Force -InputObject $trigger -MemberType NoteProperty -Name ComputerName -value $db.Parent.ComputerName
Add-Member -Force -InputObject $trigger -MemberType NoteProperty -Name InstanceName -value $db.Parent.ServiceName
Add-Member -Force -InputObject $trigger -MemberType NoteProperty -Name SqlInstance -value $db.Parent.DomainInstanceName
Select-DefaultView -InputObject $trigger -Property ComputerName, InstanceName, SqlInstance, Name, IsEnabled, DateLastModified
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
function Get-DbaDbUdf {
<#
.SYNOPSIS
Gets database User Defined Functions
.DESCRIPTION
Gets database User Defined Functions
.PARAMETER SqlInstance
The target SQL Server instance or instances
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbUdf -SqlInstance sql2016
Gets all database User Defined Functions
.EXAMPLE
PS C:\> Get-DbaDbUdf -SqlInstance Server1 -Database db1
Gets the User Defined Functions for the db1 database
.EXAMPLE
PS C:\> Get-DbaDbUdf -SqlInstance Server1 -ExcludeDatabase db1
Gets the User Defined Functions for all databases except db1
.EXAMPLE
PS C:\> Get-DbaDbUdf -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
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaDbUdf
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 {
$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-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, DataType
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDatabaseUdf
}
}
function Get-DbaDbUser {
<#
.SYNOPSIS
Gets database users
.DESCRIPTION
Gets database users
.PARAMETER SqlInstance
The target SQL Server instance or instances
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbUser -SqlInstance sql2016
Gets all database users
.EXAMPLE
PS C:\> Get-DbaDbUser -SqlInstance Server1 -Database db1
Gets the users for the db1 database
.EXAMPLE
PS C:\> Get-DbaDbUser -SqlInstance Server1 -ExcludeDatabase db1
Gets the users for all databases except db1
.EXAMPLE
PS C:\> Get-DbaDbUser -SqlInstance Server1 -ExcludeSystemUser
Gets the users for all databases that are not system objects, like 'dbo', 'guest' or 'INFORMATION_SCHEMA'
.EXAMPLE
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaDbUser
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 {
$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-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, CreateDate, DateLastModified, Name, Login, LoginType, AuthenticationType, State, HasDbAccess, DefaultSchema
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDatabaseUser
}
}
function Get-DbaDbView {
<#
.SYNOPSIS
Gets database views for each SqlInstance.
.DESCRIPTION
Gets database views for each SqlInstance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaDbView -SqlInstance sql2016
Gets all database views
.EXAMPLE
PS C:\> Get-DbaDbView -SqlInstance Server1 -Database db1
Gets the views for the db1 database
.EXAMPLE
PS C:\> Get-DbaDbView -SqlInstance Server1 -ExcludeDatabase db1
Gets the views for all databases except db1
.EXAMPLE
PS C:\> Get-DbaDbView -SqlInstance Server1 -ExcludeSystemView
Gets the views for all databases that are not system objects (there can be 400+ system views in each DB)
.EXAMPLE
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaDbView
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 {
$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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDatabaseView
}
}
#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
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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDbVirtualLogFile
.EXAMPLE
PS C:\> Get-DbaDbVirtualLogFile -SqlInstance sqlcluster
Returns all user database virtual log file details for the sqlcluster instance.
.EXAMPLE
PS C:\> Get-DbaDbVirtualLogFile -SqlInstance sqlserver | Group-Object -Property Database | Where-Object Count -gt 50
Returns user databases that have 50 or more VLFs.
.EXAMPLE
PS C:\> 'sqlserver','sqlcluster' | Get-DbaDbVirtualLogFile
Returns all VLF information for the sqlserver and sqlcluster SQL Server instances. Processes data via the pipeline.
.EXAMPLE
PS C:\> 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)]
[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 {
$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
TThe target 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDefaultPath
.EXAMPLE
PS C:\> Get-DbaDefaultPath -SqlInstance sql01\sharepoint
Returns the default file paths for sql01\sharepoint
.EXAMPLE
PS C:\> $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
PS C:\> $servers | Get-DbaDefaultPath
Returns the default file paths for "sql2014","sql2016" and "sqlcluster\sharepoint"
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "Instance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]
$SqlCredential,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
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.Query("SELECT SERVERPROPERTY('InstanceDefaultdataPath') as Data").Data
}
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.Query("SELECT SERVERPROPERTY('InstanceDefaultLogPath') as Log").Log
}
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDependency
.EXAMPLE
PS C:\> $table = (Get-DbaDatabase -SqlInstance sql2012 -Database Northwind).tables | Where Name -eq Customers
PS C:\> $table | Get-DbaDependency
Returns everything that depends on the "Customers" table
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
$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)]
$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)]
$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-DbaDeprecatedFeature {
<#
.SYNOPSIS
Displays information relating to deprecated features for SQL Server 2005 and above.
.DESCRIPTION
Displays information relating to deprecated features for SQL Server 2005 and above.
.PARAMETER SqlInstance
The target SQL Server instance
.PARAMETER SqlCredential
Login to the target instance using alternate Windows or SQL Login Authentication. 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: Deprecated
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDeprecatedFeature
.EXAMPLE
PS C:\> Get-DbaDeprecatedFeature -SqlInstance sql2008, sqlserver2012
Check deprecated features for all databases on the servers sql2008 and sqlserver2012.
.EXAMPLE
PS C:\> Get-DbaDeprecatedFeature -SqlInstance sql2008
Check deprecated features on server sql2008.
#>
[CmdletBinding()]
param (
[parameter(Position = 0, 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, object_name, instance_name as DeprecatedFeature, object_name as ObjectName, instance_name as deprecated_feature, cntr_value as UsageCount
FROM sys.dm_os_performance_counters WHERE object_name like '%Deprecated%'
and cntr_value > 0 ORDER BY deprecated_feature"
}
process {
foreach ($instance in $SqlInstance) {
Write-Message -Level Verbose -Message "Attempting to connect to $instance"
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
try {
$server.Query($sql) | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, ObjectName, DeprecatedFeature, UsageCount
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance -Continue
}
}
}
}
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDetachedDatabaseInfo
.EXAMPLE
PS C:\> 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)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter]$SqlInstance,
[parameter(Mandatory)]
[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-DbaPath -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 (@cl), netnerds.net | Jakob Bindslet
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDiskSpace
.EXAMPLE
PS C:\> Get-DbaDiskSpace -ComputerName srv0042
Get disk space for the server srv0042.
.EXAMPLE
PS C:\> Get-DbaDiskSpace -ComputerName srv0042 -Unit MB
Get disk space for the server srv0042 and displays in megabytes (MB).
.EXAMPLE
PS C:\> Get-DbaDiskSpace -ComputerName srv0042, srv0007 -Unit TB
Get disk space from two servers and displays in terabytes (TB).
.EXAMPLE
PS C:\> Get-DbaDiskSpace -ComputerName srv0042 -Force
Get all disk and volume space information.
.EXAMPLE
PS C:\> Get-DbaDiskSpace -ComputerName srv0042 -ExcludeDrive 'C:\'
Get all disk and volume space information.
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[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)
} 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-DbaService -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-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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaDump
.EXAMPLE
PS C:\> Get-DbaDump -SqlInstance sql2016
Shows the detailed information for memory dump(s) located on sql2016 instance
.EXAMPLE
PS C:\> Get-DbaDump -SqlInstance sql2016 -SqlCredential 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, ValueFromPipeline)]
[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,Pipeline,CodeStyle#
function Get-DbaEndpoint {
<#
.SYNOPSIS
Returns endpoint objects from a SQL Server instance.
.DESCRIPTION
Returns endpoint objects from a SQL Server instance.
.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 Endpoint
Return only specific endpoints.
.PARAMETER Type
Return only specific types of endpoints. Options include: DatabaseMirroring, ServiceBroker, Soap, and TSql.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaEndpoint
.EXAMPLE
PS C:\> Get-DbaEndpoint -SqlInstance localhost
Returns all endpoints on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaEndpoint -SqlInstance localhost, sql2016
Returns all endpoints for the local and sql2016 SQL Server instances
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Endpoint,
[ValidateSet('DatabaseMirroring', 'ServiceBroker', 'Soap', 'TSql')]
[string[]]$Type,
[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
}
# Not sure why minimumversion isnt working
if ($server.VersionMajor -lt 9) {
Stop-Function -Message "SQL Server version 9 required - $instance not supported." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$endpoints = $server.Endpoints
if ($endpoint) {
$endpoints = $endpoints | Where-Object Name -in $endpoint
}
if ($Type) {
$endpoints = $endpoints | Where-Object EndpointType -in $Type
}
foreach ($end in $endpoints) {
Write-Message -Level Verbose -Message "Getting endpoint $($end.Name) on $($server.Name)"
if ($end.Protocol.Tcp.ListenerPort) {
if ($instance.ComputerName -match '\.') {
$dns = $instance.ComputerName
} else {
try {
$dns = [System.Net.Dns]::GetHostEntry($instance.ComputerName).HostName
} catch {
try {
$dns = [System.Net.Dns]::GetHostAddresses($instance.ComputerName)
} catch {
$dns = $instance.ComputerName
}
}
}
$fqdn = "TCP://" + $dns + ":" + $end.Protocol.Tcp.ListenerPort
} else {
$fqdn = $null
}
Add-Member -Force -InputObject $end -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
Add-Member -Force -InputObject $end -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
Add-Member -Force -InputObject $end -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
Add-Member -Force -InputObject $end -MemberType NoteProperty -Name Fqdn -Value $fqdn
Add-Member -Force -InputObject $end -MemberType NoteProperty -Name Port -Value $end.Protocol.Tcp.ListenerPort
if ($end.Protocol.Tcp.ListenerPort) {
Select-DefaultView -InputObject $end -Property ComputerName, InstanceName, SqlInstance, ID, Name, Port, EndpointState, EndpointType, Owner, IsAdminEndpoint, Fqdn, IsSystemObject
} else {
Select-DefaultView -InputObject $end -Property ComputerName, InstanceName, SqlInstance, ID, Name, EndpointState, EndpointType, Owner, IsAdminEndpoint, Fqdn, 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
TThe target 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaErrorLog
.EXAMPLE
PS C:\> Get-DbaErrorLog -SqlInstance sql01\sharepoint
Returns every log entry from sql01\sharepoint SQL Server instance.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaErrorLog -SqlInstance sql01\sharepoint -Source Logon
Returns every log entry, with a source of Logon, from sql01\sharepoint SQL Server instance.
.EXAMPLE
PS C:\> 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
PS C:\> $servers = "sql2014","sql2016", "sqlcluster\sharepoint"
PS C:\> $servers | Get-DbaErrorLog -LogNumber 0
Returns the most recent SQL Server error logs for "sql2014","sql2016" and "sqlcluster\sharepoint"
.EXAMPLE
PS C:\> Get-DbaErrorLog -SqlInstance sql01\sharepoint -After '2016-11-14 00:00:00'
Returns every log entry found after the date 14 November 2016 from sql101\sharepoint SQL Server instance.
.EXAMPLE
PS C:\> Get-DbaErrorLog -SqlInstance sql01\sharepoint -Before '2016-08-16 00:00:00'
Returns every log entry found before the date 16 August 2016 from sql101\sharepoint SQL Server instance.
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[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-DbaLog
}
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 ($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 or 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, ErrorLog
Author: Shawn Melton (@wsmelton), https://wsmelton.github.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaErrorLogConfig
.EXAMPLE
PS C:\> 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) {
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 target SQL Server instance or 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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaEstimatedCompletionTime
.EXAMPLE
PS C:\> Get-DbaEstimatedCompletionTime -SqlInstance sql2016
Gets estimated completion times for queries performed against the entire server
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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, ValueFromPipeline)]
[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) {
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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaExecutionPlan {
<#
.SYNOPSIS
Gets execution plans and metadata
.DESCRIPTION
Gets execution plans and metadata. Can pipe to Export-DbaExecutionPlan
Thanks to following for the queries:
https://www.simple-talk.com/sql/t-sql-programming/dmvs-for-query-plan-metadata/
http://www.scarydba.com/2017/02/13/export-plans-cache-sqlplan-file/
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.PARAMETER SqlCredential
Credential object used to connect to the SQL Server as a different user
.PARAMETER Database
Return execution plans and metadata for only specific databases.
.PARAMETER ExcludeDatabase
Return execution plans and metadata 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaExecutionPlan
.EXAMPLE
PS C:\> Get-DbaExecutionPlan -SqlInstance sqlserver2014a
Gets all execution plans on sqlserver2014a
.EXAMPLE
PS C:\> Get-DbaExecutionPlan -SqlInstance sqlserver2014a -Database db1, db2 -SinceLastExecution '2016-07-01 10:47:00'
Gets all execution plans for databases db1 and db2 on sqlserver2014a since July 1, 2016 at 10:47 AM.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaExecutionPlan -SqlInstance sql2014 -Database AdventureWorks2014, pubs -Force
Gets super detailed information for execution plans on only for AdventureWorks2014 and pubs
.EXAMPLE
PS C:\> $servers = "sqlserver2014a","sql2016t"
PS C:\> $servers | Get-DbaExecutionPlan -Force
Gets super detailed information for execution plans on sqlserver2014a and sql2016
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[object[]]$Database,
[object[]]$ExcludeDatabase,
[datetime]$SinceCreation,
[datetime]$SinceLastExecution,
[switch]$ExcludeEmptyQueryPlan,
[switch]$Force,
[switch]$EnableException
)
process {
foreach ($instance in $sqlinstance) {
try {
try {
$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 -or $SinceLastExecution -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.ToString("yyyy-MM-dd HH:mm:ss") + "' "
}
if ($null -ne $SinceLastExecution) {
Write-Message -Level Verbose -Message "Adding last exectuion time"
$wherearray += " last_execution_time >= '" + $SinceLastExecution.ToString("yyyy-MM-dd HH:mm:ss") + "' "
}
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
}
}
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaFeature {
<#
.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), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaFeature
.EXAMPLE
PS C:\> Get-DbaFeature -ComputerName sql2017, sql2016, sql2005
Gets all SQL Server features for all instances on sql2017, sql2016 and sql2005.
.EXAMPLE
PS C:\> Get-DbaFeature -Verbose
Gets all SQL Server features for all instances on localhost. Outputs to screen if no instances are found.
.EXAMPLE
PS C:\> Get-DbaFeature -ComputerName sql2017 -Credential ad\sqldba
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"
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaSqlFeature
}
}
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 target SQL Server instance or instances.
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaFile
.EXAMPLE
PS C:\> Get-DbaFile -SqlInstance sqlserver2014a -Path E:\Dir1
Logs into the SQL Server "sqlserver2014a" using Windows credentials and searches E:\Dir for all files
.EXAMPLE
PS C:\> 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
PS C:\> $all = Get-DbaDefaultPath -SqlInstance sql2014
PS C:\> 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
PS C:\> Get-DbaFile -SqlInstance sql2014 -Path 'E:\Dir1', 'E:\Dir2'
Returns the files in "E:\Dir1" and "E:Dir2" on sql2014
.EXAMPLE
PS C:\> Get-DbaFile -SqlInstance sql2014, sql2016 -Path 'E:\Dir1' -FileType fsf, mld
Finds files in E:\Dir1 ending with ".fsf" and ".mld" for both the servers sql2014 and sql2016.
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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) {
#Variable marked as unused by PSScriptAnalyzer
#$paths = @()
try {
$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#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaFilestream {
<#
.SYNOPSIS
Returns the status of Filestream on specified SQL Server for both the Service and Instance levels.
.DESCRIPTION
Returns the status of Filestream on specified SQL Server for both the Service and Instance levels.
.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 SqlInstance
The target SQL Server instance or instances. Defaults to localhost.
.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Credential
Login to the target Windows 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: Filestream
Author: Stuart Moore ( @napalmgram ) | Chrissy LeMaire ( @cl )
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaFilestream
.EXAMPLE
PS C:\> Get-DbaFilestream -SqlInstance server1\instance2
Will return the status of Filestream configuration for the service and instance server1\instance2
.EXAMPLE
PS C:\> Get-DbaFilestream -SqlInstance server1\instance2 -SqlCredential sqladmin
Prompts for the password to the SQL Login "sqladmin" then returns the status of Filestream configuration for the service and instance server1\instance2
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[switch]$EnableException
)
begin {
$idServiceFS = [ordered]@{
0 = 'Disabled'
1 = 'FileStream enabled for T-Sql access'
2 = 'FileStream enabled for T-Sql and IO streaming access'
3 = 'FileStream enabled for T-Sql, IO streaming, and remote clients'
}
$idInstanceFS = [ordered]@{
0 = 'Disabled'
1 = 'T-SQL access enabled'
2 = 'Full access enabled'
}
}
process {
foreach ($instance in $SqlInstance) {
$computer = $instance.ComputerName
$instanceName = $instance.InstanceName
<# Get Service-Level information #>
if ($instance.IsLocalHost) {
$computerName = $computer
} else {
$computerName = (Resolve-DbaNetworkName -ComputerName $computer -Credential $Credential).FullComputerName
}
Write-Message -Level Verbose -Message "Attempting to connect to $computer"
try {
$ognamespace = Get-DbaCmObject -EnableException -ComputerName $computerName -Namespace root\Microsoft\SQLServer -Query "SELECT NAME FROM __NAMESPACE WHERE NAME LIKE 'ComputerManagement%'"
$namespace = $ognamespace | Where-Object {
(Get-DbaCmObject -EnableException -ComputerName $computerName -Namespace $("root\Microsoft\SQLServer\" + $_.Name) -ClassName FilestreamSettings).Count -gt 0
} |
Sort-Object Name -Descending | Select-Object -First 1
if (-not $namespace) {
$namespace = $ognamespace
}
if ($namespace.Name) {
$serviceFS = Get-DbaCmObject -EnableException -ComputerName $computerName -Namespace $("root\Microsoft\SQLServer\" + $namespace.Name) -ClassName FilestreamSettings | Where-Object InstanceName -eq $instanceName | Select-Object -First 1
} else {
Write-Message -Level Warning -Message "No ComputerManagement was found on $computer. Service level information may not be collected." -Target $computer
}
} catch {
Stop-Function -Message "Issue collecting service-level information on $computer for $instanceName" -Target $computer -ErrorRecord $_ -Continue
}
<# Get Instance-Level information #>
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
}
try {
$instanceFS = Get-DbaSpConfigure -SqlInstance $server -Name FilestreamAccessLevel | Select-Object ConfiguredValue, RunningValue
} catch {
Stop-Function -Message "Issue collection instance-level configuration on $instanceName" -Target $server -ErrorRecord $_ -Exception $_.Exception -Continue
}
$pendingRestart = $instanceFS.ConfiguredValue -ne $instanceFS.RunningValue
if (($serviceFS.AccessLevel -ne 0) -and ($instanceFS.RunningValue -ne 0)) {
if (($serviceFS.AccessLevel -eq $instanceFS.RunningValue) -and $pendingRestart) {
Write-Message -Level Verbose -Message "A restart of the instance is pending before Filestream is configured."
}
}
$runvalue = (Get-DbaSpConfigure -SqlInstance $server -Name FilestreamAccessLevel | Select-Object RunningValue).RunningValue
$servicelevel = [int]$serviceFS.AccessLevel
[PsCustomObject]@{
ComputerName = $server.NetName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
InstanceAccess = $idInstanceFS[$runvalue]
ServiceAccess = $idServiceFS[$servicelevel]
ServiceShareName = $serviceFS.ShareName
InstanceAccessLevel = $instanceFS.RunningValue
ServiceAccessLevel = $serviceFS.AccessLevel
Credential = $Credential
SqlCredential = $SqlCredential
} | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, InstanceAccess, ServiceAccess, ServiceShareName
}
}
}
#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 instance or instances. 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.
.NOTES
Tags: Certificate
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaForceNetworkEncryption
.EXAMPLE
PS C:\> Get-DbaForceNetworkEncryption
Gets Force Encryption properties on the default (MSSQLSERVER) instance on localhost - requires (and checks for) RunAs admin.
.EXAMPLE
PS C:\> Get-DbaForceNetworkEncryption -SqlInstance sql01\SQL2008R2SP2
Gets Force Network Encryption for the SQL2008R2SP2 on sql01. Uses Windows Credentials to both login and view the registry.
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "ComputerName")]
[DbaInstanceParameter[]]$SqlInstance = $env:COMPUTERNAME,
[PSCredential]$Credential,
[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
if ($null -eq $resolved) {
Stop-Function -Message "Can't resolve $instance" -Target $instance -Continue -Category InvalidArgument
}
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 something
# here to avoid an empty catch
$null = 1
}
$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
}
}
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
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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaHelpIndex
.EXAMPLE
PS C:\> Get-DbaHelpIndex -SqlInstance localhost -Database MyDB
Returns information on all indexes on the MyDB database on the localhost.
.EXAMPLE
PS C:\> Get-DbaHelpIndex -SqlInstance localhost -Database MyDB,MyDB2
Returns information on all indexes on the MyDB & MyDB2 databases.
.EXAMPLE
PS C:\> Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -ObjectName dbo.Table1
Returns index information on the object dbo.Table1 in the database MyDB.
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaHelpIndex -SqlInstance localhost -Database MyDB -IncludeFragmentation
Returns the index information for all indexes in the MyDB database as well as their fragmentation
.EXAMPLE
PS C:\> 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 = 'Database,Object,Index,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 = 'Database,Object,Index,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 StatisticsName IS NULL";
}
#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 ,
IndexType ,
ISNULL(IndexName, si.stats_name) AS IndexName ,
NULL as StatisticsName ,
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 ,
'' ,
'' ,
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 ,
IndexType ,
IndexName ,
StatisticsName ,
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) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
} 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
Statistics = $detail.StatisticsName
KeyColumns = $detail.KeyColumns
IncludeColumns = $detail.IncludeColumns
FilterDefinition = $detail.FilterDefinition
DataCompression = $detail.DataCompression
IndexReads = "{0:N0}" -f $detail.IndexReads
IndexUpdates = "{0:N0}" -f $detail.IndexUpdates
Size = "{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
}
}
}
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
Statistics = $detail.StatisticsName
KeyColumns = $detail.KeyColumns
IncludeColumns = $detail.IncludeColumns
FilterDefinition = $detail.FilterDefinition
DataCompression = $detail.DataCompression
IndexReads = $detail.IndexReads
IndexUpdates = $detail.IndexUpdates
Size = [dbasize]($detail.SizeKB * 1024)
IndexRows = $detail.IndexRows
IndexLookups = $detail.IndexLookups
MostRecentlyUsed = $recentlyused
StatsSampleRows = $detail.StatsSampleRows
StatsRowMods = $detail.StatsRowMods
HistogramSteps = $detail.HistogramSteps
StatsLastUpdated = $detail.StatsLastUpdated
IndexFragInPercent = $detail.IndexFragInPercent
}
}
}
} catch {
Stop-Function -Continue -ErrorRecord $_ -Message "Cannot process $db on $server"
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaInstanceProperty {
<#
.SYNOPSIS
Gets SQL Server instance properties of one or more instance(s) of SQL Server.
.DESCRIPTION
The Get-DbaInstanceProperty command gets SQL Server instance properties from the SMO object sqlserver.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaInstanceProperty
.EXAMPLE
PS C:\> Get-DbaInstanceProperty -SqlInstance localhost
Returns SQL Server instance properties on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaInstanceProperty -SqlInstance sql2, sql4\sqlexpress
Returns SQL Server instance properties on default instance on sql2 and sqlexpress instance on sql4
.EXAMPLE
PS C:\> 'sql2','sql4' | Get-DbaInstanceProperty
Returns SQL Server instance properties on sql2 and sql4
.EXAMPLE
PS C:\> Get-DbaInstanceProperty -SqlInstance sql2,sql4 -InstanceProperty DefaultFile
Returns SQL Server instance property DefaultFile on instance sql2 and sql4
.EXAMPLE
PS C:\> Get-DbaInstanceProperty -SqlInstance sql2,sql4 -ExcludeInstanceProperty DefaultFile
Returns all SQL Server instance properties except DefaultFile on instance sql2 and sql4
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Get-DbaInstanceProperty -SqlInstance sql2 -SqlCredential $cred
Connects using sqladmin credential and returns SQL Server instance properties from sql2
#>
[CmdletBinding(DefaultParameterSetName = "Default")]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$InstanceProperty,
[object[]]$ExcludeInstanceProperty,
[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 {
$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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaSqlInstanceProperty
}
}
function Get-DbaInstanceUserOption {
<#
.SYNOPSIS
Gets SQL Instance user options of one or more instance(s) of SQL Server.
.DESCRIPTION
The Get-DbaInstanceUserOption command gets SQL Instance user options from the SMO object sqlserver.
.PARAMETER SqlInstance
The target SQL Server instance or instances.
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaInstanceUserOption
.EXAMPLE
PS C:\> Get-DbaInstanceUserOption -SqlInstance localhost
Returns SQL Instance user options on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaInstanceUserOption -SqlInstance sql2, sql4\sqlexpress
Returns SQL Instance user options on default instance on sql2 and sqlexpress instance on sql4
.EXAMPLE
PS C:\> 'sql2','sql4' | Get-DbaInstanceUserOption
Returns SQL Instance user options on sql2 and sql4
#>
[CmdletBinding(DefaultParameterSetName = "Default")]
param (
[parameter(Position = 0, 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
}
$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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaSqlInstanceUserOption
}
}
function Get-DbaIoLatency {
<#
.SYNOPSIS
Displays IO subsystem latency statistics from sys.dm_io_virtual_file_stats. Works on SQL Server 2005 and above.
.DESCRIPTION
This command is based off of Paul Randal's post "Advanced SQL Server performance tuning"
Returns both raw and aggregate information
Reference: https://www.sqlskills.com/blogs/paul/how-to-examine-io-subsystem-latencies-from-within-sql-server/
https://www.sqlskills.com/blogs/paul/capturing-io-latencies-period-time/
.PARAMETER SqlInstance
The SQL Server instance. 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: IOLatency
Author: Patrick Flynn (@sqllensman)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaIoLatency
.EXAMPLE
PS C:\> Get-DbaIoLatency -SqlInstance sql2008, sqlserver2012
Get IO subsystem latency statistics for servers sql2008 and sqlserver2012.
.EXAMPLE
PS C:\> $output = Get-DbaIoLatency -SqlInstance sql2008 | Select * | ConvertTo-DbaDataTable
Collects all IO subsystem latency statistics on server sql2008 into a Data Table.
.EXAMPLE
PS C:\> 'sql2008','sqlserver2012' | Get-DbaIoLatency
Get IO subsystem latency statistics for servers sql2008 and sqlserver2012 via pipline
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Get-DbaIoLatency -SqlInstance sql2008 -SqlCredential $cred
Connects using sqladmin credential and returns IO subsystem latency statistics from sql2008
#>
[CmdletBinding()]
Param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias('Silent')]
[switch]$EnableException
)
BEGIN {
$sql = "SELECT
[vfs].[database_id],
DB_NAME ([vfs].[database_id]) AS [DatabaseName],
[vfs].[file_id],
[mf].[physical_name],
[num_of_reads],
[io_stall_read_ms],
[num_of_writes],
[io_stall_write_ms],
[io_stall],
[num_of_bytes_read],
[num_of_bytes_written],
[sample_ms],
[size_on_disk_bytes],
[file_handle],
[ReadLatency] =
CASE WHEN [num_of_reads] = 0
THEN 0
ELSE ([io_stall_read_ms] / [num_of_reads])
END,
[WriteLatency] =
CASE WHEN [num_of_writes] = 0
THEN 0
ELSE ([io_stall_write_ms] / [num_of_writes])
END,
[Latency] =
CASE WHEN ([num_of_reads] = 0 AND [num_of_writes] = 0)
THEN 0
ELSE ([io_stall] / ([num_of_reads] + [num_of_writes]))
END,
[AvgBPerRead] =
CASE WHEN [num_of_reads] = 0
THEN 0
ELSE ([num_of_bytes_read] / [num_of_reads])
END,
[AvgBPerWrite] =
CASE WHEN [num_of_writes] = 0
THEN 0
ELSE ([num_of_bytes_written] / [num_of_writes])
END,
[AvgBPerTransfer] =
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
FROM sys.dm_io_virtual_file_stats (NULL,NULL) AS [vfs]
INNER JOIN sys.master_files AS [mf]
ON [vfs].[database_id] = [mf].[database_id]
AND [vfs].[file_id] = [mf].[file_id];"
Write-Message -Level Debug -Message $sql
$excludeColumns = 'FileHandle', 'ReadLatency', 'WriteLatency', 'Latency', 'AvgBPerRead', 'AvgBPerWrite', 'AvgBPerTransfer'
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($instance in $SqlInstance) {
if (Test-FunctionInterrupt) { return }
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"
foreach ($row in $server.Query($sql)) {
[PSCustomObject]@{
ComputerName = $server.NetName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
DatabaseId = $row.database_id
DatabaseName = $row.DatabaseName
FileId = $row.file_id
PhysicalName = $row.physical_name
NumberOfReads = $row.num_of_reads
IoStallRead = $row.io_stall_read_ms
NumberOfwrites = $row.num_of_writes
IoStallWrite = $row.io_stall_write_ms
IoStall = $row.io_stall
NumberOfBytesRead = $row.num_of_bytes_read
NumberOfBytesWritten = $row.num_of_bytes_written
SampleMilliseconds = $row.sample_ms
SizeOnDiskBytes = $row.num_of_bytes_written
FileHandle = $row.file_handle
ReadLatency = $row.ReadLatency
WriteLatency = $row.WriteLatency
Latency = $row.Latency
AvgBPerRead = $row.AvgBPerRead
AvgBPerWrite = $row.AvgBPerWrite
AvgBPerTransfer = $row.AvgBPerTransfer
} | Select-DefaultView -ExcludeProperty $excludeColumns
}
}
}
}
#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 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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaLastBackup
.EXAMPLE
PS C:\> Get-DbaLastBackup -SqlInstance ServerA\sql987
Returns a custom object displaying Server, Database, RecoveryModel, LastFullBackup, LastDiffBackup, LastLogBackup, SinceFull, SinceDiff, SinceLog, Status, DatabaseCreated, DaysSinceDbCreated
.EXAMPLE
PS C:\> Get-DbaLastBackup -SqlInstance ServerA\sql987
Returns a custom object with Server name, Database name, and the date the last time backups were performed.
.EXAMPLE
PS C:\> 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
PS C:\> 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, ValueFromPipeline)]
[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 {
$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 target SQL Server instance or instances. Defaults to localhost.
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Ref:
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/
.LINK
https://dbatools.io/Get-DbaLastGoodCheckDb
.EXAMPLE
PS C:\> Get-DbaLastGoodCheckDb -SqlInstance ServerA\sql987
Returns a custom object displaying Server, Database, DatabaseCreated, LastGoodCheckDb, DaysSinceDbCreated, DaysSinceLastGoodCheckDb, Status and DataPurityEnabled
.EXAMPLE
PS C:\> Get-DbaLastGoodCheckDb -SqlInstance ServerA\sql987 -SqlCredential sqladmin | Format-Table -AutoSize
Returns a formatted table displaying Server, Database, DatabaseCreated, LastGoodCheckDb, DaysSinceDbCreated, DaysSinceLastGoodCheckDb, Status and DataPurityEnabled. Authenticates using SQL Server authentication.
.EXAMPLE
PS C:\> Get-DbaLastGoodCheckDb -SqlInstance sql2016 -ExcludeDatabase "TempDB" | Format-Table -AutoSize
Returns a formatted table displaying Server, Database, DatabaseCreated, LastGoodCheckDb, DaysSinceDbCreated, DaysSinceLastGoodCheckDb, Status and DataPurityEnabled. All databases except for "TempDB" will be displayed in the output.
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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 {
$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-DbaLatchStatistic {
<#
.SYNOPSIS
Displays latch statistics from sys.dm_os_latch_stats
.DESCRIPTION
This command is based off of Paul Randal's post "Advanced SQL Server performance tuning"
Returns:
LatchClass
WaitSeconds
WaitCount
Percentage
AverageWaitSeconds
URL
Reference: https://www.sqlskills.com/blogs/paul/advanced-performance-troubleshooting-waits-latches-spinlocks/
https://www.sqlskills.com/blogs/paul/most-common-latch-classes-and-what-they-mean/
.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 latch stats on the system. Default per Paul's post is 95%.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: LatchStatistics, Waits
Author: Patrick Flynn (@sqllensman)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaLatchStatistic
.EXAMPLE
PS C:\> Get-DbaLatchStatistic -SqlInstance sql2008, sqlserver2012
Check latch statistics for servers sql2008 and sqlserver2012
.EXAMPLE
PS C:\> Get-DbaLatchStatistic -SqlInstance sql2008 -Threshold 98
Check latch statistics on server sql2008 for thresholds above 98%
.EXAMPLE
PS C:\> $output = Get-DbaLatchStatistic -SqlInstance sql2008 -Threshold 100 | Select * | ConvertTo-DbaDataTable
Collects all latch statistics on server sql2008 into a Data Table.
.EXAMPLE
PS C:\> 'sql2008','sqlserver2012' | Get-DbaLatchStatistic
Get latch statistics for servers sql2008 and sqlserver2012 via pipline
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Get-DbaLatchStatistic -SqlInstance sql2008 -SqlCredential $cred
Connects using sqladmin credential and returns latch statistics from sql2008
.EXAMPLE
PS C:\> $output = Get-DbaLatchStatistic -SqlInstance sql2008
PS C:\> $output
PS C:\> 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(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[int]$Threshold = 95,
[Alias('Silent')]
[switch]$EnableException
)
BEGIN {
$sql = "WITH [Latches] AS
(
SELECT
[latch_class],
[wait_time_ms] / 1000.0 AS [WaitS],
[waiting_requests_count] AS [WaitCount],
Case WHEN SUM ([wait_time_ms]) OVER() = 0 THEN NULL ELSE 100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() END AS [Percentage],
ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
FROM sys.dm_os_latch_stats
WHERE [latch_class] NOT IN (N'BUFFER')
)
SELECT
MAX ([W1].[latch_class]) AS [LatchClass],
CAST (MAX ([W1].[WaitS]) AS DECIMAL(14, 2)) AS [WaitSeconds],
MAX ([W1].[WaitCount]) AS [WaitCount],
CAST (MAX ([W1].[Percentage]) AS DECIMAL(14, 2)) AS [Percentage],
CAST ((MAX ([W1].[WaitS]) / MAX ([W1].[WaitCount])) AS DECIMAL (14, 4)) AS [AvgWaitSeconds],
CAST ('https://www.sqlskills.com/help/latches/' + MAX ([W1].[latch_class]) as XML) AS [URL]
FROM [Latches] AS [W1]
INNER JOIN [Latches] 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 {
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 9
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
Return
}
Write-Message -Level Verbose -Message "Connected to $instance"
foreach ($row in $server.Query($sql)) {
[PSCustomObject]@{
ComputerName = $server.NetName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
WaitType = $row.LatchClass
WaitSeconds = $row.WaitSeconds
WaitCount = $row.WaitCount
Percentage = $row.Percentage
AverageWaitSeconds = $row.AvgWaitSeconds
URL = $row.URL
}
}
}
}
}
function Get-DbaLinkedServer {
<#
.SYNOPSIS
Gets all linked servers and a summary of information from the linked servers listed.
.DESCRIPTION
Retrieves information about each linked server on the instance(s).
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaLinkedServer
.EXAMPLE
PS C:\> Get-DbaLinkedServer -SqlInstance DEV01
Returns all linked servers for the SQL Server instance DEV01
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance DEV01 -Group SQLDEV | Get-DbaLinkedServer | Out-GridView
Returns all linked servers for a group of servers from SQL Server Central Management Server (CMS). Send output to GridView.
#>
[CmdletBinding(DefaultParameterSetName = 'Default')]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$LinkedServer,
[object[]]$ExcludeLinkedServer,
[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
}
$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 target SQL Server instance or 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: OS
Author: Klaas Vandenberghe (@PowerDBAKlaas)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaLocaleSetting
.EXAMPLE
PS C:\> Get-DbaLocaleSetting -ComputerName sqlserver2014a
Gets the Locale settings on computer sqlserver2014a.
.EXAMPLE
PS C:\> 'sql1','sql2','sql3' | Get-DbaLocaleSetting
Gets the Locale settings on computers sql1, sql2 and sql3.
.EXAMPLE
PS C:\> Get-DbaLocaleSetting -ComputerName sql1,sql2 -SqlCredential $credential | Out-Gridview
Gets the Locale settings on computers sql1 and sql2 using SQL Authentication to authenticate to the servers, 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 {
# uses cim commands
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 Server instance. Takes a server object from the pipeline.
.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 target 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 ExcludeSystemLogin
A Switch to remove System Logins from the output.
.PARAMETER Type
Filters logins by their type. Valid options are Windows and SQL.
.PARAMETER Locked
A Switch to return locked Logins.
.PARAMETER Disabled
A Switch to return disabled Logins.
.PARAMETER SqlLogins
Deprecated. Please use -Type SQL
.PARAMETER WindowsLogins
Deprecated. Please use -Type Windows.
.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) | Rob Sewell (@SQLDBaWithBeard)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaLogin
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance sql2016
Gets all the logins from server sql2016 using NT authentication and returns the SMO login objects
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaLogin -SqlInstance sql2016 -SqlCredential $sqlcred -Login dbatoolsuser,TheCaptain
Get specific logins from server sql2016 returned as SMO login objects.
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance sql2016 -IncludeFilter '##*','NT *'
Get all user objects from server sql2016 beginning with '##' or 'NT ', returned as SMO login objects.
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance sql2016 -ExcludeLogin dbatoolsuser
Get all user objects from server sql2016 except the login dbatoolsuser, returned as SMO login objects.
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance sql2016 -Type Windows
Get all user objects from server sql2016 that are Windows Logins
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance sql2016 -Type Windows -IncludeFilter *Rob*
Get all user objects from server sql2016 that are Windows Logins and have Rob in the name
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance sql2016 -Type SQL
Get all user objects from server sql2016 that are SQL Logins
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance sql2016 -Type SQL -IncludeFilter *Rob*
Get all user objects from server sql2016 that are SQL Logins and have Rob in the name
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance sql2016 -ExcludeSystemLogin
Get all user objects from server sql2016 that are not system objects
.EXAMPLE
PS C:\> 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
PS C:\> 'sql2016', 'sql2014' | Get-DbaLogin -SqlCredential $sqlcred
Using Get-DbaLogin on the pipeline, you can also specify which names you would like with -Login.
.EXAMPLE
PS C:\> 'sql2016', 'sql2014' | Get-DbaLogin -SqlCredential $sqlcred -Locked
Using Get-DbaLogin on the pipeline to get all locked logins on servers sql2016 and sql2014.
.EXAMPLE
PS C:\> '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, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$Login,
[object[]]$IncludeFilter,
[object[]]$ExcludeLogin,
[object[]]$ExcludeFilter,
[Alias('ExcludeSystemLogins')]
[switch]$ExcludeSystemLogin,
[ValidateSet('Windows', 'SQL')]
[string]$Type,
[switch]$HasAccess,
[switch]$SqlLogins,
[switch]$WindowsLogins,
[switch]$Locked,
[switch]$Disabled,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter SQLLogins
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter WindowsLogins
if ($SQLLogins) {
$Type = "SQL"
}
if ($WindowsLogins) {
$Type = "Windows"
}
}
process {
foreach ($instance in $SqlInstance) {
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 ($ExcludeSystemLogin) {
$serverLogins = $serverLogins | Where-Object IsSystemObject -eq $false
}
if ($Type -eq 'Windows') {
$serverLogins = $serverLogins | Where-Object LoginType -eq 'WindowsUser'
}
if ($Type -eq 'SQL') {
$serverLogins = $serverLogins | Where-Object LoginType -eq 'SqlLogin'
}
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-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 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 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) | Simone Bizzotto ( @niphlod )
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaMaintenanceSolutionLog
.EXAMPLE
PS C:\> Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a
Gets the outcome of the IndexOptimize job on sql instance sqlserver2014a.
.EXAMPLE
PS C:\> Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -SqlCredential $credential
Gets the outcome of the IndexOptimize job on sqlserver2014a, using SQL Authentication.
.EXAMPLE
PS C:\> 'sqlserver2014a', 'sqlserver2020test' | Get-DbaMaintenanceSolutionLog
Gets the outcome of the IndexOptimize job on sqlserver2014a and sqlserver2020test.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaMaintenanceSolutionLog -SqlInstance sqlserver2014a -Since '2017-07-18'
Gets the outcome of the IndexOptimize job on sqlserver2014a, starting from july 18, 2017.
.EXAMPLE
PS C:\> 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")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "", Justification = "Internal functions are ignored")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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
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-Object -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()
}
}
}
}
function Get-DbaManagementObject {
<#
.SYNOPSIS
Gets SQL Mangaement Object versions installed on the machine.
.DESCRIPTION
The Get-DbaManagementObject returns an object with the Version and the
Add-Type Load Template for each version on the server.
.PARAMETER ComputerName
The name of the Windows Server(s) 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaManagementObject
.EXAMPLE
PS C:\> Get-DbaManagementObject
Returns all versions of SMO on the computer
.EXAMPLE
PS C:\> Get-DbaManagementObject -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]
<# DO NOT use Write-Message as this is inside of a script block #>
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)
}
}
}
<# DO NOT use Write-Message as this is inside of a script block #>
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) {
<# DO NOT use Write-Message as this is inside of a script block #>
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 {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "Passed version $VersionNumber, looking for that specific version"
if ($localversion.ToString().StartsWith("$VersionNumber.")) {
<# DO NOT use Write-Message as this is inside of a script block #>
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")"
}
}
}
}
<# DO NOT use Write-Message as this is inside of a script block #>
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) {
<# DO NOT use Write-Message as this is inside of a script block #>
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 {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "Passed version $VersionNumber, looking for that specific version"
if ($array[0].StartsWith("$VersionNumber.")) {
<# DO NOT use Write-Message as this is inside of a script block #>
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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaSqlManagementObject
}
}
#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.
Results are turned in megabytes (MB).
.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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaMaxMemory
.EXAMPLE
PS C:\> Get-DbaMaxMemory -SqlInstance sqlcluster, sqlserver2012
Get memory settings for instances "sqlcluster" and "sqlserver2012". Returns results in megabytes (MB).
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlcluster | Get-DbaMaxMemory | Where-Object { $_.MaxValue -gt $_.Total }
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)
.EXAMPLE
PS C:\> Find-DbaInstance -ComputerName localhost | Get-DbaMaxMemory | Format-Table -AutoSize
Scans localhost for instances using the browser service, traverses all instances and displays memory settings in a formatted table.
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[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
}
$totalMemory = $server.PhysicalMemory
# Some servers under-report by 1.
if (($totalMemory % 1024) -ne 0) {
$totalMemory = $totalMemory + 1
}
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Total = [int]$totalMemory
MaxValue = [int]$server.Configuration.MaxServerMemory.ConfigValue
Server = $server # This will allowing piping a non-connected object
} | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Total, MaxValue
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaMemoryCondition {
<#
.SYNOPSIS
Determine the memory conditions from SQL Server ring buffers.
.DESCRIPTION
The information from SQL Server ring buffers can be used to determine the memory conditions on the server when paging occurs.
This command is based on a query provided by Microsoft support.
Reference KB article: https://support.microsoft.com/en-us/help/918483/how-to-reduce-paging-of-buffer-pool-memory-in-the-64-bit-version-of-sq
.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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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: IJeb Reitsma
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaMemoryCondition
.EXAMPLE
PS C:\> Get-DbaMemoryCondition -SqlInstance sqlserver2014a
Returns the memory conditions for the selected instance
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlserver2014a -Group GroupName | Get-DbaMemoryCondition | Out-GridView
Returns the memory conditions for a group of servers from SQL Server Central Management Server (CMS). Send output to GridView.
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[switch]$EnableException
)
begin {
$sql = "SELECT CONVERT (varchar(30), GETDATE(), 121) as Runtime,
DATEADD (ms, -1 * (sys.ms_ticks - a.[RecordTime]), GETDATE()) AS NotificationTime,
[NotificationType],
[MemoryUtilizationPercent],
[TotalPhysicalMemoryKB],
[AvailablePhysicalMemoryKB],
[TotalPageFileKB],
[AvailablePageFileKB],
[TotalVirtualAddressSpaceKB],
[AvailableVirtualAddressSpaceKB],
[NodeId],
[SQLReservedMemoryKB],
[SQLCommittedMemoryKB],
[RecordId],
[Type],
[Indicators],
[RecordTime],
sys.ms_ticks AS [CurrentTime]
FROM
(SELECT x.value('(//Record/ResourceMonitor/Notification)[1]', 'varchar(30)') AS [NotificationType],
x.value('(//Record/MemoryRecord/MemoryUtilization)[1]', 'bigint') AS [MemoryUtilizationPercent],
x.value('(//Record/MemoryRecord/TotalPhysicalMemory)[1]', 'bigint') AS [TotalPhysicalMemoryKB],
x.value('(//Record/MemoryRecord/AvailablePhysicalMemory)[1]', 'bigint') AS [AvailablePhysicalMemoryKB],
x.value('(//Record/MemoryRecord/TotalPageFile)[1]', 'bigint') AS [TotalPageFileKB],
x.value('(//Record/MemoryRecord/AvailablePageFile)[1]', 'bigint') AS [AvailablePageFileKB],
x.value('(//Record/MemoryRecord/TotalVirtualAddressSpace)[1]', 'bigint') AS [TotalVirtualAddressSpaceKB],
x.value('(//Record/MemoryRecord/AvailableVirtualAddressSpace)[1]', 'bigint') AS [AvailableVirtualAddressSpaceKB],
x.value('(//Record/MemoryNode/@id)[1]', 'bigint') AS [NodeId],
x.value('(//Record/MemoryNode/ReservedMemory)[1]', 'bigint') AS [SQLReservedMemoryKB],
x.value('(//Record/MemoryNode/CommittedMemory)[1]', 'bigint') AS [SQLCommittedMemoryKB],
x.value('(//Record/@id)[1]', 'bigint') AS [RecordId],
x.value('(//Record/@type)[1]', 'varchar(30)') AS [Type],
x.value('(//Record/ResourceMonitor/Indicators)[1]', 'bigint') AS [Indicators],
x.value('(//Record/@time)[1]', 'bigint') AS [RecordTime]
FROM (SELECT CAST (record as xml) FROM sys.dm_os_ring_buffers
WHERE ring_buffer_type = 'RING_BUFFER_RESOURCE_MONITOR') AS R(x)) a
CROSS JOIN sys.dm_os_sys_info sys
ORDER BY a.[RecordTime] ASC"
}
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
}
try {
$results = $server.Query($sql)
} catch {
Stop-Function -Message "Issue collecting data" -Target $instance -ErrorRecord $_
}
foreach ($row in $results) {
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Runtime = $row.runtime
NotificationTime = $row.NotificationTime
NotificationType = $row.NotificationType
MemoryUtilizationPercent = $row.MemoryUtilizationPercent
TotalPhysicalMemory = [dbasize]$row.TotalPhysicalMemoryKB * 1024
AvailablePhysicalMemory = [dbasize]$row.AvailablePhysicalMemoryKB * 1024
TotalPageFile = [dbasize]$row.TotalPageFileKB * 1024
AvailablePageFile = [dbasize]$row.AvailablePageFileKB * 1024
TotalVirtualAddressSpace = [dbasize]$row.TotalVirtualAddressSpaceKB * 1024
AvailableVirtualAddressSpace = [dbasize]$row.AvailableVirtualAddressSpaceKB * 1024
NodeId = $row.NodeId
SQLReservedMemory = [dbasize]$row.SQLReservedMemoryKB * 1024
SQLCommittedMemory = [dbasize]$row.SQLCommittedMemoryKB * 1024
RecordId = $row.RecordId
Type = $row.Type
Indicators = $row.Indicators
RecordTime = $row.RecordTime
CurrentTime = $row.CurrentTime
}
}
}
}
}
#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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
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
PS C:\> Get-DbaMemoryUsage -ComputerName sql2017
Returns a custom object displaying Server, counter instance, counter, number of pages, memory
.EXAMPLE
PS C:\> Get-DbaMemoryUsage -ComputerName sql2017\sqlexpress -SqlCredential sqladmin | Where-Object { $_.Memory.Megabyte -gt 100 }
Logs into the sql2017\sqlexpress as sqladmin using SQL Authentication then returns results only where memory exceeds 100 MB
.EXAMPLE
PS C:\> $servers | Get-DbaMemoryUsage | Out-Gridview
Gets results from an array of $servers then diplays them in a gridview.
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[Alias("Host", "cn", "Server")]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[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)
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "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
Memory = $_.cookedvalue / 1024
}
}
} catch {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "No Memory Manager Counters on $Computer"
}
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "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
Memory = $_.cookedvalue * 8192 / 1048576
}
}
} catch {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "No Plan Cache Counters on $Computer"
}
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "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
Memory = $_.cookedvalue * 8192 / 1048576.0
}
}
} catch {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "No Buffer Manager Counters on $Computer"
}
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "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
Memory = $_.cookedvalue / 1024
}
}
} catch {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "No SSAS Counters on $Computer"
}
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "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
Memory = $_.cookedvalue / 1024 / 1024
}
}
} catch {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose -Message "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 {
foreach ($result in (Invoke-Command2 -ComputerName $Computer -Credential $Credential -ScriptBlock $scriptblock -argumentlist $Memcounters, $Plancounters, $BufManpagecounters, $SSAScounters, $SSIScounters)) {
[PSCustomObject]@{
ComputerName = $result.ComputerName
SqlInstance = $result.SqlInstance
CounterInstance = $result.CounterInstance
Counter = $result.Counter
Pages = $result.Pages
Memory = [dbasize]($result.Memory * 1024 * 1024)
}
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $computer -Continue
}
} else {
Write-Message -Level Warning -Message "Can't resolve $Computer."
Continue
}
}
}
}
function Get-DbaModule {
<#
.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
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. 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 ExcludeSystemDatabases
Allows you to suppress output on system databases
.PARAMETER ExcludeSystemObjects
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
Tags: StoredProcedure, Trigger
Author: Brandon Abshire, netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaModule
.EXAMPLE
PS C:\> Get-DbaModule -SqlInstance sql2008, sqlserver2012
Return all modules for servers sql2008 and sqlserver2012 sorted by Database, Modify_Date ASC.
.EXAMPLE
PS C:\> Get-DbaModule -SqlInstance sql2008, sqlserver2012 | Select *
Shows hidden definition column (informative wall of text).
.EXAMPLE
PS C:\> Get-DbaModule -SqlInstance sql2008 -Database TestDB -ModifiedSince "2017-01-01 10:00:00"
Return all modules on server sql2008 for only the TestDB database with a modified date after 1 January 2017 10:00:00 AM.
.EXAMPLE
PS C:\> Get-DbaModule -SqlInstance 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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[datetime]$ModifiedSince = "1900-01-01",
[ValidateSet("View", "TableValuedFunction", "DefaultConstraint", "StoredProcedure", "Rule", "InlineTableValuedFunction", "Trigger", "ScalarFunction")]
[string[]]$Type,
[switch]$ExcludeSystemDatabases,
[switch]$ExcludeSystemObjects,
[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 ($ExcludeSystemObjects) {
$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 {
$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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaSqlModule
}
}
#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 target computer.
.NOTES
Tags: Msdtc, dtc
Author: Klaas Vandenberghe (@powerdbaklaas)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaMsdtc
.EXAMPLE
PS C:\> Get-DbaMsdtc -ComputerName srv0042
Get DTC status for the server srv0042
.EXAMPLE
PS C:\> $Computers = (Get-Content D:\configfiles\SQL\MySQLInstances.txt | % {$_.split('\')[0]})
PS C:\> $Computers | Get-DbaMsdtc
Get DTC status for all the computers in a .txt file
.EXAMPLE
PS C:\> Get-DbaMsdtc -Computername $Computers | where { $_.dtcservicestate -ne 'running' }
Get DTC status for all the computers where the MSDTC Service is not running
.EXAMPLE
PS C:\> Get-DbaMsdtc -ComputerName srv0042 | Out-Gridview
Get DTC status for the computer srv0042 and show in a grid view
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[Alias('cn', 'host', 'Server')]
[string[]]$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-Message -Level Warning -Message "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 target SQL Server instance or 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: Network
Author: Klaas Vandenberghe (@PowerDBAKlaas)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaNetworkActivity
.EXAMPLE
PS C:\> Get-DbaNetworkActivity -ComputerName sqlserver2014a
Gets the Current traffic on every Network Interface on computer sqlserver2014a.
.EXAMPLE
PS C:\> 'sql1','sql2','sql3' | Get-DbaNetworkActivity
Gets the Current traffic on every Network Interface on computers sql1, sql2 and sql3.
.EXAMPLE
PS C:\> 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 instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaNetworkCertificate
Gets computer certificates on localhost that are candidates for using with SQL Server's network encryption
.EXAMPLE
PS C:\> 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,
[switch]$EnableException
)
process {
# Registry access
foreach ($computer in $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
# here to avoid an empty catch
$null = 1
}
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
}
}
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaOpenTransaction
.EXAMPLE
PS C:\> Get-DbaOpenTransaction -SqlInstance sqlserver2014a
Returns open transactions for sqlserver2014a
.EXAMPLE
PS C:\> Get-DbaOpenTransaction -SqlInstance sqlserver2014a -SqlCredential sqladmin
Logs into sqlserver2014a using the login "sqladmin"
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[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) {
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), https://wsmelton.github.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaOperatingSystem
.EXAMPLE
PS C:\> Get-DbaOperatingSystem
Returns information about the local computer's operating system
.EXAMPLE
PS C:\> Get-DbaOperatingSystem -ComputerName sql2016
Returns information about the sql2016's operating system
.EXAMPLE
PS C:\> $wincred = Get-Credential ad\sqladmin
PS C:\> 'sql2016', 'sql2017' | Get-DbaOperatingSystem -Credential $wincred
Returns information about the sql2016 and sql2017 operating systems using alternative Windows credentials
.EXAMPLE
PS C:\> Get-Content .\servers.txt | Get-DbaOperatingSystem
Returns information about all the servers operating system that are stored in the file. Every line in the file can only contain one hostname for a server.
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[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
Write-Message -Level Verbose -Message "Resolved $computerResolved"
if (!$computerResolved) {
Write-Message -Level Warning -Message "Unable to resolve hostname of $computer. Skipping."
continue
}
try {
$TestWS = Test-WSMan -ComputerName $computerResolved -ErrorAction SilentlyContinue
} catch {
Write-Message -Level Warning -Message "Remoting not availablle on $computer. Skipping checks"
$TestWS = $null
}
$splatDbaCmObject = @{
ComputerName = $computerResolved
EnableException = $true
}
if (Test-Bound "Credential") {
$splatDbaCmObject["Credential"] = $Credential
}
if ($TestWS) {
try {
$psVersion = Invoke-Command2 -ComputerName $computerResolved -Credential $Credential -ScriptBlock { $PSVersionTable.PSVersion }
$PowerShellVersion = "$($psVersion.Major).$($psVersion.Minor)"
} catch {
Write-Message -Level Warning -Message "PowerShell Version information not available on $computer."
$PowerShellVersion = 'Unavailable'
}
} else {
$PowerShellVersion = 'Unknown'
}
try {
$os = Get-DbaCmObject @splatDbaCmObject -ClassName Win32_OperatingSystem
} catch {
Stop-Function -Message "Failure collecting OS information on $computer" -Target $computer -ErrorRecord $_
return
}
try {
$tz = Get-DbaCmObject @splatDbaCmObject -ClassName Win32_TimeZone
} catch {
Stop-Function -Message "Failure collecting TimeZone information on $computer" -Target $computer -ErrorRecord $_
return
}
try {
$powerPlan = Get-DbaCmObject @splatDbaCmObject -ClassName Win32_PowerPlan -Namespace "root\cimv2\power" | Select-Object ElementName, InstanceId, IsActive
} catch {
Write-Message -Level Warning -Message "Power plan information not available on $computer."
$powerPlan = $null
}
if ($powerPlan) {
$activePowerPlan = ($powerPlan | Where-Object IsActive).ElementName -join ','
} else {
$activePowerPlan = 'Not Avaliable'
}
$language = Get-Language $os.OSLanguage
try {
$ss = Get-DbaCmObject @splatDbaCmObject -Class Win32_SystemServices
if ($ss | Select-Object PartComponent | Where-Object {$_ -like "*ClusSvc*"}) {
$IsWsfc = $true
} else {
$IsWsfc = $false
}
} catch {
Write-Message -Level Warning -Message "Unable to determine Cluster State of $computer."
$IsWsfc = $null
}
[PSCustomObject]@{
ComputerName = $computerResolved
Manufacturer = $os.Manufacturer
Organization = $os.Organization
Architecture = $os.OSArchitecture
Version = $os.Version
Build = $os.BuildNumber
OSVersion = $os.caption;
SPVersion = $os.servicepackmajorversion;
InstallDate = [DbaDateTime]$os.InstallDate
LastBootTime = [DbaDateTime]$os.LastBootUpTime
LocalDateTime = [DbaDateTime]$os.LocalDateTime
PowerShellVersion = $PowerShellVersion
TimeZone = $tz.Caption
TimeZoneStandard = $tz.StandardName
TimeZoneDaylight = $tz.DaylightName
BootDevice = $os.BootDevice
SystemDevice = $os.SystemDevice
SystemDrive = $os.SystemDrive
WindowsDirectory = $os.WindowsDirectory
PagingFileSize = $os.SizeStoredInPagingFiles
TotalVisibleMemory = [DbaSize]($os.TotalVisibleMemorySize * 1024)
FreePhysicalMemory = [DbaSize]($os.FreePhysicalMemory * 1024)
TotalVirtualMemory = [DbaSize]($os.TotalVirtualMemorySize * 1024)
FreeVirtualMemory = [DbaSize]($os.FreeVirtualMemory * 1024)
ActivePowerPlan = $activePowerPlan
Status = $os.Status
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
IsWsfc = $IsWsfc
} | 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 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 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) | Garry Bargsley (@gbargsley) | Simone Bizzotto (@niphlod)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaOrphanUser
.EXAMPLE
PS C:\> Get-DbaOrphanUser -SqlInstance localhost\sql2016
Finds all orphan users without matching Logins in all databases present on server 'localhost\sql2016'.
.EXAMPLE
PS C:\> 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
PS C:\> 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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$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 -in @([Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin, [Microsoft.SqlServer.Management.Smo.LoginType]::Certificate)) -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 page file configuration of the target computer.
.DESCRIPTION
This command uses CIM (or other, related computer management tools) to detect the page file configuration of the target computer(s).
Note that this may require local administrator privileges for the relevant computers.
.PARAMETER ComputerName
The target SQL Server instance or instances.
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPageFileSetting
.EXAMPLE
PS C:\> 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
PS C:\> 'ServerA' | Get-DbaPageFileSetting
Returns a custom object displaying ComputerName, AutoPageFile, FileName, Status, LastModified, LastAccessed, AllocatedBaseSize, InitialSize, MaximumSize, PeakUsage, CurrentUsage for ServerA
#>
[CmdletBinding()]
param (
[Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName = $true)]
[Alias("cn", "host", "ServerInstance", "Server", "SqlServer")]
[DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($computer in $ComputerName) {
$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-DbaPbmCategory {
<#
.SYNOPSIS
Returns policy categories from policy based management from an instance.
.DESCRIPTION
Returns policy categories from policy based management from an instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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
Filters results to only show specific condition
.PARAMETER ExcludeSystemObject
By default system objects are include. Use this parameter to exclude them.
.PARAMETER InputObject
Allows piping from Get-DbaPbmStore
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Policy, PolicyBasedManagement, PBM
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPbmCategory
.EXAMPLE
PS C:\> Get-DbaPbmCategory -SqlInstance sql2016
Returns all policy categories from the sql2016 PBM server
.EXAMPLE
PS C:\> Get-DbaPbmCategory -SqlInstance sql2016 -SqlCredential $cred
Uses a credential $cred to connect and return all policy categories from the sql2016 PBM server
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[string[]]$Category,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Dmf.PolicyStore[]]$InputObject,
[switch]$ExcludeSystemObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaPbmStore -SqlInstance $instance -SqlCredential $SqlCredential
}
foreach ($store in $InputObject) {
$all = $store.PolicyCategories
if (-not $ExcludeSystemObject) {
$all = $all | Where-Object IsSystemObject -ne $true
}
if ($Category) {
$all = $all | Where-Object Name -in $Category
}
foreach ($current in $all) {
Write-Message -Level Verbose -Message "Processing $current"
Add-Member -Force -InputObject $current -MemberType NoteProperty ComputerName -value $store.ComputerName
Add-Member -Force -InputObject $current -MemberType NoteProperty InstanceName -value $store.InstanceName
Add-Member -Force -InputObject $current -MemberType NoteProperty SqlInstance -value $store.SqlInstance
Select-DefaultView -InputObject $current -Property ComputerName, InstanceName, SqlInstance, Id, Name, MandateDatabaseSubscriptions
}
}
}
}
function Get-DbaPbmCategorySubscription {
<#
.SYNOPSIS
Returns policy category subscriptions from policy based management from an instance.
.DESCRIPTION
Returns policy category subscriptions from policy based management from an instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 InputObject
Allows piping from Get-DbaPbmStore
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Policy, PolicyBasedManagement, PBM
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPbmCategorySubscription
.EXAMPLE
PS C:\> Get-DbaPbmCategorySubscription -SqlInstance sql2016
Returns all policy category subscriptions from the sql2016 PBM server
.EXAMPLE
PS C:\> Get-DbaPbmCategorySubscription -SqlInstance sql2016 -SqlCredential $cred
Uses a credential $cred to connect and return all policy category subscriptions from the sql2016 PBM server
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Dmf.PolicyStore[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaPbmStore -SqlInstance $instance -SqlCredential $SqlCredential
}
foreach ($store in $InputObject) {
$all = $store.PolicycategorySubscriptions
foreach ($current in $all) {
Write-Message -Level Verbose -Message "Processing $current"
Add-Member -Force -InputObject $current -MemberType NoteProperty ComputerName -value $store.ComputerName
Add-Member -Force -InputObject $current -MemberType NoteProperty InstanceName -value $store.InstanceName
Add-Member -Force -InputObject $current -MemberType NoteProperty SqlInstance -value $store.SqlInstance
Select-DefaultView -InputObject $current -ExcludeProperty Properties, Urn, Parent
}
}
}
}
function Get-DbaPbmCondition {
<#
.SYNOPSIS
Returns conditions from policy based management from an instance.
.DESCRIPTION
Returns conditions from policy based management from an instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 Condition
Filters results to only show specific condition
.PARAMETER IncludeSystemObject
By default system objects are filtered out. Use this parameter to include them.
.PARAMETER InputObject
Allows piping from Get-DbaPbmStore
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Policy, PolicyBasedManagement, PBM
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPbmCondition
.EXAMPLE
PS C:\> Get-DbaPbmCondition -SqlInstance sql2016
Returns all conditions from the sql2016 PBM server
.EXAMPLE
PS C:\> Get-DbaPbmCondition -SqlInstance sql2016 -SqlCredential $cred
Uses a credential $cred to connect and return all conditions from the sql2016 PBM server
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[string[]]$Condition,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Dmf.PolicyStore[]]$InputObject,
[switch]$IncludeSystemObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaPbmStore -SqlInstance $instance -SqlCredential $SqlCredential
}
foreach ($store in $InputObject) {
$allconditions = $store.Conditions
if (-not $IncludeSystemObject) {
$allconditions = $allconditions | Where-Object IsSystemObject -eq $false
}
if ($Condition) {
$allconditions = $allconditions | Where-Object Name -in $Condition
}
foreach ($currentcondition in $allconditions) {
Write-Message -Level Verbose -Message "Processing $currentcondition"
Add-Member -Force -InputObject $currentcondition -MemberType NoteProperty ComputerName -value $store.ComputerName
Add-Member -Force -InputObject $currentcondition -MemberType NoteProperty InstanceName -value $store.InstanceName
Add-Member -Force -InputObject $currentcondition -MemberType NoteProperty SqlInstance -value $store.SqlInstance
Select-DefaultView -InputObject $currentcondition -Property ComputerName, InstanceName, SqlInstance, Id, Name, CreateDate, CreatedBy, DateModified, Description, ExpressionNode, Facet, HasScript, IsSystemObject, ModifiedBy
}
}
}
}
function Get-DbaPbmObjectSet {
<#
.SYNOPSIS
Returns object sets from policy based management.
.DESCRIPTION
Returns object sets from policy based management.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 ObjectSet
Filters results to only show specific object set
.PARAMETER IncludeSystemObject
By default system objects are filtered out. Use this parameter to include them.
.PARAMETER InputObject
Allows piping from Get-DbaPbmStore
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Policy, PolicyBasedManagement, PBM
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPbmObjectSet
.EXAMPLE
PS C:\> Get-DbaPbmObjectSet -SqlInstance sql2016
Returns all object sets from the sql2016 PBM instance
.EXAMPLE
PS C:\> Get-DbaPbmObjectSet -SqlInstance sql2016 -SqlCredential $cred
Uses a credential $cred to connect and return all object sets from the sql2016 PBM instance
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[string[]]$ObjectSet,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Dmf.PolicyStore[]]$InputObject,
[switch]$IncludeSystemObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaPbmStore -SqlInstance $instance -SqlCredential $SqlCredential
}
foreach ($store in $InputObject) {
$all = $store.ObjectSets
if (-not $IncludeSystemObject) {
$all = $all | Where-Object IsSystemObject -eq $false
}
if ($ObjectSet) {
$all = $all | Where-Object Name -in $ObjectSet
}
foreach ($currentset in $all) {
Write-Message -Level Verbose -Message "Processing $currentset"
Add-Member -Force -InputObject $currentset -MemberType NoteProperty ComputerName -value $store.ComputerName
Add-Member -Force -InputObject $currentset -MemberType NoteProperty InstanceName -value $store.InstanceName
Add-Member -Force -InputObject $currentset -MemberType NoteProperty SqlInstance -value $store.SqlInstance
Select-DefaultView -InputObject $currentset -Property ComputerName, InstanceName, SqlInstance, Id, Name, Facet, TargetSets, IsSystemObject
}
}
}
}
function Get-DbaPbmPolicy {
<#
.SYNOPSIS
Returns policies from Policy-Based Management from an instance.
.DESCRIPTION
Returns details of policies with the option to filter on Category and SystemObjects.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 InputObject
Allows piping from Get-DbaPbmStore
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Policy, PolicyBasedManagement, PBM
Author: Stephen Bennett, https://sqlnotesfromtheunderground.wordpress.com/
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPbmPolicy
.EXAMPLE
PS C:\> Get-DbaPbmPolicy -SqlInstance sql2016
Returns all policies from sql2016 server
.EXAMPLE
PS C:\> Get-DbaPbmPolicy -SqlInstance sql2016 -SqlCredential $cred
Uses a credential $cred to connect and return all policies from sql2016 instance
.EXAMPLE
PS C:\> Get-DbaPbmPolicy -SqlInstance sql2016 -Category MorningCheck
Returns all policies from sql2016 server that part of the PolicyCategory MorningCheck
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[string[]]$Policy,
[string[]]$Category,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Dmf.PolicyStore[]]$InputObject,
[switch]$IncludeSystemObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaPbmStore -SqlInstance $instance -SqlCredential $SqlCredential
}
foreach ($store in $InputObject) {
$allpolicies = $store.Policies
if (-not $IncludeSystemObject) {
$allpolicies = $allpolicies | Where-Object IsSystemObject -eq $false
}
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 $store.ComputerName
Add-Member -Force -InputObject $currentpolicy -MemberType NoteProperty InstanceName -value $store.InstanceName
Add-Member -Force -InputObject $currentpolicy -MemberType NoteProperty SqlInstance -value $store.SqlInstance
Select-DefaultView -InputObject $currentpolicy -ExcludeProperty HelpText, HelpLink, Urn, Properties, Metadata, Parent, IdentityKey, HasScript, PolicyEvaluationStarted, ConnectionProcessingStarted, TargetProcessed, ConnectionProcessingFinished, PolicyEvaluationFinished, PropertyMetadataChanged, PropertyChanged
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaPolicy
}
}
function Get-DbaPbmStore {
<#
.SYNOPSIS
Returns the policy based management store.
.DESCRIPTION
Returns the policy based management store.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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
Tags: Policy, PolicyBasedManagement, PBM
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPbmStore
.EXAMPLE
PS C:\> Get-DbaPbmStore -SqlInstance sql2016
Return the policy store from the sql2016 instance
.EXAMPLE
PS C:\> Get-DbaPbmStore -SqlInstance sql2016 -SqlCredential $cred
Uses a credential $cred to connect and return the policy store from the sql2016 instance
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
$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 $instance -Continue
}
Add-Member -Force -InputObject $store -MemberType NoteProperty ComputerName -value $server.ComputerName
Add-Member -Force -InputObject $store -MemberType NoteProperty InstanceName -value $server.ServiceName
Add-Member -Force -InputObject $store -MemberType NoteProperty SqlInstance -value $server.DomainInstanceName
Select-DefaultView -InputObject $store -ExcludeProperty SqlStoreConnection, ConnectionContext, Properties, Urn, Parent, DomainInstanceName, Metadata, IdentityKey, Name
}
}
}
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 target SQL Server instance or instances. Defaults to localhost.
.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 ExcludeSystemObjects
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPermission
.EXAMPLE
PS C:\> Get-DbaPermission -SqlInstance ServerA\sql987
Returns a custom object with Server name, Database name, permission state, permission type, grantee and securable.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaPermission -SqlInstance ServerA\sql987 -ExcludeSystemObjects -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
PS C:\> Get-DbaPermission -SqlInstance sql2016 -Database master
Returns a custom object with permissions for the master database.
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstance[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[switch]$IncludeServerLevel,
[switch]$ExcludeSystemObjects,
[Alias('Silent')]
[switch]$EnableException
)
begin {
if ($ExcludeSystemObjects) {
$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) {
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-Message -Level Warning -Message "The database $db is not accessible. Skipping database."
Continue
}
Write-Message -Level Debug -Message "T-SQL: $DBPermsql"
$db.ExecuteWithResults($DBPermsql).Tables.Rows
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPfAvailableCounter
.EXAMPLE
PS C:\> Get-DbaPfAvailableCounter
Gets all available counters on the local machine.
.EXAMPLE
PS C:\> Get-DbaPfAvailableCounter -Pattern *sql*
Gets all counters matching sql on the local machine.
.EXAMPLE
PS C:\> Get-DbaPfAvailableCounter -ComputerName sql2017 -Pattern *sql*
Gets all counters matching sql on the remote server sql2017.
.EXAMPLE
PS C:\> Get-DbaPfAvailableCounter -Pattern *sql*
Gets all counters matching sql on the local machine.
.EXAMPLE
PS C:\> 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) {
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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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: Performance, DataCollector, PerfCounter
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPfDataCollector
.EXAMPLE
PS C:\> Get-DbaPfDataCollector
Gets all Collectors on localhost.
.EXAMPLE
PS C:\> Get-DbaPfDataCollector -ComputerName sql2017
Gets all Collectors on sql2017.
.EXAMPLE
PS C:\> Get-DbaPfDataCollector -ComputerName sql2017, sql2016 -Credential ad\sqldba -CollectorSet 'System Correlation'
Gets all Collectors for the 'System Correlation' CollectorSet on sql2017 and sql2016 using alternative credentials.
.EXAMPLE
PS C:\> 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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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: Performance, DataCollector, PerfCounter
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPfDataCollectorCounter
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorCounter
Gets all counters for all Collector Sets on localhost.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorCounter -ComputerName sql2017
Gets all counters for all Collector Sets on on sql2017.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorCounter -ComputerName sql2017 -Counter '\Processor(_Total)\% Processor Time'
Gets the '\Processor(_Total)\% Processor Time' counter on sql2017.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorCounter -ComputerName sql2017, sql2016 -Credential ad\sqldba -CollectorSet 'System Correlation'
Gets all counters for the 'System Correlation' CollectorSet on sql2017 and sql2016 using alternative credentials.
.EXAMPLE
PS C:\> 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 {
#Variable marked as unused by PSScriptAnalyzer
#$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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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: Performance, DataCollector, PerfCounter
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPfDataCollectorCounterSample
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorCounterSample
Gets a single sample for all counters for all Collector Sets on localhost.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorCounterSample -Counter '\Processor(_Total)\% Processor Time'
Gets a single sample for all counters for all Collector Sets on localhost.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaPfDataCollectorCounterSample -ComputerName sql2017
Gets a single sample for all counters for all Collector Sets on sql2017.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorCounterSample -ComputerName sql2017, sql2016 -Credential ad\sqldba -CollectorSet 'System Correlation'
Gets a single sample for all counters for the 'System Correlation' CollectorSet on sql2017 and sql2016 using alternative credentials.
.EXAMPLE
PS C:\> 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
}
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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: Performance, DataCollector, PerfCounter
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPfDataCollectorSet
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorSet
Gets all Collector Sets on localhost.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorSet -ComputerName sql2017
Gets all Collector Sets on sql2017.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorSet -ComputerName sql2017 -Credential ad\sqldba -CollectorSet 'System Correlation'
Gets the 'System Correlation' CollectorSet on sql2017 using alternative credentials.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorSet | Select-Object *
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 {
<# DO NOT use Write-Message as this is inside of a script block #>
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) {
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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPfDataCollectorSetTemplate
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorSetTemplate
Returns information about all the templates in the local dbatools repository.
.EXAMPLE
PS C:\> 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
PS C:\> 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 adhoc and prepared plan cache for each database, if over 100 MB you should consider using Remove-DbaQueryPlan to clear the plan caches or turning on "optimize for adhoc workloads" configuration if 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 or 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: Memory
Author: Tracy Boggiano, databasesuperhero.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPlanCache
.EXAMPLE
PS C:\> Get-DbaPlanCache -SqlInstance sql2017
Returns the single use plan cache usage information for SQL Server instance 2017
.EXAMPLE
PS C:\> Get-DbaPlanCache -SqlInstance sql2017 -SqlCredential sqladmin
Returns the single use plan cache 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 {
$server = Connect-SqlInstance -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-DbaPowerPlan {
<#
.SYNOPSIS
Gets the Power Plan settings for compliance with best practices, which recommend High Performance for SQL Server.
.DESCRIPTION
Gets the Power Plan settings on a computer against best practices recommendations.
.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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPowerPlan
.EXAMPLE
PS C:\> Get-DbaPowerPlan -ComputerName sql2017
Gets the Power Plan settings for sql2017
.EXAMPLE
PS C:\> Get-DbaPowerPlan -ComputerName sql2017 -Credential ad\admin
Gets the Power Plan settings for sql2017 using an alternative credential
#>
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlInstance")]
[DbaInstance[]]$ComputerName,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $ComputerName) {
$server = Resolve-DbaNetworkName -ComputerName $computer -Credential $Credential
$computerResolved = $server.FullComputerName
if (-not $computerResolved) {
Stop-Function -Message "Couldn't resolve hostname. Skipping." -Continue
}
$splatDbaCmObject = @{
ComputerName = $computerResolved
EnableException = $true
}
if (Test-Bound "Credential") {
$splatDbaCmObject["Credential"] = $Credential
}
Write-Message -Level Verbose -Message "Getting Power Plan information from $computer."
try {
$powerPlans = Get-DbaCmObject @splatDbaCmObject -ClassName Win32_PowerPlan -Namespace "root\cimv2\power" | 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 ($null -eq $powerPlan.InstanceID) {
$powerPlan.ElementName = "Unknown"
}
[PSCustomObject]@{
ComputerName = $computer
InstanceId = $powerPlan.InstanceID
PowerPlan = $powerPlan.ElementName
Credential = $Credential
} | Select-DefaultView -ExcludeProperty Credential, InstanceId
}
}
}
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 target SQL Server instance or 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: Privilege
Author: Klaas Vandenberghe (@PowerDBAKlaas)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaPrivilege
.EXAMPLE
PS C:\> Get-DbaPrivilege -ComputerName sqlserver2014a
Gets the local privileges on computer sqlserver2014a.
.EXAMPLE
PS C:\> 'sql1','sql2','sql3' | Get-DbaPrivilege
Gets the local privileges on computers sql1, sql2 and sql3.
.EXAMPLE
PS C:\> 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 {
$null = Test-PSRemoting -ComputerName $Computer -EnableException
} catch {
Stop-Function -Message "Failure on $computer" -ErrorRecord $_ -Continue
}
try {
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'
}
}
if ($Priv.count -eq 0) {
Write-Message -Level Verbose -Message "No users with Batch Logon, Instant File Initialization, or Lock Pages in Memory Rights on $computer"
}
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("");
$blEntries = (Get-Content $temp\secpolByDbatools.cfg | Where-Object {
$_ -like "SeBatchLogonRight*"
})
if ($null -ne $blEntries) {
$blEntries.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("");
$ifiEntries = (Get-Content $temp\secpolByDbatools.cfg | Where-Object {
$_ -like 'SeManageVolumePrivilege*'
})
if ($null -ne $ifiEntries) {
$ifiEntries.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("");
$lpimEntries = (Get-Content $temp\secpolByDbatools.cfg | Where-Object {
$_ -like 'SeLockMemoryPrivilege*'
})
if ($null -ne $lpimEntries) {
$lpimEntries.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 = $_
LogonAsBatch = $BL -contains $_
InstantFileInitialization = $ifi -contains $_
LockPagesInMemory = $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
}
} 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 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 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 ExcludeSystemSpids
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaProcess
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[int[]]$Spid,
[int[]]$ExcludeSpid,
[string[]]$Database,
[string[]]$Login,
[string[]]$Hostname,
[string[]]$Program,
[switch]$ExcludeSystemSpids,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $sqlinstance) {
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 ($ExcludeSystemSpids -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
}
}
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Get-DbaProductKey {
<#
.SYNOPSIS
Gets SQL Server Product Keys from local or destination SQL Servers. Works with SQL Server 2005-2017
.DESCRIPTION
This command find the product key for all installed instances. Clustered instances are supported as well.
Uses key decoder by Jakob Bindslet (http://goo.gl/1jiwcB)
.PARAMETER ComputerName
The target SQL Server instance or instances.
.PARAMETER Credential
Login to the target Windows instance using alternative credentials. Windows Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER SqlCredential
This command logs into the SQL instance to gather additional information.
Use this parameter to connect to the discovered SQL instances 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: ProductKey
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaProductKey
.EXAMPLE
PS C:\> Get-DbaProductKey -ComputerName winxp, sqlservera, sqlserver2014a, win2k8
Gets SQL Server versions, editions and product keys for all instances within each server or workstation.
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline, Mandatory)]
[Alias("SqlInstance")]
[DbaInstanceParameter[]]$ComputerName,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[switch]$EnableException
)
begin {
$scriptblock = {
$versionMajor = $args[0]
$instanceReg = $args[1]
$edition = $args[2]
Function Unlock-SqlInstanceKey {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[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
}
$localmachine = [Microsoft.Win32.RegistryHive]::LocalMachine
$defaultview = [Microsoft.Win32.RegistryView]::Default
$reg = [Microsoft.Win32.RegistryKey]::OpenBaseKey($localmachine, $defaultview)
switch ($versionMajor) {
9 {
$sqlversion = "SQL Server 2005 $servicePack"
$findkeys = $reg.OpenSubKey("$($instanceReg.Path)\ProductID", $false)
foreach ($findkey in $findkeys.GetValueNames()) {
if ($findkey -like "DigitalProductID*") {
$key = @("$($instanceReg.Path)\ProductID\$findkey")
}
}
}
10 {
$sqlversion = "SQL Server 2008 $servicePack"
if ($server.VersionMinor -eq 50) {
$sqlversion = "SQL Server 2008 R2 $servicePack"
}
$key = @("$($instanceReg.Path)\Setup\DigitalProductID")
}
11 {
$key = @("$($instanceReg.Path)\Setup\DigitalProductID", "$($instanceReg.Path)\ClientSetup\DigitalProductID")
$sqlversion = "SQL Server 2012 $servicePack"
}
12 {
$key = @("$($instanceReg.Path)\Setup\DigitalProductID", "$($instanceReg.Path)\ClientSetup\DigitalProductID")
$sqlversion = "SQL Server 2014 $servicePack"
}
13 {
$key = @("$($instanceReg.Path)\Setup\DigitalProductID", "$($instanceReg.Path)\ClientSetup\DigitalProductID")
$sqlversion = "SQL Server 2016 $servicePack"
}
14 {
$key = @("$($instanceReg.Path)\Setup\DigitalProductID", "$($instanceReg.Path)\ClientSetup\DigitalProductID")
$sqlversion = "SQL Server 2017 $servicePack"
}
default {
Stop-Function -Message "SQL version not currently supported." -Continue
}
}
if ($edition -notlike "*Express*") {
$sqlkey = ''
foreach ($k in $key) {
$subkey = Split-Path $k
$binaryvalue = Split-Path $k -Leaf
try {
$binarykey = $($reg.OpenSubKey($subkey)).GetValue($binaryvalue)
break
} catch {
$binarykey = $null
}
}
if ($null -eq $binarykey) {
$sqlkey = "Could not read Product Key from registry on $env:COMPUTERNAME"
} else {
try {
$sqlkey = Unlock-SqlInstanceKey $binarykey $versionMajor
} catch {
$sqlkey = "Unable to unlock key"
}
}
} else {
$sqlkey = "SQL Server Express Edition"
}
[pscustomobject]@{
Version = $sqlversion
Key = $sqlkey
}
$reg.Close()
}
}
process {
foreach ($computer in $ComputerName) {
try {
$registryroot = Get-DbaRegistryRoot -ComputerName $computer.ComputerName -EnableException
} catch {
Stop-Function -Message "Can't access registry for $($computer.ComputerName). Is the Remote Registry service started?" -Continue
}
if (-not $registryroot) {
Stop-Function -Message "No instances found on $($computer.ComputerName)" -Continue
}
# Get Product Keys for all instances on the server.
foreach ($instanceReg in $registryroot) {
try {
$server = Connect-SqlInstance -SqlInstance $instanceReg.SqlInstance -SqlCredential $SqlCredential -MinimumVersion 10
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instanceReg.SqlInstance -Continue
}
$servicePack = $server.ProductLevel
$versionMajor = $server.VersionMajor
Write-Message -Level Debug -Message "$instance $instanceName version is $($server.VersionMajor)"
try {
$results = Invoke-Command2 -ComputerName $computer.ComputerName -Credential $Credential -ScriptBlock $scriptblock -ArgumentList $server.VersionMajor, $instanceReg, $server.Edition
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
}
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Version = $results.Version
Edition = $server.Edition
Key = $results.Key
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-SqlServerKey
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaSqlProductKey
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
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 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 ExcludeSystem
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaQueryExecutionTime
.EXAMPLE
PS C:\> Get-DbaQueryExecutionTime -SqlInstance sql2008, sqlserver2012
Return the top 100 slowest stored procedures or statements for servers sql2008 and sqlserver2012.
.EXAMPLE
PS C:\> Get-DbaQueryExecutionTime -SqlInstance sql2008 -Database TestDB
Return the top 100 slowest stored procedures or statements on server sql2008 for only the TestDB database.
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]
$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[parameter(Position = 1)]
[int]$MaxResultsPerDb = 100,
[parameter(Position = 2)]
[int]$MinExecs = 100,
[parameter(Position = 3)]
[int]$MinExecMs = 500,
[parameter(Position = 4)]
[Alias("ExcludeSystemDatabases")]
[switch]$ExcludeSystem,
[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) {
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 ($ExcludeSystem) {
$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
}
}
}
}
}
function Get-DbaRegistryRoot {
<#
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaRegistryRoot
Gets the registry root for all instances on localhost
.EXAMPLE
PS C:\> Get-DbaRegistryRoot -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) {
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.ComputerName)\$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"
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaSqlRegistryRoot
}
}
function Get-DbaRepDistributor {
<#
.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
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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Replication
Author: William Durkin (@sql_williamd)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaRepDistributor
.EXAMPLE
PS C:\> Get-DbaRepDistributor -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) {
# 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
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaDistributor
}
}
function Get-DbaRepPublication {
<#
.SYNOPSIS
Displays all publications for a server or database.
.DESCRIPTION
Quickly find all transactional, merge, and snapshot publications on a specific server or database.
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.PARAMETER Database
The database(s) to process. If unspecified, all databases will be processed.
.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER PublicationType
Limit by specific type of publication. Valid choices include: Transactional, Merge, Snapshot
.PARAMETER EnableException
byng this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Replication
Author: Colin Douglas
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaRepPublication
.EXAMPLE
PS C:\> Get-DbaRepPublication -SqlInstance sql2008, sqlserver2012
Return all publications for servers sql2008 and sqlserver2012.
.EXAMPLE
PS C:\> Get-DbaRepPublication -SqlInstance sql2008 -Database TestDB
Return all publications on server sql2008 for only the TestDB database
.EXAMPLE
PS C:\> Get-DbaRepPublication -SqlInstance sql2008 -PublicationType Transactional
Return all publications on server sql2008 for all databases that have Transactional publications
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[object[]]$Database,
[PSCredential]$SqlCredential,
[ValidateSet("Transactional", "Merge", "Snapshot")]
[object[]]$PublicationType,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
# Connect to Publisher
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$dbList = $server.Databases
if ($Database) {
$dbList = $dbList | Where-Object name -in $Database
}
$dbList = $dbList | Where-Object { ($_.ID -gt 4) -and ($_.status -ne "Offline") }
foreach ($db in $dbList) {
if (($db.ReplicationOptions -ne "Published") -and ($db.ReplicationOptions -ne "MergePublished")) {
Write-Message -Level Verbose -Message "Skipping $($db.name). Database is not published."
}
$repDB = Connect-ReplicationDB -Server $server -Database $db
$pubTypes = $repDB.TransPublications + $repDB.MergePublications
if ($PublicationType) {
$pubTypes = $pubTypes | Where-Object Type -in $PublicationType
}
foreach ($pub in $pubTypes) {
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.InstanceName
SqlInstance = $server.SqlInstance
Server = $server.name
Database = $db.name
PublicationName = $pub.Name
PublicationType = $pub.Type
}
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaRepServer {
<#
.SYNOPSIS
Gets a replication server object
.DESCRIPTION
Gets a replication server object
.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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Replication
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaRepServer -SqlInstance sql2016
Gets the replication server object for sql2016 using Windows authentication
.EXAMPLE
PS C:\> Get-DbaRepServer -SqlInstance sql2016 -SqlCredential repadmin
Gets the replication server object for sql2016 using SQL authentication
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
New-Object Microsoft.SqlServer.Replication.ReplicationServer $server.ConnectionContext.SqlConnectionObject
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
}
}
}
function Get-DbaResourceGovernor {
<#
.SYNOPSIS
Gets the Resource Governor object
.DESCRIPTION
Gets the Resource Governor object
.PARAMETER SqlInstance
The target SQL Server instance or instances
.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: ResourceGovernor
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaResourceGovernor
.EXAMPLE
PS C:\> Get-DbaResourceGovernor -SqlInstance sql2016
Gets the resource governor object of the SqlInstance sql2016
.EXAMPLE
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaResourceGovernor
Gets the resource governor object on Sql1 and Sql2/sqlexpress instances
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -MinimumVersion 10
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$resourcegov = $server.ResourceGovernor
if ($resourcegov) {
Add-Member -Force -InputObject $resourcegov -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
Add-Member -Force -InputObject $resourcegov -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
Add-Member -Force -InputObject $resourcegov -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
}
Select-DefaultView -InputObject $resourcegov -Property ComputerName, InstanceName, SqlInstance, ClassifierFunction, Enabled, MaxOutstandingIOPerVolume, ReconfigurePending, ResourcePools, ExternalResourcePools
}
}
}
function Get-DbaRgClassifierFunction {
<#
.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 or instances
.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials
.PARAMETER InputObject
Allows input to be piped from Get-DbaResourceGovernor
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaRgClassifierFunction
.EXAMPLE
PS C:\> Get-DbaRgClassifierFunction -SqlInstance sql2016
Gets the classifier function from sql2016
.EXAMPLE
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaResourceGovernor | Get-DbaRgClassifierFunction
Gets the classifier function object on Sql1 and Sql2/sqlexpress instances
#>
[CmdletBinding()]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.ResourceGovernor[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaResourceGovernor -SqlInstance $SqlInstance -SqlCredential $SqlCredential
}
foreach ($resourcegov in $InputObject) {
$server = $resourcegov.Parent
$classifierFunction = $null
foreach ($currentFunction in $server.Databases["master"].UserDefinedFunctions) {
$fullyQualifiedFunctionName = [string]::Format("[{0}].[{1}]", $currentFunction.Schema, $currentFunction.Name)
if ($fullyQualifiedFunctionName -eq $InputObject.ClassifierFunction) {
$classifierFunction = $currentFunction
}
}
if ($classifierFunction) {
Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name ComputerName -value $resourcegov.ComputerName
Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name InstanceName -value $resourcegov.InstanceName
Add-Member -Force -InputObject $classifierFunction -MemberType NoteProperty -Name SqlInstance -value $resourcegov.SqlInstance
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
}
}
}
function Get-DbaRgResourcePool {
<#
.SYNOPSIS
Gets Resource Governor Pool objects, including internal or external
.DESCRIPTION
Gets Resource Governor Pool objects, including internal or external
.PARAMETER SqlInstance
The target SQL Server instance or instances
.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials
.PARAMETER InputObject
Allows input to be piped from Get-DbaResourceGovernor
.PARAMETER Type
Internal or External
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: ResourceGovernor
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaRgResourcePool
.EXAMPLE
PS C:\> Get-DbaRgResourcePool -SqlInstance sql2016
Gets the internal resource pools on sql2016
.EXAMPLE
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaResourceGovernor | Get-DbaRgResourcePool
Gets the internal resource pools on Sql1 and Sql2/sqlexpress instances
.EXAMPLE
PS C:\> 'Sql1','Sql2/sqlexpress' | Get-DbaResourceGovernor | Get-DbaRgResourcePool -Type External
Gets the external resource pools on Sql1 and Sql2/sqlexpress instances
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[ValidateSet("Internal", "External")]
[string]$Type = "Internal",
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.ResourceGovernor[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaResourceGovernor -SqlInstance $SqlInstance -SqlCredential $SqlCredential
}
foreach ($resourcegov in $InputObject) {
if ($Type -eq "External") {
$respool = $resourcegov.ExternalResourcePools
if ($respool) {
$respool | Add-Member -Force -MemberType NoteProperty -Name ComputerName -value $resourcegov.ComputerName
$respool | Add-Member -Force -MemberType NoteProperty -Name InstanceName -value $resourcegov.InstanceName
$respool | Add-Member -Force -MemberType NoteProperty -Name SqlInstance -value $resourcegov.SqlInstance
$respool | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Id, Name, CapCpuPercentage, IsSystemObject, MaximumCpuPercentage, MaximumIopsPerVolume, MaximumMemoryPercentage, MinimumCpuPercentage, MinimumIopsPerVolume, MinimumMemoryPercentage, WorkloadGroups
}
} else {
$respool = $resourcegov.ResourcePools
if ($respool) {
$respool | Add-Member -Force -MemberType NoteProperty -Name ComputerName -value $resourcegov.ComputerName
$respool | Add-Member -Force -MemberType NoteProperty -Name InstanceName -value $resourcegov.InstanceName
$respool | Add-Member -Force -MemberType NoteProperty -Name SqlInstance -value $resourcegov.SqlInstance
$respool | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Id, Name, CapCpuPercentage, IsSystemObject, MaximumCpuPercentage, MaximumIopsPerVolume, MaximumMemoryPercentage, MinimumCpuPercentage, MinimumIopsPerVolume, MinimumMemoryPercentage, WorkloadGroups
}
}
}
}
}
function Get-DbaRgWorkloadGroup {
<#
.SYNOPSIS
Gets all Resource Governor Pool objects, including both internal and external
.DESCRIPTION
Gets all Resource Governor Pool objects, including both internal and external
.PARAMETER SqlInstance
The target SQL Server instance or instances
.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials
.PARAMETER InputObject
Allows input to be piped from Get-DbaRgResourcePool
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: ResourceGovernor
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaRgWorkloadGroup
.EXAMPLE
PS C:\> Get-DbaRgWorkloadGroup -SqlInstance sql2017
Gets the workload groups on sql2017
.EXAMPLE
PS C:\> Get-DbaResourceGovernor -SqlInstance sql2017 | Get-DbaRgResourcePool | Get-DbaRgWorkloadGroup
Gets the workload groups on sql2017
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.ResourcePool[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaRgResourcePool -SqlInstance $SqlInstance -SqlCredential $SqlCredential
}
foreach ($pool in $InputObject) {
$group = $pool.WorkloadGroups
if ($group) {
$group | Add-Member -Force -MemberType NoteProperty -Name ComputerName -value $pool.ComputerName
$group | Add-Member -Force -MemberType NoteProperty -Name InstanceName -value $pool.InstanceName
$group | Add-Member -Force -MemberType NoteProperty -Name SqlInstance -value $pool.SqlInstance
$group | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, Id, Name, ExternalResourcePoolName, GroupMaximumRequests, Importance, IsSystemObject, MaximumDegreeOfParallelism, RequestMaximumCpuTimeInSeconds, RequestMaximumMemoryGrantPercentage, RequestMemoryGrantTimeoutInSeconds
}
}
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
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
.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 InputObject
Enables piped input from Get-DbaAgentJob
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaRunningJob
.EXAMPLE
PS C:\> Get-DbaRunningJob -SqlInstance sql2017
Returns any active jobs on sql2017
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance sql2017, sql2019 | Get-DbaRunningJob
Returns all active jobs on multiple instances piped into the function.
.EXAMPLE
PS C:\> $servers | Get-DbaRunningJob
Returns all active jobs on multiple instances piped into the function.
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Agent.Job[]]$InputObject,
[switch]$EnableException
)
process {
if ($SqlInstance) {
Get-DbaAgentJob -SqlInstance $SqlInstance -SqlCredential $SqlCredential | Where-Object CurrentRunStatus -ne 'Idle'
}
$InputObject | Where-Object CurrentRunStatus -ne 'Idle'
}
}
#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 time frame
Only works with SQL 2005 and later, as the system trace didn't exist before then
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 beginning 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaSchemaChangeHistory
.EXAMPLE
PS C:\> Get-DbaSchemaChangeHistory -SqlInstance localhost
Returns all DDL changes made in all databases on the SQL Server instance localhost since the system trace began
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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, ValueFromPipeline)]
[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) {
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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaServerAudit
.EXAMPLE
PS C:\> Get-DbaServerAudit -SqlInstance localhost
Returns all Security Audits on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaServerAudit -SqlInstance localhost, sql2016
Returns all Security Audits for the local and sql2016 SQL Server instances
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[string[]]$Audit,
[string[]]$ExcludeAudit,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaServerAuditSpecification
.EXAMPLE
PS C:\> Get-DbaServerAuditSpecification -SqlInstance localhost
Returns all Security Audit Specifications on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaServerAuditSpecification -SqlInstance localhost, sql2016
Returns all Security Audit Specifications for the local and sql2016 SQL Server instances
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -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.
.DESCRIPTION
This command returns:
SqlInstallDate
WindowsInstallDate (use -IncludeWindows)
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.PARAMETER SqlCredential
Credential object used to connect to the SQL Server using SQL Authentication as a different user
.PARAMETER Credential
Credential object used to connect to the SQL Server as a different Windows 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: Install
Author: Mitchell Hamann (@SirCaptainMitch), mitchellhamann.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaServerInstallDate
.EXAMPLE
PS C:\> Get-DbaServerInstallDate -SqlInstance SqlBox1\Instance2
Returns an object with SQL Instance Install date as a string.
.EXAMPLE
PS C:\> Get-DbaServerInstallDate -SqlInstance winserver\sqlexpress, sql2016
Returns an object with SQL Instance Install date as a string for both SQLInstances that are passed to the cmdlet.
.EXAMPLE
PS C:\> 'sqlserver2014a', 'sql2016' | Get-DbaServerInstallDate
Returns an object with SQL Instance Install date as a string for both SQLInstances that are passed to the cmdlet via the pipeline.
.EXAMPLE
PS C:\> Get-DbaServerInstallDate -SqlInstance sqlserver2014a, sql2016 -IncludeWindows
Returns an object with the Windows Install date and the SQL install date as a string.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "ComputerName")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]
$SqlCredential,
[PSCredential]
$Credential,
[Switch]$IncludeWindows,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$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 target SQL Server instance or 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaServerProtocol
.EXAMPLE
PS C:\> Get-DbaServerProtocol -ComputerName sqlserver2014a
Gets the SQL Server related server protocols on computer sqlserver2014a.
.EXAMPLE
PS C:\> 'sql1','sql2','sql3' | Get-DbaServerProtocol
Gets the SQL Server related server protocols on computers sql1, sql2 and sql3.
.EXAMPLE
PS C:\> 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
PS C:\> (Get-DbaServerProtocol -ComputerName sql1 | Where { $_.DisplayName = 'via' }).Disable()
Disables the VIA ServerNetworkProtocol on computer sql1.
If successful, return code 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 target SQL Server instance or instances. 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
Author: Shawn Melton (@wsmelton)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaServerRole
.EXAMPLE
PS C:\> Get-DbaServerRole -SqlInstance sql2016a
Outputs list of server-level roles for sql2016a instance.
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$ServerRole,
[string[]]$ExcludeServerRole,
[switch]$ExcludeFixedRole,
[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
}
$serverroles = $server.Roles
if ($ServerRole) {
$serverroles = $serverroles | Where-Object Name -In $ServerRole
}
if ($ExcludeServerRole) {
$serverroles = $serverroles | Where-Object Name -NotIn $ExcludeServerRole
}
if ($ExcludeFixedRole) {
$serverroles = $serverroles | Where-Object IsFixedRole -eq $false
}
foreach ($role in $serverroles) {
$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-DbaServerRoleMember {
<#
.SYNOPSIS
Get members of server roles for each instance(s) of SQL Server.
.DESCRIPTION
The Get-DbaServerRoleMember returns connected SMO object for server roles for each instance(s) of SQL Server.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 alternate Windows or SQL Login Authentication. Accepts credential objects (Get-Credential).
.PARAMETER ServerRole
The role(s) to process. If unspecified, all roles will be processed.
.PARAMETER ExcludeServerRole
The role(s) to exclude.
.PARAMETER Login
The login(s) to process. If unspecified, all logins will be processed.
.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, Login
Author: Klaas Vandenberghe (@PowerDBAKlaas)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaServerRoleMember
.EXAMPLE
PS C:\> Get-DbaServerRoleMember -SqlInstance localhost
Returns all members of all server roles on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaServerRoleMember -SqlInstance localhost, sql2016
Returns all members of all server roles on the local and sql2016 SQL Server instances
.EXAMPLE
PS C:\> $servers = Get-Content C:\servers.txt
PS C:\> $servers | Get-DbaServerRoleMember
Returns all members of all server roles for every server in C:\servers.txt
.EXAMPLE
PS C:\> Get-DbaServerRoleMember -SqlInstance localhost -ServerRole 'sysadmin', 'dbcreator'
Returns all members of the sysadmin or dbcreator roles on localhost.
.EXAMPLE
PS C:\> Get-DbaServerRoleMember -SqlInstance localhost -ExcludeServerRole 'sysadmin'
Returns all members of server-level roles other than sysadmin.
.EXAMPLE
PS C:\> Get-DbaServerRoleMember -SqlInstance sql2017a -ExcludeFixedRole
Returns all members of server-level role(s) that are not fixed roles on sql2017a instance.
.EXAMPLE
PS C:\> Get-DbaServerRoleMember -SqlInstance localhost -Login 'MyFriendlyDeveloper'
Returns all server-level role(s) for the MyFriendlyDeveloper login on localhost.
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias('ServerInstance', 'SqlServer')]
[DbaInstance[]]$SqlInstance,
[Alias('Credential')]
[PSCredential]$SqlCredential,
[string[]]$ServerRole,
[string[]]$ExcludeServerRole,
[object[]]$Login,
[switch]$ExcludeFixedRole,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
Write-Message -Level Verbose -Message "Attempting to connect to $instance"
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message 'Failure' -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$roles = $server.Roles
if (Test-Bound -ParameterName 'Login') {
$logins = Get-DbaLogin -SqlInstance $instance -Login $Login
Write-Message -Level 'Verbose' -Message "Filtering by logins: $($logins -join ', ')"
foreach ($l in $logins) {
$loginRoles += $l.ListMembers()
}
$loginRoles = $loginRoles | Select-Object -Unique
Write-Message -Level 'Verbose' -Message "Filtering by roles: $($loginRoles -join ', ')"
$roles = $roles | Where-Object { $_.Name -in $loginRoles }
}
if (Test-Bound -ParameterName 'ServerRole') {
$roles = $roles | Where-Object { $_.Name -in $ServerRole }
}
if (Test-Bound -ParameterName 'ExcludeServerRole') {
$roles = $roles | Where-Object { $_.Name -notin $ExcludeServerRole }
}
if (Test-Bound -ParameterName 'ExcludeFixedRole') {
$roles = $roles | Where-Object { $_.IsFixedRole -eq $false }
}
foreach ($role in $roles) {
Write-Message -Level 'Verbose' -Message "Getting Server Role Members for $role on $instance"
$members = $role.EnumMemberNames()
Write-Message -Level 'Verbose' -Message "$role members: $($members -join ', ')"
if (Test-Bound -ParameterName 'Login') {
Write-Message -Level 'Verbose' -Message "Only returning results for $($logins.Name -join ', ')"
$members = $members | Where-Object { $_ -in $logins.Name }
}
foreach ($member in $members) {
$l = $server.Logins | Where-Object { $_.Name -eq $member }
if ($l) {
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name Role -Value $role.Name
# Select object because Select-DefaultView causes strange behaviors when assigned to a variable (??)
Select-Object -InputObject $l -Property 'ComputerName', 'InstanceName', 'SqlInstance', 'Role', 'Name'
}
}
}
}
}
}
function Get-DbaServerTrigger {
<#
.SYNOPSIS
Get all existing server triggers on one or more SQL instances.
.DESCRIPTION
Get all existing server triggers on one or more SQL instances.
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.PARAMETER SqlCredential
SqlCredential 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: Database, Trigger
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaServerTrigger
.EXAMPLE
PS C:\> Get-DbaServerTrigger -SqlInstance sql2017
Returns all server triggers on sql2017
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[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
}
foreach ($trigger in $server.Triggers) {
try {
Add-Member -Force -InputObject $trigger -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
Add-Member -Force -InputObject $trigger -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
Add-Member -Force -InputObject $trigger -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
Select-DefaultView -InputObject $trigger -Property ComputerName, InstanceName, SqlInstance, ID, Name, AnsiNullsStatus, AssemblyName, BodyStartIndex, ClassName, CreateDate, DateLastModified, DdlTriggerEvents, ExecutionContext, ExecutionContextLogin, ImplementationType, IsDesignMode, IsEnabled, IsEncrypted, IsSystemObject, MethodName, QuotedIdentifierStatus, State, TextHeader, TextMode
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
function Get-DbaService {
<#
.SYNOPSIS
Gets the SQL Server related services on a computer.
.DESCRIPTION
Gets the SQL Server related services on one or more computers.
Requires Local Admin rights on destination computer(s).
.PARAMETER ComputerName
The target SQL Server instance or 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", "PolyBase"
.PARAMETER ServiceName
Can be used to specify service names explicitly, without looking for service types/instances.
.PARAMETER AdvancedProperties
Collect additional properties from the SqlServiceAdvancedProperty Namespace
This collects information about Version, Service Pack Level", SkuName, Clustered status and the Cluster Service Name
This adds additional overhead to 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: Klaas Vandenberghe ( @PowerDBAKlaas )
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaService
.EXAMPLE
PS C:\> Get-DbaService -ComputerName sqlserver2014a
Gets the SQL Server related services on computer sqlserver2014a.
.EXAMPLE
PS C:\> 'sql1','sql2','sql3' | Get-DbaService -AdvancedProperties
Gets the SQL Server related services on computers sql1, sql2 and sql3. Includes Advanced Properties from the SqlServiceAdvancedProperty Namespace
.EXAMPLE
PS C:\> $cred = Get-Credential WindowsUser
PS C:\> Get-DbaService -ComputerName sql1,sql2 -Credential $cred | Out-GridView
Gets the SQL Server related services on computers sql1 and sql2 via the user WindowsUser, and shows them in a grid view.
.EXAMPLE
PS C:\> Get-DbaService -ComputerName sql1,sql2 -InstanceName MSSQLSERVER
Gets the SQL Server related services related to the default instance MSSQLSERVER on computers sql1 and sql2.
.EXAMPLE
PS C:\> Get-DbaService -ComputerName $MyServers -Type SSRS
Gets the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.
.EXAMPLE
PS C:\> $MyServers = Get-Content .\servers.txt
PS C:\> Get-DbaService -ComputerName $MyServers -ServiceName MSSQLSERVER,SQLSERVERAGENT
Gets the SQL Server related services with ServiceName MSSQLSERVER or SQLSERVERAGENT for all the servers that are stored in the file. Every line in the file can only contain one hostname for a server.
.EXAMPLE
PS C:\> $services = Get-DbaService -ComputerName sql1 -Type Agent,Engine
PS C:\> $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
PS C:\> (Get-DbaService -ComputerName sql1 -Type Engine).Restart($true)
Calls a Restart method for each Engine service on computer sql1.
#>
[CmdletBinding(DefaultParameterSetName = "Search")]
param (
[parameter(ValueFromPipeline, 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", "PolyBase")]
[string[]]$Type,
[Parameter(ParameterSetName = "ServiceName")]
[string[]]$ServiceName,
[switch]$AdvancedProperties,
[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 = "PolyBase"; Id = 10, 11 },
@{ 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
$outputServices = @()
if (!$Type -or 'SSRS' -in $Type) {
Write-Message -Level Verbose -Message "Getting SQL Reporting Server services on $Computer" -Target $Computer
$reportingServices = Get-DbaReportingService -ComputerName $Computer -InstanceName $InstanceName -Credential $Credential -ServiceName $ServiceName
$outputServices += $reportingServices
}
Write-Message -Level Verbose -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 {
# here to avoid an empty catch
$null = 1
}
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 -Message "Failed to acquire services from namespace $($namespace.Name)." -Target $Computer -ErrorRecord $_
}
}
}
#use highest namespace available
$services = ($servicesTemp | Group-Object Name | ForEach-Object { $_.Group | Sort-Object Namespace -Descending | Select-Object -First 1 }).Service
#remove services returned by the SSRS namespace
$services = $services | Where-Object ServiceName -notin $reportingServices.ServiceName
#Add custom properties and methods to the service 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-DbaService -InputObject $this -Force:$Force
}
Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "Start" -Value { Start-DbaService -InputObject $this }
Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "Restart" -Value {
param ([bool]$Force = $false)
Restart-DbaService -InputObject $this -Force:$Force
}
Add-Member -Force -InputObject $service -MemberType ScriptMethod -Name "ChangeStartMode" -Value {
param (
[parameter(Mandatory)]
[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-DbaService'
Return
}
Set-ServiceStartMode -InputObject $this -Mode $Mode -ErrorAction Stop
$this.StartMode = $Mode
}
if ($AdvancedProperties) {
$namespaceValue = $service.CimClass.ToString().ToUpper().Replace(":SQLSERVICE", "").Replace("ROOT/MICROSOFT/SQLSERVER/", "")
$serviceAdvancedProperties = Get-DbaCmObject -ComputerName $Computer -Namespace "root\Microsoft\SQLServer\$($namespaceValue)" -Query "SELECT * FROM SqlServiceAdvancedProperty WHERE ServiceName = '$($service.ServiceName)'"
Add-Member -Force -InputObject $service -MemberType NoteProperty -Name Version -Value ($serviceAdvancedProperties | Where-Object PropertyName -eq 'VERSION' ).PropertyStrValue
Add-Member -Force -InputObject $service -MemberType NoteProperty -Name SPLevel -Value ($serviceAdvancedProperties | Where-Object PropertyName -eq 'SPLEVEL' ).PropertyNumValue
Add-Member -Force -InputObject $service -MemberType NoteProperty -Name SkuName -Value ($serviceAdvancedProperties | Where-Object PropertyName -eq 'SKUNAME' ).PropertyStrValue
$ClusterServiceTypeList = @(1, 2, 5, 7)
if ($ClusterServiceTypeList -contains $service.SQLServiceType) {
Add-Member -Force -InputObject $service -MemberType NoteProperty -Name Clustered -Value ($serviceAdvancedProperties | Where-Object PropertyName -eq 'CLUSTERED' ).PropertyNumValue
Add-Member -Force -InputObject $service -MemberType NoteProperty -Name VSName -Value ($serviceAdvancedProperties | Where-Object PropertyName -eq 'VSNAME' ).PropertyStrValue
} else {
Add-Member -Force -InputObject $service -MemberType NoteProperty -Name Clustered -Value ''
Add-Member -Force -InputObject $service -MemberType NoteProperty -Name VSName -Value ''
}
}
$outputServices += $service
}
}
if ($AdvancedProperties) {
$defaults = "ComputerName", "ServiceName", "ServiceType", "InstanceName", "DisplayName", "StartName", "State", "StartMode", "Version", "SPLevel", "SkuName", "Clustered", "VSName"
} else {
$defaults = "ComputerName", "ServiceName", "ServiceType", "InstanceName", "DisplayName", "StartName", "State", "StartMode"
}
if ($outputServices) {
$outputServices | Select-DefaultView -Property $defaults -TypeName DbaSqlService
} else {
Stop-Function -Message "No services found in relevant namespaces on $Computer. Please note that this function is available from SQL 2005 up."
}
} else {
Stop-Function -EnableException $EnableException -Message "Failed to connect to $Computer" -Continue
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaSqlService
}
}
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
The target SQL Server instance or instances. 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. Name can be either values from (sys.configuration/sp_configure) or from SMO object
.PARAMETER ExcludeName
Exclude specific configurations. Name can be either values from (sys.configuration/sp_configure) or from SMO 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: SpConfig, Configure, Configuration
Author: Nic Cain, https://sirsql.net/
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaSpConfigure
.INPUTS
A DbaInstanceParameter representing an array of SQL Server instances.
.OUTPUTS
Returns PSCustomObject with properties ServerName, ComputerName, InstanceName, SqlInstance, Name, DisplayName, Description, IsAdvanced, IsDynamic, MinValue, MaxValue, ConfiguredValue, RunningValue, DefaultValue, IsRunningDefaultValue
.EXAMPLE
PS C:\> Get-DbaSpConfigure -SqlInstance localhost
Returns all system configuration information on the localhost.
.EXAMPLE
PS C:\> 'localhost','localhost\namedinstance' | Get-DbaSpConfigure
Returns system configuration information on multiple instances piped into the function
.EXAMPLE
PS C:\> Get-DbaSpConfigure -SqlInstance sql2012 -Name 'max server memory (MB)'
Returns only the system configuration for MaxServerMemory on sql2012.
.EXAMPLE
PS C:\> Get-DbaSpConfigure -SqlInstance sql2012 -ExcludeName 'max server memory (MB)', RemoteAccess | Out-GridView
Returns system configuration information on sql2012 but excludes for max server memory (MB) and remote access. Values returned in grid view
.EXAMPLE
PS C:\> $cred = Get-Credential SqlCredential
PS C:\> 'sql2012' | Get-DbaSpConfigure -SqlCredential $cred -Name RemoteAccess, 'max server memory (MB)' -ExcludeName 'remote access' | Out-GridView
Returns system configuration information on sql2012 using SQL Server Authentication. Only MaxServerMemory is returned as RemoteAccess was also excluded.
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Config", "ConfigName")]
[string[]]$Name,
[string[]]$ExcludeName,
[switch]$EnableException
)
begin {
$smoName = [pscustomobject]@{
"Ad Hoc Distributed Queries" = "AdHocDistributedQueriesEnabled"
"affinity I/O mask" = "AffinityIOMask"
"affinity mask" = "AffinityMask"
"affinity64 I/O mask" = "Affinity64IOMask"
"affinity64 mask" = "Affinity64Mask"
"Agent XPs" = "AgentXPsEnabled"
"allow updates" = "AllowUpdates"
"awe enabled" = "AweEnabled"
"backup compression default" = "DefaultBackupCompression"
"blocked process threshold" = "BlockedProcessThreshold"
"blocked process threshold (s)" = "BlockedProcessThreshold"
"c2 audit mode" = "C2AuditMode"
"clr enabled" = "IsSqlClrEnabled"
"common criteria compliance enabled" = "CommonCriteriaComplianceEnabled"
"contained database authentication" = "ContainmentEnabled"
"cost threshold for parallelism" = "CostThresholdForParallelism"
"cross db ownership chaining" = "CrossDBOwnershipChaining"
"cursor threshold" = "CursorThreshold"
"Database Mail XPs" = "DatabaseMailEnabled"
"default full-text language" = "DefaultFullTextLanguage"
"default language" = "DefaultLanguage"
"default trace enabled" = "DefaultTraceEnabled"
"disallow results from triggers" = "DisallowResultsFromTriggers"
"EKM provider enabled" = "ExtensibleKeyManagementEnabled"
"filestream access level" = "FilestreamAccessLevel"
"fill factor (%)" = "FillFactor"
"ft crawl bandwidth (max)" = "FullTextCrawlBandwidthMax"
"ft crawl bandwidth (min)" = "FullTextCrawlBandwidthMin"
"ft notify bandwidth (max)" = "FullTextNotifyBandwidthMax"
"ft notify bandwidth (min)" = "FullTextNotifyBandwidthMin"
"index create memory (KB)" = "IndexCreateMemory"
"in-doubt xact resolution" = "InDoubtTransactionResolution"
"lightweight pooling" = "LightweightPooling"
"locks" = "Locks"
"max degree of parallelism" = "MaxDegreeOfParallelism"
"max full-text crawl range" = "FullTextCrawlRangeMax"
"max server memory (MB)" = "MaxServerMemory"
"max text repl size (B)" = "ReplicationMaxTextSize"
"max worker threads" = "MaxWorkerThreads"
"media retention" = "MediaRetention"
"min memory per query (KB)" = "MinMemoryPerQuery"
"min server memory (MB)" = "MinServerMemory"
"nested triggers" = "NestedTriggers"
"network packet size (B)" = "NetworkPacketSize"
"Ole Automation Procedures" = "OleAutomationProceduresEnabled"
"open objects" = "OpenObjects"
"optimize for ad hoc workloads" = "OptimizeAdhocWorkloads"
"PH timeout (s)" = "ProtocolHandlerTimeout"
"precompute rank" = "PrecomputeRank"
"priority boost" = "PriorityBoost"
"query governor cost limit" = "QueryGovernorCostLimit"
"query wait (s)" = "QueryWait"
"recovery interval (min)" = "RecoveryInterval"
"remote access" = "RemoteAccess"
"remote admin connections" = "RemoteDacConnectionsEnabled"
"remote data archive" = "RemoteDataArchiveEnabled"
"remote login timeout (s)" = "RemoteLoginTimeout"
"remote proc trans" = "RemoteProcTrans"
"remote query timeout (s)" = "RemoteQueryTimeout"
"Replication XPs" = "ReplicationXPsEnabled"
"scan for startup procs" = "ScanForStartupProcedures"
"server trigger recursion" = "ServerTriggerRecursionEnabled"
"set working set size" = "SetWorkingSetSize"
"show advanced options" = "ShowAdvancedOptions"
"SMO and DMO XPs" = "SmoAndDmoXPsEnabled"
"SQL Mail XPs" = "SqlMailXPsEnabled"
"transform noise words" = "TransformNoiseWords"
"two digit year cutoff" = "TwoDigitYearCutoff"
"user connections" = "UserConnections"
"user options" = "UserOptions"
"Web Assistant Procedures" = "WebXPsEnabled"
"xp_cmdshell" = "XPCmdShellEnabled"
# Configurations without defined names - Created dummy entries
"access check cache bucket count" = "AccessCheckCacheBucketCount"
"access check cache quota" = "AccessCheckCacheQuota"
"allow polybase export" = "AllowPolybaseExport"
"automatic soft-NUMA disabled" = "AutomaticSoftnumaDisabled"
"backup checksum default" = "BackupChecksumDefault"
"clr strict security" = "ClrStrictSecurity"
"external scripts enabled" = "ExternalScriptsEnabled"
"hadoop connectivity" = "HadoopConnectivity"
"polybase network encryption" = "PolybaseNetworkEncryption"
"User Instance Timeout" = "UserInstanceTimeout"
"user instances enabled" = "UserInstancesEnabled"
}
}
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
} catch {
Stop-Function -Message "Failed to process Instance $Instance" -ErrorRecord $_ -Target $instance -Continue
}
#Get a list of the configuration Properties. This collection matches entries in sys.configurations
try {
$proplist = $server.Configuration.Properties
} catch {
Stop-Function -Message "Unable to gather configuration properties $instance" -Target $instance -ErrorRecord $_ -Continue
}
if ($Name) {
$proplist = $proplist | Where-Object { ($_.DisplayName -in $Name -or ($smoName).$($_.DisplayName) -in $Name) }
}
if (Test-Bound "ExcludeName") {
$proplist = $proplist | Where-Object { ($_.DisplayName -NotIn $ExcludeName -and ($smoName).$($_.DisplayName) -NotIn $ExcludeName) }
}
#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) {
$defaultConfig = $defaultConfigs | Where-Object { $_.Name -eq $prop.DisplayName };
if ($defaultConfig.Value -eq $prop.RunValue) { $isDefault = $true }
else { $isDefault = $false }
#Ignores properties that are not valid on this version of SQL
if (!([string]::IsNullOrEmpty($prop.RunValue))) {
$DisplayName = $prop.DisplayName
[pscustomobject]@{
ServerName = $server.Name
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Name = ($smoName).$DisplayName
DisplayName = $DisplayName
Description = $prop.Description
IsAdvanced = $prop.IsAdvanced
IsDynamic = $prop.IsDynamic
MinValue = $prop.Minimum
MaxValue = $prop.Maximum
ConfiguredValue = $prop.ConfigValue
RunningValue = $prop.RunValue
DefaultValue = $defaultConfig.Value
IsRunningDefaultValue = $isDefault
Parent = $server
ConfigName = ($smoName).$DisplayName
} | Select-DefaultView -ExcludeProperty ServerName, Parent, ConfigName
}
}
}
}
}
function Get-DbaSpinLockStatistic {
<#
.SYNOPSIS
Displays information from sys.dm_os_spinlock_stats. Works on SQL Server 2008 and above.
.DESCRIPTION
This command is based off of Paul Randal's post "Advanced SQL Server performance tuning"
Returns:
SpinLockName
Collisions
Spins
SpinsPerCollision
SleepTime
Backoffs
Reference: https://www.sqlskills.com/blogs/paul/advanced-performance-troubleshooting-waits-latches-spinlocks/
.PARAMETER SqlInstance
The SQL Server instance. 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: SpinLockStatistics, Waits
Author: Patrick Flynn (@sqllensman)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaSpinLockStatistic
.EXAMPLE
PS C:\> Get-DbaSpinLockStatistic -SqlInstance sql2008, sqlserver2012
Get SpinLock Statistics for servers sql2008 and sqlserver2012.
.EXAMPLE
PS C:\> $output = Get-DbaSpinLockStatistic -SqlInstance sql2008 | Select * | ConvertTo-DbaDataTable
Collects all SpinLock Statistics on server sql2008 into a Data Table.
.EXAMPLE
PS C:\> 'sql2008','sqlserver2012' | Get-DbaSpinLockStatistic
Get SpinLock Statistics for servers sql2008 and sqlserver2012 via pipline
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Get-DbaSpinLockStatistic -SqlInstance sql2008 -SqlCredential $cred
Connects using sqladmin credential and returns SpinLock Statistics from sql2008
#>
[CmdletBinding()]
Param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias('Silent')]
[switch]$EnableException
)
BEGIN {
$sql = "SELECT
name,
collisions,
spins,
spins_per_collision,
sleep_time,
backoffs
FROM sys.dm_os_spinlock_stats;"
Write-Message -Level Debug -Message $sql
}
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 10
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
Write-Message -Level Verbose -Message "Connected to $instance"
foreach ($row in $server.Query($sql)) {
[PSCustomObject]@{
ComputerName = $server.NetName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
SpinLockName = $row.name
Collisions = $row.collisions
Spins = $row.spins
SpinsPerCollision = $row.spins_per_collision
SleepTime = $row.sleep_time
Backoffs = $row.backoffs
}
}
}
}
}
#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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaSpn
.EXAMPLE
PS C:\> Get-DbaSpn -ComputerName SQLSERVERA -Credential ad\sqldba
Returns a custom object with SearchTerm (ServerName) and the SPNs that were found
.EXAMPLE
PS C:\> Get-DbaSpn -AccountName domain\account -Credential ad\sqldba
Returns a custom object with SearchTerm (domain account) and the SPNs that were found
.EXAMPLE
PS C:\> Get-DbaSpn -ComputerName SQLSERVERA,SQLSERVERB -Credential ad\sqldba
Returns a custom object with SearchTerm (ServerName) and the SPNs that were found for multiple computers
#>
[cmdletbinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "", Justification = "Internal functions are ignored")]
param (
[Parameter(ValueFromPipeline)]
[string[]]$ComputerName,
[string[]]$AccountName,
[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
}
#Variable marked as unused by PSScriptAnalyzer
# 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-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
The target SQL Server instance or instances.
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)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaSsisEnvironmentVariable
.EXAMPLE
PS C:\> Get-DbaSsisEnvironmentVariable -SqlInstance localhost -Environment DEV -Folder DWH_ETL
Gets variables of 'DEV' environment located in 'DWH_ETL' folder on 'localhost' Server
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 'localhost' | Get-DbaSsisEnvironmentVariable -EnvironmentExclude DEV, PROD
Gets all SSIS environments except 'DEV' and 'PROD' from 'localhost' server. The server name comes from pipeline
.EXAMPLE
PS C:\> 'SRV1', 'SRV3' | Get-DbaSsisEnvironmentVariable
Gets all SSIS environments from 'SRV1' and 'SRV3' servers. The server's names come from pipeline
.EXAMPLE
PS C:\> '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
PS C:\> '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,
[PSCredential]$SqlCredential,
[object[]]$Environment,
[object[]]$EnvironmentExclude,
[object[]]$Folder,
[object[]]$FolderExclude,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -MinimumVersion 11
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
try {
$ISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"
$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
The target SQL Server instance or instances.
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 Since
Datetime object used to narrow the results to a date
.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: Chris Tucker (@ChrisTuc47368095)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaSsisExecutionHistory
.EXAMPLE
PS C:\> Get-DbaSsisExecutionHistory -SqlInstance SMTQ01 -Folder SMTQ_PRC
Get all history items for SMTQ01 in folder SMTQ_PRC.
.EXAMPLE
PS C:\> Get-DbaSsisExecutionHistory -SqlInstance SMTQ01 -Status Failed,Cancelled
Gets all failed or canceled executions for SMTQ01.
.EXAMPLE
PS C:\> 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)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[datetime]$Since,
[ValidateSet("Created", "Running", "Cancelled", "Failed", "Pending", "Halted", "Succeeded", "Stopping", "Completed")]
[String[]]$Status,
[String[]]$Project,
[String[]]$Folder,
[String[]]$Environment,
[switch]$EnableException
)
begin {
$params = @{}
#build status parameter
$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 = "`n`t`tAND e.[Status] in ($csv)"
} else {
$statusq = ''
}
#construct parameterized collection predicate for project array
if ($Project) {
$projectq = "`n`t`tAND ( 1=0 "
$i = 0
foreach ($p in $Project) {
$i ++
$projectq += "`n`t`t`tOR e.[project_name] = @project$i"
$params.Add("project$i", $p)
}
$projectq += "`n`t`t)"
} else {
$projectq = ''
}
#construct parameterized collection predicate for folder array
if ($Folder) {
$folderq = "`n`t`tAND ( 1=0 "
$i = 0
foreach ($f in $Folder) {
$i ++
$folderq += "`n`t`t`tOR e.[folder_name] = @folder$i"
$params.Add("folder$i" , $f)
}
$folderq += "`n`t`t)"
} else {
$folderq = ''
}
#construct parameterized collection predicate for environment array
if ($Environment) {
$environmentq = "`n`t`tAND ( 1=0 "
$i = 0
foreach ($e in $Environment) {
$i ++
$environmentq += "`n`t`t`tOR e.[environment_name] = @environment$i"
$params.Add("environment$i" , $e)
}
$environmentq += "`n`t`t)"
} else {
$environmentq = ''
}
#construct date filter for since
if ($Since) {
$sinceq = "`n`t`tAND e.[start_time] >= @since"
$params.Add('since', $Since )
}
$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$sinceq
OPTION ( RECOMPILE );
"
#debug verbose output
Write-Message -Level Debug -Message "`nSQL statement: $sql"
$paramout = ($params | Out-String)
Write-Message -Level Debug -Message "`nParameters:$paramout"
}
process {
foreach ($instance in $SqlInstance) {
$results = Invoke-DbaQuery -SqlInstance $instance -Database SSISDB -Query $sql -as PSObject -SqlParameters $params -SqlCredential $SqlCredential
foreach ($row in $results) {
$row.StartTime = [dbadatetime]$row.StartTime.DateTime
$row.EndTime = [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 target SQL Server instance or instances.
.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.
.NOTES
Tags: WSMan, SQLWMI, Memory
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaStartupParameter
.EXAMPLE
PS C:\> Get-DbaStartupParameter -SqlInstance sql2014
Logs into SQL WMI as the current user then displays the values for numerous startup parameters.
.EXAMPLE
PS C:\> $wincred = Get-Credential ad\sqladmin
PS C:\> 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.
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline, Mandatory)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("SqlCredential")]
[PSCredential]$Credential,
[switch]$Simple,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$computerName = $instance.ComputerName
$instanceName = $instance.InstanceName
$ogInstance = $instance.FullSmoName
$computerName = (Resolve-DbaNetworkName -ComputerName $computerName).FullComputerName
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
The target SQL Server instance or instances
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaSuspectPage -SqlInstance sql2016
Retrieve any records stored for Suspect Pages on the sql2016 SQL Server.
.EXAMPLE
PS C:\> 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-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 -All 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 target SQL Server instance or instances.
.PARAMETER SqlCredential
Allows you to connect to servers using alternate Windows credentials
$scred = Get-Credential, then pass $scred object to the -SqlCredential parameter.
.PARAMETER All
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 Detailed
Output all properties, will be deprecated in 1.0.0 release. Use All instead.
.PARAMETER ExcludeIpv6
If this switch is enabled, IPv6 information is excluded from All 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaTcpPort
.EXAMPLE
PS C:\> Get-DbaTcpPort -SqlInstance sqlserver2014a
Returns just the port number for the default instance on sqlserver2014a.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaTcpPort -SqlInstance sqlserver2014a, sql2016 -All
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
PS C:\> Get-DbaCmsRegServer -SqlInstance sql2014 | Get-DbaTcpPort -ExcludeIpv6 -All
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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[switch]$Detailed,
[switch]$All,
[Alias("Ipv4")]
[switch]$ExcludeIpv6,
[Alias('Silent')]
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
}
process {
foreach ($instance in $SqlInstance) {
if ($All) {
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 {
# Shouldn't have an empty catch block
# Use write-verbose becaues it's remote and write-message may note exist
Write-Verbose -Message "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
}
}
}
[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
$computername = $resolved.FullComputerName
$fqdn = $resolved.Fqdn
try {
Write-Message -Level Verbose -Message "Trying with ComputerName ($computer)."
$someIps = Invoke-ManagedComputerCommand -ComputerName $computer -Credential $Credential -ArgumentList $computer -ScriptBlock $scriptblock
} catch {
Write-Message -Level Verbose -Message "Trying with FullComputerName because ComputerName failed."
$someIps = Invoke-ManagedComputerCommand -ComputerName $computername -Credential $Credential -ArgumentList $fqdn -ScriptBlock $scriptblock
}
} catch {
Stop-Function -Message "Could not get all information." -Target $instance -ErrorRecord $_
}
$results = $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$"
$results = $results | Where-Object { $_.IPAddress -match $ipv4 }
}
$results
}
#Default Execution of Get-DbaTcpPort
if (-not $All -or ($All -and ($null -eq $someIps))) {
try {
$server = Connect-SqlInstance -SqlInstance "TCP:$instance" -SqlCredential $SqlCredential -MinimumVersion 9
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target "TCP:$instance" -Continue
}
# WmiComputer can be unreliable :( Use T-SQL
$sql = "SELECT local_net_address,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
IPAddress = $port.local_net_address
Port = $port.local_tcp_port
Static = $true
Type = "Normal"
} | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, IPAddress, 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 target SQL Server instance or instances.
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaTempdbUsage
.EXAMPLE
PS C:\> Get-DbaTempdbUsage -SqlInstance localhost\SQLDEV2K14
Gets tempdb usage for localhost\SQLDEV2K14
#>
[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
}
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 Raw
By default, messages such as SQL statements are flattened. Use raw to see the output without flattened formatting.
.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
.NOTES
Tags: Debug
Author: Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbatoolsLog
.EXAMPLE
PS C:\> Get-DbatoolsLog
Returns all log entries currently in memory.
.EXAMPLE
PS C:\> Get-DbatoolsLog -Target "a" -Last 1 -Skip 1
Returns all log entries that targeted the object "a" in the second last execution sent.
.EXAMPLE
PS C:\> 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()]
[object]$Target,
[string[]]$Tag,
[int]$Last,
[int]$Skip = 0,
[guid]$Runspace,
[Sqlcollaborative.Dbatools.Message.MessageLevel[]]$Level,
[switch]$Raw,
[switch]$Errors
)
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
}
if ($Raw) {
return $messages
} else {
$messages | Select-Object -Property CallStack, ComputerName, File, FunctionName, Level, Line, @{
Name = "Message"
Expression = {
$msg = ($_.Message.Split("`n") -join " ")
do {
$msg = $msg.Replace(' ', ' ')
} until ($msg -notmatch ' ')
$msg
}
}, ModuleName, Runspace, Tags, TargetObject, Timestamp, Type, Username
}
}
}
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
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 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 returned 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaTopResourceUsage
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaTopResourceUsage -SqlInstance sql2016 -Limit 30
Return the highest usage by duration (top 30) and frequency (top 30) for the TestDB on sql2016
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaTopResourceUsage -SqlInstance sql2016| Select-Object *
Return all the columns plus the QueryPlan column
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[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) {
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
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaTrace -SqlInstance sql2016
Lists all the trace files on the sql2016 SQL Server.
.EXAMPLE
PS C:\> 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
The target SQL Server instance or instances. 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)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
References: https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql
.LINK
https://dbatools.io/Get-DbaTraceFlag
.EXAMPLE
PS C:\> Get-DbaTraceFlag -SqlInstance localhost
Returns all Trace Flag information on the local default SQL Server instance
.EXAMPLE
PS C:\> Get-DbaTraceFlag -SqlInstance localhost, sql2016
Returns all Trace Flag(s) for the local and sql2016 SQL Server instances
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[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-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 target SQL Server instance or instances.
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaUptime
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaCmsRegServer -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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "ComputerName")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[Alias('Silent')]
[switch]$EnableException
)
begin {
$nowutc = (Get-Date).ToUniversalTime()
}
process {
# uses cim commands
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 {
$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-DbaUserPermission {
<#
.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
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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaUserPermission
.EXAMPLE
PS C:\> Get-DbaUserPermission -SqlInstance sql2008, sqlserver2012
Check server and database permissions for servers sql2008 and sqlserver2012.
.EXAMPLE
PS C:\> Get-DbaUserPermission -SqlInstance sql2008 -Database TestDB
Check server and database permissions on server sql2008 for only the TestDB database
.EXAMPLE
PS C:\> Get-DbaUserPermission -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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[parameter(Position = 1)]
[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) {
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 {
# here to avoid an empty catch
$null = 1
} # 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 {
# here to avoid an empty catch
$null = 1
}
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 {
# here to avoid an empty catch
$null = 1
}
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 {
# here to avoid an empty catch
$null = 1
}
$sql = $sql.Replace($db.Name, "<TARGETDB>")
#Sashay Away
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Get-DbaUserLevelPermission
}
}
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 target SQL Server instance or instances. 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), https://wsmelton.github.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWaitingTask
.EXAMPLE
PS C:\> Get-DbaWaitingTask -SqlInstance sqlserver2014a
Returns the waiting task for all sessions on sqlserver2014a
.EXAMPLE
PS C:\> Get-DbaWaitingTask -SqlInstance sqlserver2014a -IncludeSystemSpid
Returns the waiting task for all sessions (user and system) on sqlserver2014a
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[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) {
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 target SQL Server instance or instances.
.PARAMETER SqlCredential
Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.
.PARAMETER WaitResource
The wait resource 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.
.NOTES
Tags: Pages, DBCC
Author: Stuart Moore (@napalmgram), stuart-moore.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWaitResource
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaWaitResource -SqlInstance server2 -WaitResource 'KEY: 7:35457594073541168 (de21f92a1572)'
Will return an object containing; database name, schema name and index name which is being waited on.
.EXAMPLE
PS C:\> Get-DbaWaitResource -SqlInstance 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.
#>
[CmdletBinding()]
param (
[parameter(Mandatory)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstance]$SqlInstance,
[PsCredential]$SqlCredential,
[parameter(Mandatory, ValueFromPipeline)]
[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 target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWaitStatistic
.EXAMPLE
PS C:\> Get-DbaWaitStatistic -SqlInstance sql2008, sqlserver2012
Check wait statistics for servers sql2008 and sqlserver2012
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaWaitStatistic -SqlInstance sql2008 | Select *
Shows detailed notes, if available, from Paul's post
.EXAMPLE
PS C:\> $output = Get-DbaWaitStatistic -SqlInstance sql2008 -Threshold 100 -IncludeIgnorable | Select-Object * | ConvertTo-DbaDataTable
Collects all Wait Statistics (including ignorable waits) on server sql2008 into a Data Table.
.EXAMPLE
PS C:\> $output = Get-DbaWaitStatistic -SqlInstance sql2008
PS C:\> 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(Mandatory, ValueFromPipeline)]
[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 Brent 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],
Case WHEN SUM ([wait_time_ms]) OVER() = 0 THEN NULL ELSE 100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() END 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],
Case WHEN SUM ([wait_time_ms]) OVER() = 0 THEN NULL ELSE 100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() END 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) {
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 | Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWindowsLog
.EXAMPLE
PS C:\> $ErrorLogs = Get-DbaWindowsLog -SqlInstance sql01\sharepoint
PS C:\> $ErrorLogs | Where-Object ErrorNumber -eq 18456
Returns all lines in the errorlogs that have event number 18456 in them
#>
#This exists to ignore the Script Analyzer rule for Start-Runspace
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[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 {
# here to avoid an empty catch
$null = 1
}
}
#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,
[PSCredential]
$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()
$defaultrunspace = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace
$RunspacePool = [RunspaceFactory]::CreateRunspacePool($InitialSessionState)
$RunspacePool.SetMinRunspaces(1) | Out-Null
if ($MaxThreads -gt 0) { $null = $RunspacePool.SetMaxRunspaces($MaxThreads) }
$RunspacePool.Open()
$countStarted = 0
#Variable marked as unused by PSScriptAnalyzer
#$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()
[System.Management.Automation.Runspaces.Runspace]::DefaultRunspace = $defaultrunspace
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaWsfcAvailableDisk {
<#
.SYNOPSIS
Gets information about the disks that can support Failover Clustering and are visible to all nodes, but are not yet part of the set of clustered disks.
.DESCRIPTION
Gets information about the disks that can support Failover Clustering and are visible to all nodes, but are not yet part of the set of clustered disks.
All Windows Server Failover Clustering (Wsfc) commands require local admin on each member node.
.PARAMETER ComputerName
The target cluster name. Can be a node or the cluster name itself.
.PARAMETER Credential
Allows you to login to the cluster 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: Cluster, WSFC, FCI, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWsfcAvailableDisk
.EXAMPLE
PS C:\> Get-DbaWsfcAvailableDisk -ComputerName cluster01
Gets available disks from the failover cluster cluster01
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $computername) {
$cluster = Get-DbaWsfcCluster -ComputerName $computer -Credential $Credential
$disk = Get-DbaCmObject -Computername $computer -Credential $Credential -Namespace root\MSCluster -ClassName MSCluster_AvailableDisk
# I don't have an available disk, so I can't see how to clean this up: Passthru
$disk | Add-Member -Force -NotePropertyName State -NotePropertyValue (Get-ResourceState $resource.State)
$disk | Add-Member -Force -NotePropertyName ClusterName -NotePropertyValue $cluster.Name
$disk | Add-Member -Force -NotePropertyName ClusterFqdn -NotePropertyValue $cluster.Fqdn -PassThru
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaWsfcCluster {
<#
.SYNOPSIS
Gets information about one or more failover clusters in a given domain.
.DESCRIPTION
Gets information about one or more failover clusters in a given domain.
All Windows Server Failover Clustering (Wsfc) commands require local admin on each member node.
.PARAMETER ComputerName
The target cluster name. Can be a node or the cluster name itself.
.PARAMETER Credential
Allows you to login to the cluster 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: Cluster, WSFC, FCI, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWsfcCluster
.EXAMPLE
PS C:\> Get-DbaWsfcCluster -ComputerName cluster01
Gets failover cluster information about cluster01
.EXAMPLE
PS C:\> Get-DbaWsfcCluster -ComputerName cluster01 | Select *
Shows all cluster values, including the ones not shown in the default view
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $computername) {
$cluster = Get-DbaCmObject -Computername $computer -Credential $Credential -Namespace root\MSCluster -ClassName MSCluster_Cluster
$cluster | Add-Member -Force -NotePropertyName State -NotePropertyValue (Get-ResourceState $resource.State)
$cluster | Select-DefaultView -Property Name, Fqdn, State, DrainOnShutdown, DynamicQuorumEnabled, EnableSharedVolumes, SharedVolumesRoot, QuorumPath, QuorumType, QuorumTypeValue, RequestReplyTimeout
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaWsfcDisk {
<#
.SYNOPSIS
Gets information about the clustered disks on one or more failover clusters in a given domain.
.DESCRIPTION
Gets information about the clustered disks on one or more failover clusters in a given domain.
All Windows Server Failover Clustering (Wsfc) commands require local admin on each member node.
.PARAMETER ComputerName
The target cluster name. Can be a node or the cluster name itself.
.PARAMETER Credential
Allows you to login to the cluster 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: Cluster, WSFC, FCI, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWsfcDisk
.EXAMPLE
PS C:\> Get-DbaWsfcDisk -ComputerName cluster01
Gets disk information from the failover cluster cluster01
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $computername) {
$resources = Get-DbaWsfcResource -ComputerName $computer -Credential $Credential | Where-Object Type -eq 'Physical Disk'
foreach ($resource in $resources) {
$disks = $resource | Get-CimAssociatedInstance -ResultClassName MSCluster_Disk
foreach ($disk in $disks) {
$diskpart = $disk | Get-CimAssociatedInstance -ResultClassName MSCluster_DiskPartition
[pscustomobject]@{
ClusterName = $resource.ClusterName
ClusterFqdn = $resource.ClusterFqdn
ResourceGroup = $resource.OwnerGroup
Disk = $resource.Name
State = $resource.State
FileSystem = $diskpart.FileSystem
Path = $diskpart.Path
Label = $diskpart.VolumeLabel
Size = [dbasize]($diskpart.TotalSize * 1MB)
Free = [dbasize]($diskpart.FreeSpace * 1MB)
MountPoints = $diskpart.MountPoints
SerialNumber = $diskpart.SerialNumber
ClusterDisk = $disk
ClusterDiskPart = $diskpart
ClusterResource = $resource
} | Select-DefaultView -ExcludeProperty ClusterDisk, ClusterDiskPart, ClusterResource
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaWsfcNetwork {
<#
.SYNOPSIS
Gets information about one or more networks in a failover cluster.
.DESCRIPTION
Gets information about one or more networks in a failover cluster.
All Windows Server Failover Clustering (Wsfc) commands require local admin on each member node.
.PARAMETER ComputerName
The target cluster name. Can be a Network or the cluster name itself.
.PARAMETER Credential
Allows you to login to the cluster 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: Cluster, WSFC, FCI, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWsfcNetwork
.EXAMPLE
PS C:\> Get-DbaWsfcNetwork -ComputerName cluster01
Gets network information from the failover cluster cluster01
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $computername) {
$cluster = Get-DbaWsfcCluster -ComputerName $computer -Credential $Credential
$network = Get-DbaCmObject -Computername $computer -Credential $Credential -Namespace root\MSCluster -ClassName MSCluster_Network
$network | Add-Member -Force -NotePropertyName ClusterName -NotePropertyValue $cluster.Name
$network | Add-Member -Force -NotePropertyName ClusterFqdn -NotePropertyValue $cluster.Fqdn
$network | Select-DefaultView -Property ClusterName, ClusterFqdn, Name, Address, AddressMask, IPv4Addresses, IPv4PrefixLengths, IPv6Addresses, IPv6PrefixLengths, QuorumType, QuorumTypeValue, RequestReplyTimeout, Role
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaWsfcNetworkInterface {
<#
.SYNOPSIS
Gets information about one or more network adapters in a failover cluster.
.DESCRIPTION
Gets information about one or more network adapters in a failover cluster.
All Windows Server Failover Clustering (Wsfc) commands require local admin on each member node.
.PARAMETER ComputerName
The target cluster name. Can be a Network or the cluster name itself.
.PARAMETER Credential
Allows you to login to the cluster 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: Cluster, WSFC, FCI, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWsfcNetworkInterface
.EXAMPLE
PS C:\> Get-DbaWsfcNetworkInterface -ComputerName cluster01
Gets network interface information from the failover cluster cluster01
.EXAMPLE
PS C:\> Get-DbaWsfcNetworkInterface -ComputerName cluster01 | Select *
Shows all network interface values, including the ones not shown in the default view
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $computername) {
$cluster = Get-DbaWsfcCluster -ComputerName $computer -Credential $Credential
$network = Get-DbaCmObject -Computername $computer -Credential $Credential -Namespace root\MSCluster -ClassName MSCluster_NetworkInterface
$network | Add-Member -Force -NotePropertyName ClusterName -NotePropertyValue $cluster.Name
$network | Add-Member -Force -NotePropertyName ClusterFqdn -NotePropertyValue $cluster.Fqdn
$network | Select-DefaultView -Property ClusterName, ClusterFqdn, Name, Network, Node, Adapter, Address, DhcpEnabled, IPv4Addresses, IPv6Addresses, IPv6Addresses
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaWsfcNode {
<#
.SYNOPSIS
Gets information about one or more nodes, or servers, in a failover cluster.
.DESCRIPTION
Gets information about one or more nodes, or servers, in a failover cluster.
All Windows Server Failover Clustering (Wsfc) commands require local admin on each member node.
.PARAMETER ComputerName
The target cluster name. Can be a node or the cluster name itself.
.PARAMETER Credential
Allows you to login to the cluster 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: Cluster, WSFC, FCI, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWsfcNode
.EXAMPLE
PS C:\> Get-DbaWsfcNode -ComputerName cluster01
Gets node information from the failover cluster cluster01
.EXAMPLE
PS C:\> Get-DbaWsfcNode -ComputerName cluster01 | Select-Object *
Shows all node values, including the ones not shown in the default view
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $computername) {
$cluster = Get-DbaWsfcCluster -ComputerName $computer -Credential $Credential
$node = Get-DbaCmObject -Computername $computer -Credential $Credential -Namespace root\MSCluster -ClassName MSCluster_Node
$node | Add-Member -Force -NotePropertyName ClusterName -NotePropertyValue $cluster.Name
$node | Add-Member -Force -NotePropertyName ClusterFqdn -NotePropertyValue $cluster.Fqdn
$node | Select-DefaultView -Property ClusterName, ClusterFqdn, Name, PrimaryOwnerName, PrimaryOwnerContact, Dedicated, NodeHighestVersion, NodeLowestVersion
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaWsfcResource {
<#
.SYNOPSIS
Gets information about one or more resources in a failover cluster.
.DESCRIPTION
Gets information about one or more resources in a failover cluster.
All Windows Server Failover Clustering (Wsfc) commands require local admin on each member node.
.PARAMETER ComputerName
The target cluster name. Can be a node or the cluster name itself.
.PARAMETER Credential
Allows you to login to the cluster 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: Cluster, WSFC, FCI, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWsfcResource
.EXAMPLE
PS C:\> Get-DbaWsfcResource -ComputerName cluster01
Gets resource information from the failover cluster cluster01
.EXAMPLE
PS C:\> Get-DbaWsfcResource -ComputerName cluster01 | Select *
Shows all resource values, including the ones not shown in the default view
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $computername) {
$cluster = Get-DbaWsfcCluster -ComputerName $computer -Credential $Credential
$resources = Get-DbaCmObject -Computername $computer -Credential $Credential -Namespace root\MSCluster -ClassName MSCluster_Resource
foreach ($resource in $resources) {
$resource | Add-Member -Force -NotePropertyName State -NotePropertyValue (Get-ResourceState $resource.State)
$resource | Add-Member -Force -NotePropertyName ClusterName -NotePropertyValue $cluster.Name
$resource | Add-Member -Force -NotePropertyName ClusterFqdn -NotePropertyValue $cluster.Fqdn
$resource | Select-DefaultView -Property ClusterName, ClusterFqdn, Name, State, Type, OwnerGroup, OwnerNode, PendingTimeout, PersistentState, QuorumCapable, RequiredDependencyClasses, RequiredDependencyTypes, RestartAction, RestartDelay, RestartPeriod, RestartThreshold, RetryPeriodOnFailure, SeparateMonitor
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaWsfcResourceType {
<#
.SYNOPSIS
Gets information about one or more resource types in a failover cluster.
.DESCRIPTION
Gets information about one or more resource types in a failover cluster.
All Windows Server Failover Clustering (Wsfc) commands require local admin on each member node.
.PARAMETER ComputerName
The target cluster name. Can be a node or the cluster name itself.
.PARAMETER Credential
Allows you to login to the cluster 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: Cluster, WSFC, FCI, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWsfcResourceType
.EXAMPLE
PS C:\> Get-DbaWsfcResourceType -ComputerName cluster01
Gets resource type information from the failover cluster cluster01
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $computername) {
$cluster = Get-DbaWsfcCluster -ComputerName $computer -Credential $Credential
$resource = Get-DbaCmObject -Computername $computer -Credential $Credential -Namespace root\MSCluster -ClassName MSCluster_ResourceType
$resource | Add-Member -Force -NotePropertyName ClusterName -NotePropertyValue $cluster.Name
$resource | Add-Member -Force -NotePropertyName ClusterFqdn -NotePropertyValue $cluster.Fqdn
$resource | Select-DefaultView -Property ClusterName, ClusterFqdn, Name, DisplayName, DllName, RequiredDependencyTypes
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaWsfcRole {
<#
.SYNOPSIS
Gets information about one or more clustered roles (resource groups) in a failover cluster.
.DESCRIPTION
Gets information about one or more clustered roles (resource groups) in a failover cluster.
All Windows Server Failover Clustering (Wsfc) commands require local admin on each member node.
.PARAMETER ComputerName
The target cluster name. Can be a Role or the cluster name itself.
.PARAMETER Credential
Allows you to login to the cluster 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: Cluster, WSFC, FCI, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWsfcRole
.EXAMPLE
PS C:\> Get-DbaWsfcRole -ComputerName cluster01
Gets role information from the failover cluster cluster01
.EXAMPLE
PS C:\> Get-DbaWsfcRole -ComputerName cluster01 | Select *
Shows all role values, including the ones not shown in the default view
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $computername) {
$cluster = Get-DbaWsfcCluster -ComputerName $computer -Credential $Credential
$role = Get-DbaCmObject -Computername $computer -Credential $Credential -Namespace root\MSCluster -ClassName MSCluster_ResourceGroup
$role | Add-Member -Force -NotePropertyName State -NotePropertyValue (Get-ResourceState $resource.State)
$role | Add-Member -Force -NotePropertyName ClusterName -NotePropertyValue $cluster.Name
$role | Add-Member -Force -NotePropertyName ClusterFqdn -NotePropertyValue $cluster.Fqdn
$role | Select-DefaultView -Property ClusterName, ClusterFqdn, Name, OwnerNode, State
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaWsfcSharedVolume {
<#
.SYNOPSIS
Gets information about Cluster Shared Volumes in a failover cluster.
.DESCRIPTION
Gets information about Cluster Shared Volumes in a failover cluster.
All Windows Server Failover Clustering (Wsfc) commands require local admin on each member node.
.PARAMETER ComputerName
The target cluster name. Can be a node or the cluster name itself.
.PARAMETER Credential
Allows you to login to the cluster 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: Cluster, WSFC, FCI, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaWsfcSharedVolume
.EXAMPLE
PS C:\> Get-DbaWsfcSharedVolume -ComputerName cluster01
Gets shared volume (CSV) information from the failover cluster cluster01
#>
[CmdletBinding()]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
foreach ($computer in $computername) {
$cluster = Get-DbaWsfcCluster -ComputerName $computer -Credential $Credential
$volume = Get-DbaCmObject -Computername $computer -Credential $Credential -Namespace root\MSCluster -ClassName ClusterSharedVolume
# I don't have a shared volume, so I can't see how to clean this up: Passthru
$volume | Add-Member -Force -NotePropertyName ClusterName -NotePropertyValue $cluster.Name
$volume | Add-Member -Force -NotePropertyName ClusterFqdn -NotePropertyValue $cluster.Fqdn
$volume | Add-Member -Force -NotePropertyName State -NotePropertyValue (Get-ResourceState $resource.State) -PassThru
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaXEObject {
<#
.SYNOPSIS
Gets a list of extended events objects exposed by event packages from specified SQL Server instance(s).
.DESCRIPTION
This function returns a list of extended events objects exposed by event packages from specified SQL Server instance(s).
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Get-DbaXEObject -SqlInstance sql2016
Lists all the XE Objects on the sql2016 SQL Server.
.EXAMPLE
PS C:\> 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 $_
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaXESession
.EXAMPLE
PS C:\> Get-DbaXESession -SqlInstance ServerA\sql987
Returns a custom object with ComputerName, SQLInstance, Session, StartTime, Status and other properties.
.EXAMPLE
PS C:\> Get-DbaXESession -SqlInstance ServerA\sql987 | Format-Table ComputerName, SqlInstance, Session, Status -AutoSize
Returns a formatted table displaying ComputerName, SqlInstance, Session, and Status.
.EXAMPLE
PS C:\> '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, ValueFromPipeline)]
[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 {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11 -AzureUnsupported
$SqlConn = $server.ConnectionContext.SqlConnectionObject
$SqlStoreConnection = New-Object Microsoft.SqlServer.Management.Sdk.Sfc.SqlStoreConnection $SqlConn
$XEStore = New-Object Microsoft.SqlServer.Management.XEvent.XEStore $SqlStoreConnection
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaXESessionTarget
.EXAMPLE
PS C:\> Get-DbaXESessionTarget -SqlInstance ServerA\sql987 -Session system_health
Shows targets for the system_health session on ServerA\sql987.
.EXAMPLE
PS C:\> Get-DbaXESession -SqlInstance sql2016 -Session system_health | Get-DbaXESessionTarget
Returns the targets for the system_health session on sql2016.
.EXAMPLE
PS C:\> 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
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaXESessionTargetFile
.EXAMPLE
PS C:\> Get-DbaXESessionTargetFile -SqlInstance sql2017 -Session 'Long Running Queries'
Shows Target Files for the 'Long Running Queries' session on sql2017.
.EXAMPLE
PS C:\> 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 $_
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 Grafe (@ChrGraefe) 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaXESessionTemplate
.EXAMPLE
PS C:\> Get-DbaXESessionTemplate
Returns information about all the templates in the local dbatools repository.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaXESessionTemplate -Path "$home\Documents\SQL Server Management Studio\Templates\XEventTemplates"
Returns information about all the templates in your local XEventTemplates repository.
.EXAMPLE
PS C:\> Get-DbaXESessionTemplate -Pattern duration
Returns information about all the templates that match the word "duration" in the title, category or body.
.EXAMPLE
PS C:\> 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
}
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl) | SmartTarget by Gianluca Sartori (@spaghettidba)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaXESmartTarget
.EXAMPLE
PS C:\> 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 $_
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Get-DbaXEStore {
<#
.SYNOPSIS
Get a Extended Events store
.DESCRIPTION
Get a Extended Events store
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Get-DbaXEStore
.EXAMPLE
PS C:\> 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 {
$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
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Grant-DbaAgPermission {
<#
.SYNOPSIS
Grants endpoint and availability group permissions to a login.
.DESCRIPTION
Grants endpoint and availability group permissions to a login. If the account is a Windows login and does not exist, it will be automatically added.
.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 Login
The login or logins to modify.
.PARAMETER AvailabilityGroup
Only modify specific availability groups.
.PARAMETER Type
Specify type: Endpoint or AvailabilityGroup. Endpoint will modify the DatabaseMirror endpoint type.
.PARAMETER Permission
Grants one or more permissions:
Alter
Connect
Control
CreateSequence
CreateAnyDatabase
Delete
Execute
Impersonate
Insert
Receive
References
Select
Send
TakeOwnership
Update
ViewChangeTracking
ViewDefinition
CreateAnyDatabase
Connect is default.
.PARAMETER InputObject
Enables piping from Get-DbaLogin.
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Grant-DbaAgPermission
.EXAMPLE
PS C:\> Grant-DbaAgPermission -SqlInstance sql2017a -Type AvailabilityGroup -AvailabilityGroup SharePoint -Login ad\spservice -Permission CreateAnyDatabase
Adds CreateAnyDatabase permissions to ad\spservice on the SharePoint availability group on sql2017a. Does not prompt for confirmation.
.EXAMPLE
PS C:\> Grant-DbaAgPermission -SqlInstance sql2017a -Type AvailabilityGroup -AvailabilityGroup ag1, ag2 -Login ad\spservice -Permission CreateAnyDatabase -Confirm
Adds CreateAnyDatabase permissions to ad\spservice on the ag1 and ag2 availability groups on sql2017a. Prompts for confirmation.
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance sql2017a | Out-GridView -Passthru | Grant-DbaAgPermission -Type EndPoint
Grants the selected logins Connect permissions on the DatabaseMirroring endpoint for sql2017a
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Login,
[string[]]$AvailabilityGroup,
[parameter(Mandatory)]
[ValidateSet('Endpoint', 'AvailabilityGroup')]
[string[]]$Type,
[ValidateSet('Alter', 'Connect', 'Control', 'CreateAnyDatabase', 'CreateSequence', 'Delete', 'Execute', 'Impersonate', 'Insert', 'Receive', 'References', 'Select', 'Send', 'TakeOwnership', 'Update', 'ViewChangeTracking', 'ViewDefinition')]
[string[]]$Permission = "Connect",
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Login[]]$InputObject,
[switch]$EnableException
)
process {
if ($SqlInstance -and -not $Login -and -not $AvailabilityGroup) {
Stop-Function -Message "You must specify one or more logins when using the SqlInstance parameter."
return
}
if ($Type -contains "AvailabilityGroup" -and -not $AvailabilityGroup) {
Stop-Function -Message "You must specify at least one availability group when using the AvailabilityGroup type."
return
}
foreach ($instance in $SqlInstance) {
if ($perm -contains "CreateAnyDatabase") {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
foreach ($ag in $AvailabilityGroup) {
try {
$server.Query("ALTER AVAILABILITY GROUP $ag GRANT CREATE ANY DATABASE")
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance
return
}
}
} elseif ($Login) {
$InputObject += Get-DbaLogin -SqlInstance $instance -SqlCredential $SqlCredential -Login $Login
foreach ($account in $Login) {
if ($account -notin $InputObject.Name) {
try {
$InputObject += New-DbaLogin -SqlInstance $server -Login $account -EnableException
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance
return
}
}
}
}
}
foreach ($account in $InputObject) {
$server = $account.Parent
if ($Type -contains "Endpoint") {
$server.Endpoints.Refresh()
$endpoint = $server.Endpoints | Where-Object EndpointType -eq DatabaseMirroring
if (-not $endpoint) {
Stop-Function -Message "DatabaseMirroring endpoint does not exist on $server" -Target $server -Continue
}
foreach ($perm in $Permission) {
if ($Pscmdlet.ShouldProcess($server.Name, "Granting $perm on $endpoint")) {
if ($perm -in 'CreateAnyDatabase') {
Stop-Function -Message "$perm not supported by endpoints" -Continue
}
try {
$bigperms = New-Object Microsoft.SqlServer.Management.Smo.ObjectPermissionSet([Microsoft.SqlServer.Management.Smo.ObjectPermission]::$perm)
$endpoint.Grant($bigperms, $account.Name)
[pscustomobject]@{
ComputerName = $account.ComputerName
InstanceName = $account.InstanceName
SqlInstance = $account.SqlInstance
Name = $account.Name
Permission = $perm
Type = "Grant"
Status = "Success"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $ag -Continue
}
}
}
}
if ($Type -contains "AvailabilityGroup") {
$ags = Get-DbaAvailabilityGroup -SqlInstance $account.Parent -AvailabilityGroup $AvailabilityGroup
foreach ($ag in $ags) {
foreach ($perm in $Permission) {
if ($perm -notin 'Alter', 'Control', 'TakeOwnership', 'ViewDefinition') {
Stop-Function -Message "$perm not supported by availability groups" -Continue
}
if ($Pscmdlet.ShouldProcess($server.Name, "Granting $perm on $ags")) {
try {
$bigperms = New-Object Microsoft.SqlServer.Management.Smo.ObjectPermissionSet([Microsoft.SqlServer.Management.Smo.ObjectPermission]::$perm)
$ag.Grant($bigperms, $account.Name)
[pscustomobject]@{
ComputerName = $account.ComputerName
InstanceName = $account.InstanceName
SqlInstance = $account.SqlInstance
Name = $account.Name
Permission = $perm
Type = "Grant"
Status = "Success"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $ag -Continue
}
}
}
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Import-DbaCmsRegServer {
<#
.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
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 Group
Imports to specific group
.PARAMETER Path
Optional path to exported reg server XML
.PARAMETER InputObject
Enables piping from Get-DbaCmsRegServer, Get-DbaCmsRegServerGroup, 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
Tags: RegisteredServer, CMS
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Import-DbaCmsRegServer
.EXAMPLE
PS C:\> Import-DbaCmsRegServer -SqlInstance sql2012 -Path C:\temp\corp-regservers.xml
Imports C:\temp\corp-regservers.xml to the CMS on sql2012
.EXAMPLE
PS C:\> Import-DbaCmsRegServer -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
PS C:\> Get-DbaCmsRegServer -SqlInstance sql2008, sql2012 | Import-DbaCmsRegServer -SqlInstance sql2017
Imports all registered servers from sql2008 and sql2012 to sql2017
.EXAMPLE
PS C:\> Get-DbaCmsRegServerGroup -SqlInstance sql2008 -Group hr\Seattle | Import-DbaCmsRegServer -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-DbaCmsRegServerGroup -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-DbaCmsRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $object.Parent.Name
if (-not $groupexists) {
$groupexists = Add-DbaCmsRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Name $object.Parent.Name
}
Add-DbaCmsRegServer -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-DbaCmsRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $regserver.Parent.Name
if (-not $groupexists) {
$groupexists = Add-DbaCmsRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Name $regserver.Parent.Name
}
Add-DbaCmsRegServer -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-DbaCmsRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group
}
} else {
$reggroups = Get-DbaCmsRegServerGroup -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-DbaCmsRegServer -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-DbaCmsRegServer -SqlInstance $instance -SqlCredential $SqlCredential -Name $object.Name -ServerName $object.ServerName -Description $object.Description -Group $groupobject
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Import-DbaRegisteredServer
}
}
function Import-DbaCsv {
<#
.SYNOPSIS
Efficiently imports very large (and small) CSV files into SQL Server.
.DESCRIPTION
Import-DbaCsv takes advantage of .NET's super fast SqlBulkCopy class to import CSV files into SQL Server.
The entire import is performed within a transaction, so if a failure occurs or the script is aborted, no changes will persist.
If the table or view specified does not exist and -AutoCreateTable, it will be automatically created using slow and efficient but accomodating data types.
.PARAMETER Path
Specifies path to the CSV file(s) to be imported. Multiple files may be imported at once.
.PARAMETER NoHeaderRow
By default, the first row is used to determine column names for the data being imported.
Use this switch if the first row contains data and not column names.
.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. Otherwise, the delimiter check bombs.
.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.
If the table specified does not exist and -AutoCreateTable, it will be automatically created using slow and efficient but accomodating data types.
If the automatically generated table datatypes do not work for you, please create the table prior to import.
If you want to import specific columns from a CSV, create a view with corresponding columns.
.PARAMETER Column
Import only specific columns. To remap column names, use the ColumnMap.
.PARAMETER ColumnMap
By default, the bulk copy tries to automap columns. When it doesn't work as desired, this parameter will help. Check out the examples for more information.
.PARAMETER AutoCreateTable
Creates a table if it does not already exist. The table will be created with sub-optimal data types such as nvarchar(max)
.PARAMETER Truncate
If this switch is enabled, the destination table will be truncated prior to import.
.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."
.PARAMETER NoProgress
The progress bar is pretty but can slow down imports. Use this parameter to quietly import.
.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, Import
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Import-DbaCsv
.EXAMPLE
PS C:\> Import-DbaCsv -Path 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, using the first row as column names.
Since a table name was not specified, the table name is automatically determined from filename as "housing".
.EXAMPLE
PS C:\> Import-DbaCsv -Path .\housing.csv -SqlInstance sql001 -Database markets -Table housing -Delimiter "`t" -NoHeaderRow
Imports the entire comma-delimited housing.csv, including the first row which is not used for colum names, to the SQL markets database, into the housing table, on a SQL Server named sql001.
.EXAMPLE
PS C:\> Import-DbaCsv -Path C:\temp\huge.txt -SqlInstance sqlcluster -Database locations -Table latitudes -Delimiter "|"
Imports the entire pipe-delimited huge.txt to the locations database, into the latitudes table on a SQL Server named sqlcluster.
.EXAMPLE
PS C:\> Import-DbaCsv -Path c:\temp\SingleColumn.csv -SqlInstance sql001 -Database markets -Table TempTable -SingleColumn
Imports the single column CSV into TempTable
.EXAMPLE
PS C:\> Get-ChildItem -Path \\FileServer\csvs | Import-DbaCsv -SqlInstance sql001, sql002 -Database tempdb -AutoCreateTable
Imports every CSV in the \\FileServer\csvs path into both sql001 and sql002's tempdb database. Each CSV will be imported into an automatically determined table name.
.EXAMPLE
PS C:\> Get-ChildItem -Path \\FileServer\csvs | Import-DbaCsv -SqlInstance sql001, sql002 -Database tempdb -AutoCreateTable -WhatIf
Shows what would happen if the command were to be executed
.EXAMPLE
PS C:\> Import-DbaCsv -Path c:\temp\dataset.csv -SqlInstance sql2016 -Database tempdb -Column Name, Address, Mobile
Import only Name, Address and Mobile even if other columns exist. All other columns are ignored and therefore null or default values.
.EXAMPLE
PS C:\> $columns = @{
>> Text = 'FirstName'
>> Number = 'PhoneNumber'
>> }
PS C:\> Import-DbaCsv -Path c:\temp\supersmall.csv -SqlInstance sql2016 -Database tempdb -ColumnMap $columns
The CSV column 'Text' is inserted into SQL column 'FirstName' and CSV column Number is inserted into the SQL Column 'PhoneNumber'. All other columns are ignored and therefore null or default values.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "line", Justification = "Variable line is used, False Positive on line 330")]
param (
[parameter(ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[Alias("Csv", "FullPath")]
[object[]]$Path,
[Parameter(Mandatory)]
[DbaInstanceParameter[]]$SqlInstance,
[pscredential]$SqlCredential,
[Parameter(Mandatory)]
[string]$Database,
[string]$Table,
[string]$Schema = "dbo",
[switch]$Truncate,
[string]$Delimiter = ",",
[switch]$SingleColumn,
[int]$BatchSize = 50000,
[int]$NotifyAfter = 50000,
[switch]$TableLock,
[switch]$CheckConstraints,
[switch]$FireTriggers,
[switch]$KeepIdentity,
[switch]$KeepNulls,
[string[]]$Column,
[hashtable[]]$ColumnMap,
[switch]$AutoCreateTable,
[switch]$NoProgress,
[switch]$NoHeaderRow,
[switch]$EnableException
)
begin {
$FirstRowHeader = $NoHeaderRow -eq $false
$scriptelapsed = [System.Diagnostics.Stopwatch]::StartNew()
try {
# SilentContinue isn't enough
Add-Type -Path "$script:PSModuleRoot\bin\csv\LumenWorks.Framework.IO.dll" -ErrorAction Stop
} catch {
$null = 1
}
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 -Path $Path -Delimiter $Delimiter -Columns $columns -ColumnText $columntext -SqlConn $sqlconn -Transaction $transaction
.OUTPUTS
Creates new table
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
param (
[Parameter(Mandatory)]
[string]$Path,
[Parameter(Mandatory)]
[string]$Delimiter,
[Parameter(Mandatory)]
[bool]$FirstRowHeader,
[System.Data.SqlClient.SqlConnection]$sqlconn,
[System.Data.SqlClient.SqlTransaction]$transaction
)
$reader = New-Object LumenWorks.Framework.IO.Csv.CsvReader((New-Object System.IO.StreamReader($Path)), $FirstRowHeader, $Delimiter, 1)
$columns = $reader.GetFieldHeaders()
$reader.Close()
$reader.Dispose()
# Get SQL datatypes by best guess on first data row
$sqldatatypes = @(); $index = 0
foreach ($column in $Columns) {
$sqldatatypes += "[$column] varchar(MAX)"
}
$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()
Stop-Function -Continue -Message "Failed to execute $sql. `nDid you specify the proper delimiter? `n$errormessage"
}
Write-Message -Level Verbose -Message "Successfully created table $schema.$table with the following column definitions:`n $($sqldatatypes -join "`n ")"
# Write-Message -Level Warning -Message "All columns are created using a best guess, and use their maximum datatype."
Write-Message -Level Verbose -Message "This is inefficient but allows the script to import without issues."
Write-Message -Level Verbose -Message "Consider creating the table first using best practices if the data will be used in production."
}
Write-Message -Level Verbose -Message "Started at $(Get-Date)"
# 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);
}
}
}
'
if (-not $script:core) {
try {
Add-Type -ReferencedAssemblies System.Data.dll -TypeDefinition $sourcecode -ErrorAction Stop
} catch {
$null = 1
}
}
}
process {
foreach ($filename in $Path) {
if ($filename.FullName) {
$filename = $filename.FullName
}
if (-not (Test-Path -Path $filename)) {
Stop-Function -Continue -Message "$filename cannot be found"
}
$file = (Resolve-Path -Path $filename).ProviderPath
# Does the second line contain the specified delimiter?
try {
$firstline = Get-Content -Path $file -TotalCount 2 -ErrorAction Stop
} catch {
Stop-Function -Continue -Message "Failure reading $file" -ErrorRecord $_
}
if (-not $SingleColumn) {
if ($firstline -notmatch $Delimiter) {
Stop-Function -Message "Delimiter ($Delimiter) not found in first few rows of $file. If this is a single column import, please specify -SingleColumn"
return
}
}
# Automatically generate Table name if not specified, then prompt user to confirm
if (-not (Test-Bound -ParameterName Table)) {
$table = [IO.Path]::GetFileNameWithoutExtension($file)
Write-Message -Level Verbose -Message "Table name not specified, using $table"
}
foreach ($instance in $SqlInstance) {
$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
# Open Connection to SQL Server
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential -StatementTimeout 0 -MinimumVersion 9
$sqlconn = $server.ConnectionContext.SqlConnectionObject
$sqlconn.Open()
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if ($PSCmdlet.ShouldProcess($instance, "Starting transaction in $Database")) {
# 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)
if (($sqlcmd.ExecuteScalar()) -eq 0) {
Stop-Function -Continue -Message "Database does not exist on $instance"
}
Write-Message -Level Verbose -Message "Database exists"
$sqlconn.ChangeDatabase($Database)
# Ensure Schema exists
$sql = "select count(*) from $Database.sys.schemas where name='$schema'"
$sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
# If Schema doesn't exist create it
# Defaulting to dbo.
if (($sqlcmd.ExecuteScalar()) -eq 0) {
if (-not $AutoCreateTable) {
Stop-Function -Continue -Message "Schema $Schema does not exist and AutoCreateTable was not specified"
}
$sql = "CREATE SCHEMA [$schema] AUTHORIZATION dbo"
if ($PSCmdlet.ShouldProcess($instance, "Creating schema $schema")) {
$sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
try {
$null = $sqlcmd.ExecuteNonQuery()
} catch {
Stop-Function -Continue -Message "Could not create $schema" -ErrorRecord $_
}
}
}
# Ensure table or view 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)
$sql2 = "select count(*) from $Database.sys.views where name = '$table' and schema_id=schema_id('$schema')"
$sqlcmd2 = New-Object System.Data.SqlClient.SqlCommand($sql2, $sqlconn, $transaction)
# 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 (($sqlcmd.ExecuteScalar()) -eq 0 -and ($sqlcmd2.ExecuteScalar()) -eq 0) {
if (-not $AutoCreateTable) {
Stop-Function -Continue -Message "Table or view $table does not exist and AutoCreateTable was not specified"
}
Write-Message -Level Verbose -Message "Table does not exist"
if ($PSCmdlet.ShouldProcess($instance, "Creating table $table")) {
New-SqlTable -Path $file -Delimiter $Delimiter -FirstRowHeader $FirstRowHeader -SqlConn $sqlconn -Transaction $transaction
}
} else {
Write-Message -Level Verbose -Message "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) {
$sql = "TRUNCATE TABLE [$schema].[$table]"
if ($PSCmdlet.ShouldProcess($instance, "Performing TRUNCATE TABLE [$schema].[$table] on $Database")) {
$sqlcmd = New-Object System.Data.SqlClient.SqlCommand($sql, $sqlconn, $transaction)
try {
$null = $sqlcmd.ExecuteNonQuery()
} catch {
Stop-Function -Continue -Message "Could not truncate $schema.$table" -ErrorRecord $_
}
}
}
# Setup bulk copy
Write-Message -Level Verbose -Message "Starting bulk copy for $(Split-Path $file -Leaf)"
# Setup bulk copy options
$bulkCopyOptions = @()
$options = "TableLock", "CheckConstraints", "FireTriggers", "KeepIdentity", "KeepNulls", "Default"
foreach ($option in $options) {
$optionValue = Get-Variable $option -ValueOnly -ErrorAction SilentlyContinue
if ($optionValue -eq $true) {
$bulkCopyOptions += "$option"
}
}
$bulkCopyOptions = $bulkCopyOptions -join " & "
if ($PSCmdlet.ShouldProcess($instance, "Performing import from $file")) {
try {
# Create SqlBulkCopy using default options, or options specified in command line.
if ($bulkCopyOptions) {
$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
$bulkCopy.EnableStreaming = $true
if ($ColumnMap) {
foreach ($columnname in $ColumnMap) {
foreach ($key in $columnname.Keys) {
$null = $bulkcopy.ColumnMappings.Add($key, $columnname[$key])
}
}
}
if ($Column) {
foreach ($columnname in $Column) {
$null = $bulkcopy.ColumnMappings.Add($columnname, $columnname)
}
}
} catch {
Stop-Function -Continue -Message "Failure" -ErrorRecord $_
}
# Write to server :D
try {
# Open the text file from disk
# // or using (CsvReader csv = new CsvReader(File.OpenRead(path), false, Encoding.UTF8, addMark))
# When addMark is true, consecutive null bytes will be replaced by [removed x null bytes] to indicate the removal
$reader = New-Object LumenWorks.Framework.IO.Csv.CsvReader((New-Object System.IO.StreamReader($file)), $FirstRowHeader, $Delimiter, 1)
# Add rowcount output
$bulkCopy.Add_SqlRowsCopied( {
$script:totalrows = $args[1].RowsCopied
if (-not $NoProgress) {
$timetaken = [math]::Round($elapsed.Elapsed.TotalSeconds, 2)
Write-ProgressHelper -StepNumber 1 -TotalSteps 2 -Activity "Importing from $file" -Message ([System.String]::Format("Progress: {0} rows in {2} seconds", $script:totalrows, $percent, $timetaken)) -ExcludePercent
}
})
$bulkCopy.WriteToServer($reader)
if ($resultcount -is [int]) {
Write-Progress -id 1 -activity "Inserting $resultcount rows" -status "Complete" -Completed
}
$reader.Close()
$reader.Dispose()
$completed = $true
} catch {
$completed = $false
if ($resultcount -is [int]) {
Write-Progress -id 1 -activity "Inserting $resultcount rows" -status "Failed" -Completed
}
Stop-Function -Continue -Message "Failure" -ErrorRecord $_
}
}
if ($PSCmdlet.ShouldProcess($instance, "Committing transaction")) {
if ($completed) {
# "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 ($script:core) {
$rowscopied = "Unsupported in Core"
} else {
$rowscopied = [System.Data.SqlClient.SqlBulkCopyExtension]::RowsCopiedCount($bulkcopy)
}
Write-Message -Level Verbose -Message "$rowscopied total rows copied"
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Database = $Database
Table = $table
Schema = $schema
RowsCopied = $rowscopied
Elapsed = [prettytimespan]$elapsed.Elapsed
Path = $file
}
} else {
Stop-Function -Message "Transaction rolled back. Was the proper delimiter specified? Is the first row the column name?" -ErrorRecord $_
return
}
}
# Close everything just in case & ignore errors
try {
$null = $sqlconn.close(); $null = $sqlconn.Dispose();
$null = $bulkCopy.close(); $bulkcopy.dispose();
$null = $reader.close(); $null = $reader.dispose()
} catch {
#here to avoid an empty catch
$null = 1
}
}
}
}
end {
# Close everything just in case & ignore errors
try {
$null = $sqlconn.close(); $null = $sqlconn.Dispose();
$null = $bulkCopy.close(); $bulkcopy.dispose();
$null = $reader.close(); $null = $reader.dispose()
} catch {
#here to avoid an empty catch
$null = 1
}
# Script is finished. Show elapsed time.
$totaltime = [math]::Round($scriptelapsed.Elapsed.TotalSeconds, 2)
Write-Message -Level Verbose -Message "Total Elapsed Time for everything: $totaltime seconds"
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Import-DbaCsvtoSql
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Import-CsvToSql
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Import-DbaPfDataCollectorSetTemplate
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaPfDataCollectorSetTemplate | Out-GridView -PassThru | Import-DbaPfDataCollectorSetTemplate -ComputerName sql2017
Allows you to select a Session template then import to an instance named sql2017.
.EXAMPLE
PS C:\> 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 {
#Variable marked as unused by PSScriptAnalyzer
#$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 = $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 -ErrorAction SilentlyContinue).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 "
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
}
}
}
}
}
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 Destination
Destination SQL Server. You must have sysadmin access and the server must be SQL Server 2000 or higher.
.PARAMETER SourceSqlCredential
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 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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: SpConfig, Configure, Configuration
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Import-DbaSpConfigure
.INPUTS
None You cannot pipe objects to Import-DbaSpConfigure
.OUTPUTS
$true if success
$false if failure
.EXAMPLE
PS C:\> Import-DbaSpConfigure -Source sqlserver -Destination sqlcluster
Imports the sp_configure settings from the source server sqlserver and sets them on the sqlcluster server using Windows Authentication
.EXAMPLE
PS C:\> Import-DbaSpConfigure -Source sqlserver -Destination sqlcluster -Force
Imports the sp_configure settings from the source server sqlserver and sets them on the sqlcluster server using Windows Authentication. Will not do a version check between Source and Destination
.EXAMPLE
PS C:\> Import-DbaSpConfigure -Source sqlserver -Destination sqlcluster -SourceSqlCredential $SourceSqlCredential -DestinationSqlCredential $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 $SourceSqlCredential and $DestinationSqlCredential
.EXAMPLE
PS C:\> Import-DbaSpConfigure -SqlInstance sqlserver -Path .\spconfig.sql -SqlCredential $SqlCredential
Imports the sp_configure settings from the file .\spconfig.sql and sets them on the sqlserver server using the SQL credential stored in the variable $SqlCredential
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
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,
[Alias('Silent')]
[switch]$EnableException
)
begin {
if ($Path.length -eq 0) {
try {
$sourceserver = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
} catch {
Stop-Function -Message "Failed to process Instance $Source" -ErrorRecord $_ -Target $Source
return
}
if (!(Test-SqlSa -SqlInstance $sourceserver -SqlCredential $SourceSqlCredential)) {
Stop-Function -Message "Not a sysadmin on $sourceserver. Quitting." -Category PermissionDenied -ErrorRecord $_ -Target $server -Continue
}
try {
$destserver = Connect-SqlInstance -SqlInstance $Destination -SqlCredential $DestinationSqlCredential
} catch {
Stop-Function -Message "Failed to process Instance $Destination" -ErrorRecord $_ -Target $Destination
return
}
if (!(Test-SqlSa -SqlInstance $destserver -SqlCredential $DestinationSqlCredential)) {
Stop-Function -Message "Not a sysadmin on $destserver. Quitting." -Category PermissionDenied -ErrorRecord $_ -Target $server -Continue
}
$source = $sourceserver.DomainInstanceName
$destination = $destserver.DomainInstanceName
} else {
try {
$server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failed to process Instance $SqlInstance" -ErrorRecord $_ -Target $SqlInstance -Continue
}
if (!(Test-SqlSa -SqlInstance $server -SqlCredential $SqlCredential)) {
Stop-Function -Message "Not a sysadmin on $server. Quitting." -Category PermissionDenied -ErrorRecord $_ -Target $server -Continue
}
if ((Test-Path $Path) -eq $false) {
Stop-Function -Message "File $Path Not Found" -Category InvalidArgument -Target $Path -Continue
}
}
}
process {
if ($Path.length -eq 0) {
if ($Pscmdlet.ShouldProcess($destination, "Export sp_configure")) {
$sqlfilename = Export-DbaSpConfigure $sourceserver
}
if ($sourceserver.versionMajor -ne $destserver.versionMajor -and $force -eq $false) {
Write-Message -Level Warning -Message "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.Configuration.Alter($true)
$destserver.Configuration.ShowAdvancedOptions.ConfigValue = $true
$sourceserver.Configuration.Alter($true)
$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
$null = $destserver.Query("RECONFIGURE WITH OVERRIDE")
Write-Message -Level Output -Message "updated $($destprop.displayname) to $($sourceprop.configvalue)."
} catch {
Stop-Function -Message "Could not set $($destprop.displayname) to $($sourceprop.configvalue). Feature may not be supported." -ErrorRecord $_ -Continue
}
}
}
try {
$destserver.Configuration.Alter()
} catch {
$needsrestart = $true
}
$sourceserver.Configuration.ShowAdvancedOptions.ConfigValue = $false
$sourceserver.Configuration.Alter($true)
$destserver.Configuration.ShowAdvancedOptions.ConfigValue = $false
$destserver.Configuration.Alter($true)
if ($needsrestart -eq $true) {
Write-Message -Level Warning -Message "Some configuration options will be updated once SQL Server is restarted."
} else {
Write-Message -Level Output -Message "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 {
$null = $server.Query($line)
Write-Message -Level Output -Message "Successfully executed $line."
} catch {
Stop-Function -Message "$line failed. Feature may not be supported." -ErrorRecord $_ -Continue
}
}
$server.Configuration.ShowAdvancedOptions.ConfigValue = $false
Write-Message -Level Warning -Message "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-Message -Level Output -Message "SQL Server configuration options migration finished."
}
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Import-SqlSpConfigure
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Import-DbaXESessionTemplate
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaXESession -SqlInstance sql2017 -Session db_ola_health | Remove-DbaXESession
PS C:\> Import-DbaXESessionTemplate -SqlInstance sql2017 -Template db_ola_health | Start-DbaXESession
Imports a session if it exists, then recreates it using a template.
.EXAMPLE
PS C:\> 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 {
$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-DbaPath -SqlInstance $server -Path $TargetFilePath)) {
$null = New-DbaDirectory -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
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 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 LocalFile
Specifies the path to a local file to install FRK from. This *should* be the zipfile as distributed by the maintainers.
If this parameter is not specified, the latest version will be downloaded and installed from https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit
.PARAMETER Force
If this switch is enabled, the FRK will be downloaded from the internet even if previously cached.
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Install-DbaFirstResponderKit
.EXAMPLE
PS C:\> Install-DbaFirstResponderKit -SqlInstance server1 -Database master
Logs into server1 with Windows authentication and then installs the FRK in the master database.
.EXAMPLE
PS C:\> Install-DbaFirstResponderKit -SqlInstance server1\instance1 -Database DBA
Logs into server1\instance1 with Windows authentication and then installs the FRK in the DBA database.
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> $servers = "sql2016\standardrtm", "sql2016\sqlexpress", "sql2014"
PS C:\> $servers | Install-DbaFirstResponderKit
Logs into sql2016\standardrtm, sql2016\sqlexpress and sql2014 with Windows authentication and then installs the FRK in the master database.
.EXAMPLE
PS C:\> Install-DbaFirstResponderKit -SqlInstance sql2016 -Branch dev
Installs the dev branch version of the FRK in the master database on sql2016 instance.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[ValidateSet('master', 'dev')]
[string]$Branch = "master",
[object]$Database = "master",
[string]$LocalFile,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
$DbatoolsData = Get-DbatoolsConfigValue -FullName "Path.DbatoolsData"
if (-not $DbatoolsData) {
$DbatoolsData = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
}
$url = "https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit/archive/$Branch.zip"
$temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
$zipfile = "$temp\SQL-Server-First-Responder-Kit-$Branch.zip"
$zipfolder = "$temp\SQL-Server-First-Responder-Kit-$Branch\"
$FRKLocation = "FRK_$Branch"
$LocalCachedCopy = Join-Path -Path $DbatoolsData -ChildPath $FRKLocation
if ($LocalFile) {
if (-not(Test-Path $LocalFile)) {
Stop-Function -Message "$LocalFile doesn't exist"
return
}
if (-not($LocalFile.EndsWith('.zip'))) {
Stop-Function -Message "$LocalFile should be a zip file"
return
}
}
if ($Force -or -not(Test-Path -Path $LocalCachedCopy -PathType Container) -or $LocalFile) {
# Force was passed, or we don't have a local copy, or $LocalFile was passed
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
if ($LocalFile) {
Unblock-File $LocalFile -ErrorAction SilentlyContinue
Expand-Archive -Path $LocalFile -DestinationPath $zipfolder -Force
} else {
Write-Message -Level Verbose -Message "Downloading and unzipping the First Responder Kit zip file."
try {
try {
Invoke-TlsWebRequest $url -OutFile $zipfile -ErrorAction Stop -UseBasicParsing
} catch {
# Try with default proxy and usersettings
(New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
Invoke-TlsWebRequest $url -OutFile $zipfile -ErrorAction Stop -UseBasicParsing
}
# 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
}
}
## Copy it into local area
if (Test-Path -Path $LocalCachedCopy -PathType Container) {
Remove-Item -Path (Join-Path $LocalCachedCopy '*') -Recurse -ErrorAction SilentlyContinue
} else {
$null = New-Item -Path $LocalCachedCopy -ItemType Container
}
Copy-Item -Path $zipfolder -Destination $LocalCachedCopy -Recurse
}
}
process {
if (Test-FunctionInterrupt) {
return
}
foreach ($instance in $SqlInstance) {
try {
$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 $LocalCachedCopy -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-DbaQuery -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."
}
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
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 LocalFile
Specifies the path to a local file to install Ola's solution from. This *should* be the zipfile as distributed by the maintainers.
If this parameter is not specified, the latest version will be downloaded and installed from https://github.com/olahallengren/sql-server-maintenance-solution
.PARAMETER Force
If this switch is enabled, the Ola's solution will be downloaded from the internet even if previously cached.
.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, cviorel.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
http://dbatools.io/Install-DbaMaintenanceSolution
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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, ConfirmImpact = "Medium")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Internal functions are ignored")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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,
[string]$LocalFile,
[switch]$Force,
[switch]$EnableException
)
begin {
if ($InstallJobs -and $Solution -ne 'All') {
Stop-Function -Message "Jobs can only be created for all solutions. To create SQL Agent jobs you need to use '-Solution All' (or not specify the Solution and let it default to All) and '-InstallJobs'."
return
}
if ((Test-Bound -ParameterName CleanupTime) -and -not $InstallJobs) {
Stop-Function -Message "CleanupTime is only useful when installing jobs. To install jobs, please use '-InstallJobs' in addition to CleanupTime."
return
}
if ($ReplaceExisting -eq $true) {
Write-Message -Level Verbose -Message "If Ola Hallengren's scripts are found, we will drop and recreate them!"
}
$DbatoolsData = Get-DbatoolsConfigValue -FullName "Path.DbatoolsData"
$url = "https://github.com/olahallengren/sql-server-maintenance-solution/archive/master.zip"
$temp = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
$zipfile = "$temp\ola-sql-server-maintenance-solution.zip"
$zipfolder = "$temp\ola-sql-server-maintenance-solution\"
$OLALocation = "OLA_SQL_MAINT_master"
$LocalCachedCopy = Join-Path -Path $DbatoolsData -ChildPath $OLALocation
if ($LocalFile) {
if (-not (Test-Path $LocalFile)) {
Stop-Function -Message "$LocalFile doesn't exist"
return
}
if (-not ($LocalFile.EndsWith('.zip'))) {
Stop-Function -Message "$LocalFile should be a zip file"
return
}
}
if ($Force -or -not (Test-Path -Path $LocalCachedCopy -PathType Container) -or $LocalFile) {
# Force was passed, or we don't have a local copy, or $LocalFile was passed
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
if ($LocalFile) {
Unblock-File $LocalFile -ErrorAction SilentlyContinue
Expand-Archive -Path $LocalFile -DestinationPath $zipfolder -Force
} else {
Write-Message -Level Verbose -Message "Downloading and unzipping Ola's maintenance solution zip file."
try {
try {
Invoke-TlsWebRequest $url -OutFile $zipfile -ErrorAction Stop -UseBasicParsing
} catch {
# Try with default proxy and usersettings
(New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
Invoke-TlsWebRequest $url -OutFile $zipfile -ErrorAction Stop -UseBasicParsing
}
# 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 Ola's maintenance solution. Download and install manually from https://github.com/olahallengren/sql-server-maintenance-solution/archive/master.zip." -ErrorRecord $_
return
}
}
## Copy it into local area
if (Test-Path -Path $LocalCachedCopy -PathType Container) {
Remove-Item -Path (Join-Path $LocalCachedCopy '*') -Recurse -ErrorAction SilentlyContinue
} else {
$null = New-Item -Path $LocalCachedCopy -ItemType Container
}
Copy-Item -Path $zipfolder -Destination $LocalCachedCopy -Recurse
}
function Get-DbaOlaWithParameters($listOfFiles) {
$fileContents = @{ }
foreach ($file in $listOfFiles) {
$fileContents[$file] = Get-Content -Path $file -Raw
}
foreach ($file in $($fileContents.Keys)) {
# In which database we install
if ($Database -ne 'master') {
$findDB = 'USE [master]'
$replaceDB = 'USE [' + $Database + ']'
$fileContents[$file] = $fileContents[$file].Replace($findDB, $replaceDB)
}
# Backup location
if ($BackupLocation) {
$findBKP = 'SET @BackupDirectory = NULL'
$replaceBKP = 'SET @BackupDirectory = N''' + $BackupLocation + ''''
$fileContents[$file] = $fileContents[$file].Replace($findBKP, $replaceBKP)
}
# CleanupTime
if ($CleanupTime -ne 0) {
$findCleanupTime = 'SET @CleanupTime = NULL'
$replaceCleanupTime = 'SET @CleanupTime = ' + $CleanupTime
$fileContents[$file] = $fileContents[$file].Replace($findCleanupTime, $replaceCleanupTime)
}
# OutputFileDirectory
if (-not $OutputFileDirectory) {
$findOutputFileDirectory = 'SET @OutputFileDirectory = NULL'
$replaceOutputFileDirectory = 'SET @OutputFileDirectory = N''' + $OutputFileDirectory + ''''
$fileContents[$file] = $fileContents[$file].Replace($findOutputFileDirectory, $replaceOutputFileDirectory)
}
# LogToTable
if (!$LogToTable) {
$findLogToTable = "SET @LogToTable = 'Y'"
$replaceLogToTable = "SET @LogToTable = 'N'"
$fileContents[$file] = $fileContents[$file].Replace($findLogToTable, $replaceLogToTable)
}
# Create Jobs
if (-not $InstallJobs) {
$findCreateJobs = "SET @CreateJobs = 'Y'"
$replaceCreateJobs = "SET @CreateJobs = 'N'"
$fileContents[$file] = $fileContents[$file].Replace($findCreateJobs, $replaceCreateJobs)
}
}
return $fileContents
}
}
process {
if (Test-FunctionInterrupt) {
return
}
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -NonPooled
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if ((Test-Bound -ParameterName ReplaceExisting -Not)) {
$procs = Get-DbaModule -SqlInstance $server -Database $Database | Where-Object Name -in 'CommandExecute', 'DatabaseBackup', 'DatabaseIntegrityCheck', 'IndexOptimize'
$table = Get-DbaDbTable -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 -ParameterName 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]
# 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"
$listOfFiles = Get-ChildItem -Filter "*.sql" -Path $LocalCachedCopy -Recurse | Select-Object -ExpandProperty FullName
$fileContents = Get-DbaOlaWithParameters -listOfFiles $listOfFiles
$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 $instance, database $Database."
foreach ($file in $fileContents.Keys) {
$shortFileName = Split-Path $file -Leaf
if ($required.Contains($shortFileName)) {
Write-Message -Level Output -Message "Installing $shortFileName."
$sql = $fileContents[$file]
try {
foreach ($query in ($sql -Split "\nGO\b")) {
$null = $db.Query($query)
}
} catch {
Stop-Function -Message "Could not execute $shortFileName in $Database on $instance." -ErrorRecord $_ -Target $db -Continue
}
}
}
} catch {
Stop-Function -Message "Could not execute $shortFileName in $Database on $instance." -ErrorRecord $_ -Target $db -Continue
}
}
# Only here due to need for non-pooled connection in this command
try {
$server.ConnectionContext.Disconnect()
} catch {
# here to avoid an empty catch
$null = 1
}
Write-Message -Level Output -Message "Installation complete."
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Install-DbaSqlWatch {
<#
.SYNOPSIS
Installs or updates SqlWatch.
.DESCRIPTION
Downloads, extracts and installs or updates SqlWatch.
https://sqlwatch.io/
.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 install SqlWatch into. Defaults to SQLWATCH.
.PARAMETER LocalFile
Specifies the path to a local file to install SqlWatch from. This *should* be the zipfile as distributed by the maintainers.
If this parameter is not specified, the latest version will be downloaded and installed from https://github.com/marcingminski/sqlwatch
.PARAMETER Force
If this switch is enabled, SqlWatch will be downloaded from the internet even if previously cached.
.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: SqlWatch
Author: Ken K (github.com/koglerk)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Install-DbaSqlWatch
.EXAMPLE
Install-DbaSqlWatch -SqlInstance server1
Logs into server1 with Windows authentication and then installs SqlWatch in the SQLWATCH database.
.EXAMPLE
Install-DbaSqlWatch -SqlInstance server1\instance1 -Database DBA
Logs into server1\instance1 with Windows authentication and then installs SqlWatch in the DBA database.
.EXAMPLE
Install-DbaSqlWatch -SqlInstance server1\instance1 -Database DBA -SqlCredential $cred
Logs into server1\instance1 with SQL authentication and then installs SqlWatch in the DBA database.
.EXAMPLE
Install-DbaSqlWatch -SqlInstance sql2016\standardrtm, sql2016\sqlexpress, sql2014
Logs into sql2016\standardrtm, sql2016\sqlexpress and sql2014 with Windows authentication and then installs SqlWatch in the SQLWATCH database.
.EXAMPLE
$servers = "sql2016\standardrtm", "sql2016\sqlexpress", "sql2014"
$servers | Install-DbaSqlWatch
Logs into sql2016\standardrtm, sql2016\sqlexpress and sql2014 with Windows authentication and then installs SqlWatch in the SQLWATCH database.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string]$Database = "SQLWATCH",
[string]$LocalFile,
[switch]$Force,
[switch]$EnableException
)
begin {
$stepCounter = 0
$DbatoolsData = Get-DbatoolsConfigValue -FullName "Path.DbatoolsData"
$tempFolder = ([System.IO.Path]::GetTempPath()).TrimEnd("\")
$zipfile = "$tempFolder\SqlWatch.zip"
if (-not $LocalFile) {
if ($PSCmdlet.ShouldProcess($env:computername, "Downloading latest release from GitHub")) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Downloading latest release from GitHub"
# query the releases to find the latest, check and see if its cached
$ReleasesUrl = "https://api.github.com/repos/marcingminski/sqlwatch/releases"
$DownloadBase = "https://github.com/marcingminski/sqlwatch/releases/download/"
Write-Message -Level Verbose -Message "Checking GitHub for the latest release."
$LatestReleaseUrl = (Invoke-TlsWebRequest -UseBasicParsing -Uri $ReleasesUrl | ConvertFrom-Json)[0].assets[0].browser_download_url
Write-Message -Level VeryVerbose -Message "Latest release is available at $LatestReleaseUrl"
$LocallyCachedZip = Join-Path -Path $DbatoolsData -ChildPath $($LatestReleaseUrl -replace $DownloadBase, '');
# if local cached copy exists, use it, otherwise download a new one
if (-not $Force) {
# download from github
Write-Message -Level Verbose "Downloading $LatestReleaseUrl"
try {
Invoke-TlsWebRequest $LatestReleaseUrl -OutFile $zipfile -ErrorAction Stop -UseBasicParsing
} catch {
#try with default proxy and usersettings
(New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
Invoke-TlsWebRequest $LatestReleaseUrl -OutFile $zipfile -ErrorAction Stop -UseBasicParsing
}
# copy the file from temp to local cache
Write-Message -Level Verbose "Copying $zipfile to $LocallyCachedZip"
try {
New-Item -Path $LocallyCachedZip -ItemType File -Force | Out-Null
Copy-Item -Path $zipfile -Destination $LocallyCachedZip -Force
} catch {
# should we stop the function if the file copy fails?
# here to avoid an empty catch
$null = 1
}
}
}
} else {
# $LocalFile was passed, so use it
if ($PSCmdlet.ShouldProcess($env:computername, "Copying local file to temp directory")) {
if ($LocalFile.EndsWith("zip")) {
$LocallyCachedZip = $zipfile
Copy-Item -Path $LocalFile -Destination $LocallyCachedZip -Force
} else {
$LocallyCachedZip = (Join-Path -path $tempFolder -childpath "SqlWatch.zip")
Copy-Item -Path $LocalFile -Destination $LocallyCachedZip -Force
}
}
}
# expand the zip file
if ($PSCmdlet.ShouldProcess($env:computername, "Unpacking zipfile")) {
Write-Message -Level VeryVerbose "Unblocking $LocallyCachedZip"
Unblock-File $LocallyCachedZip -ErrorAction SilentlyContinue
$LocalCacheFolder = Split-Path $LocallyCachedZip -Parent
Write-Message -Level Verbose "Extracting $LocallyCachedZip to $LocalCacheFolder"
if (Get-Command -ErrorAction SilentlyContinue -Name "Expand-Archive") {
try {
Expand-Archive -Path $LocallyCachedZip -DestinationPath $LocalCacheFolder -Force
} catch {
Stop-Function -Message "Unable to extract $LocallyCachedZip. Archive may not be valid." -ErrorRecord $_
return
}
} else {
# Keep it backwards compatible
$shell = New-Object -ComObject Shell.Application
$zipPackage = $shell.NameSpace($LocallyCachedZip)
$destinationFolder = $shell.NameSpace($LocalCacheFolder)
Get-ChildItem "$LocalCacheFolder\SqlWatch.zip" | Remove-Item
$destinationFolder.CopyHere($zipPackage.Items())
}
Write-Message -Level VeryVerbose "Deleting $LocallyCachedZip"
Remove-Item -Path $LocallyCachedZip
}
}
process {
if (Test-FunctionInterrupt) {
return
}
foreach ($instance in $SqlInstance) {
if ($PSCmdlet.ShouldProcess($instance, "Installing SqlWatch on $Database")) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
} catch {
Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Starting installing/updating SqlWatch in $database on $instance"
try {
# create a publish profile and publish DACPAC
$DacPacPath = Get-ChildItem -Filter "SqlWatch.dacpac" -Path $LocalCacheFolder -Recurse | Select-Object -ExpandProperty FullName
$PublishOptions = @{
RegisterDataTierApplication = $true
}
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Publishing SqlWatch dacpac to $database on $instance"
$DacProfile = New-DbaDacProfile -SqlInstance $server -Database $Database -Path $LocalCacheFolder -PublishOptions $PublishOptions | Select-Object -ExpandProperty FileName
$PublishResults = Publish-DbaDacPackage -SqlInstance $server -Database $Database -Path $DacPacPath -PublishXml $DacProfile
# parse results
$parens = Select-String -InputObject $PublishResults.Result -Pattern "\(([^\)]+)\)" -AllMatches
if ($parens.matches) {
$ExtractedResult = $parens.matches | Select-Object -Last 1
}
[PSCustomObject]@{
ComputerName = $PublishResults.ComputerName
InstanceName = $PublishResults.InstanceName
SqlInstance = $PublishResults.SqlInstance
Database = $PublishResults.Database
Status = $ExtractedResult
}
} catch {
Stop-Function -Message "DACPAC failed to publish to $database on $instance." -ErrorRecord $_ -Target $instance -Continue
}
Write-Message -Level Verbose -Message "Finished installing/updating SqlWatch in $database on $instance."
}
}
}
}
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Install-DbaWatchUpdate
.EXAMPLE
PS C:\> Install-DbaWatchUpdate
Adds the scheduled task needed by Watch-DbaUpdate
.EXAMPLE
PS C:\> 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
#Variable $Task marked as unused by PSScriptAnalyzer replaced with $null for catching output
$null = Register-ScheduledTask -Principal $principal -TaskName 'dbatools version check' -Action $action -Trigger $trigger -Settings $settings -ErrorAction Stop
} catch {
# keep moving
# here to avoid an empty catch
$null = 1
}
}
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 target SQL Server instance or instances. 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 Force
If this switch is enabled, the sp_WhoisActive will be downloaded from the internet even if previously cached.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Install-DbaWhoIsActive
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> $instances = Get-DbaCmsRegServer sqlserver
PS C:\> Install-DbaWhoIsActive -SqlInstance $instances -Database master
Installs sp_WhoisActive to all servers within CMS
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
param (
[parameter(Mandatory, ValueFromPipeline, Position = 0)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PsCredential]$SqlCredential,
[ValidateScript( { Test-Path -Path $_ -PathType Leaf })]
[string]$LocalFile,
[object]$Database,
[switch][Alias('Silent')]
$EnableException,
[switch]$Force
)
begin {
$DbatoolsData = Get-DbatoolsConfigValue -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-TlsWebRequest -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-TlsWebRequest $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-TlsWebRequest $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"
$matchString = 'Who Is Active? v'
If ($sql -like "*$matchString*") {
$posStart = $sql.IndexOf("$matchString")
$PosEnd = $sql.IndexOf(")", $PosStart)
$versionWhoIsActive = $sql.Substring($posStart + $matchString.Length, $posEnd - ($posStart + $matchString.Length) + 1).TrimEnd()
} Else {
$versionWhoIsActive = ''
}
}
}
process {
if (Test-FunctionInterrupt) { return }
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 $Database) {
if ($PSCmdlet.ShouldProcess($instance, "Prompting with GUI list of databases")) {
$Database = Show-DbaDbList -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'
Version = $versionWhoIsActive
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-Dbadatabase` 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
Author: Stuart Moore (@napalmgram - http://stuart-moore.com)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaAdvancedRestore
.EXAMPLE
PS C:\> $BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance
Will restore all the backups in the BackupHistory object according to the transformations it contains
.EXAMPLE
PS C:\> $BackupHistory | Invoke-DbaAdvancedRestore -SqlInstance MyInstance -OutputScriptOnly
PS C:\> $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, ConfirmImpact = "Low")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "AzureCredential", Justification = "For Parameter AzureCredential")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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 ','
}
$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 1 -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 1 -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 1 -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 1 -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 1 -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 1 -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 1 -Activity "Restoring $Database to $sqlinstance - Backup $BackupCnt of $($Backups.count)" -percentcomplete 0
}
Write-Progress -id 2 -ParentId 1 -Activity "Restore $($backup.FullName -Join ',')" -percentcomplete 0
$script = $Restore.Script($Server)
$percentcomplete = [Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] {
Write-Progress -id 2 -ParentId 1 -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 2 -ParentId 1 -Activity "Restore $($backup.FullName -Join ',')" -Completed
Write-Progress -id 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) {
$pathSep = Get-DbaPathSep -Server $server
$RestoreDirectory = ((Split-Path $backup.FileList.PhysicalName -Parent) | Sort-Object -Unique).Replace('\', $pathSep) -Join ','
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Database = $backup.Database
DatabaseName = $backup.Database
DatabaseOwner = $server.ConnectionContext.TrueLogin
Owner = $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 = $RestoreDirectory
BackupSize = if ([bool]($backup.psobject.Properties.Name -contains 'TotalSize')) { [dbasize](($backup | Measure-Object -Property TotalSize -Sum).Sum) } else { $null }
CompressedBackupSize = if ([bool]($backup.psobject.Properties.Name -contains 'CompressedBackupSize')) { [dbasize](($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 -Property ComputerName, InstanceName, SqlInstance, BackupFile, BackupFilesCount, BackupSize, CompressedBackupSize, Database, Owner, DatabaseRestoreTime, FileRestoreTime, NoRecovery, RestoreComplete, RestoredFile, RestoredFilesCount, Script, RestoreDirectory, WithReplace
} else {
$script
}
if ($Restore.Devices.Count -gt 0) {
$Restore.Devices.Clear()
}
Write-Message -Level Verbose -Message "Succeeded, Closing Server connection"
$server.ConnectionContext.Disconnect()
}
}
$BackupCnt++
}
Write-Progress -id 2 -Activity "Finished" -Completed
if ($server.ConnsectionContext.exists) {
$server.ConnectionContext.Disconnect()
}
Write-Progress -id 1 -Activity "Finished" -Completed
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-DbaAgFailover {
<#
.SYNOPSIS
Failover an availability group.
.DESCRIPTION
Failover an availability group.
.PARAMETER SqlInstance
The SQL Server instance. 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
Only failover specific availability groups.
.PARAMETER InputObject
Enables piping from Get-DbaAvailabilityGroup
.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
Force Failover and allow data loss
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaAgFailover
.EXAMPLE
PS C:\> Invoke-DbaAgFailover -SqlInstance sql2017 -AvailabilityGroup SharePoint
Safely (no potential data loss) fails over the SharePoint AG on sql2017. Prompts for confirmation.
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql2017 | Out-GridView -Passthru | Invoke-DbaAgFailover -Confirm:$false
Safely (no potential data loss) fails over the selected availability groups on sql2017. Does not prompt for confirmation.
.EXAMPLE
PS C:\> Invoke-DbaAgFailover -SqlInstance sql2017 -AvailabilityGroup SharePoint -Force
Forcefully (with potential data loss) fails over the SharePoint AG on sql2017. Prompts for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[parameter(Mandatory)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup[]]$InputObject,
[switch]$Force,
[switch]$EnableException
)
process {
if ($SqlInstance -and -not $AvailabilityGroup) {
Stop-Function -Message "You must specify at least one availability group when using SqlInstance."
return
}
if ($SqlInstance) {
$InputObject += Get-DbaAvailabilityGroup -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup
}
foreach ($ag in $InputObject) {
$server = $ag.Parent
if ($Force) {
if ($Pscmdlet.ShouldProcess($server.Name, "Forcefully failing over $($ag.Name), allowing potential data loss")) {
$ag.FailoverWithPotentialDataLoss()
$ag.Refresh()
$ag
}
} else {
if ($Pscmdlet.ShouldProcess($server.Name, "Gracefully failing over $($ag.Name)")) {
$ag.Failover()
$ag.Refresh()
$ag
}
}
}
}
}
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Invoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1
This command will distribute the data in database db1 on instance sql1
.EXAMPLE
PS C:\> Invoke-DbaBalanceDataFiles -SqlInstance sql1 -Database db1 | Select-Object -ExpandProperty DataFilesEnd
This command will distribute the data in database db1 on instance sql1
.EXAMPLE
PS C:\> 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
PS C:\> 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)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Singular Noun doesn't make sense")]
param (
[parameter(ParameterSetName = "Pipe", Mandatory)]
[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
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-DbaDbFile -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-DbaDbFile -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-DbaDbFile -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 target SQL Server instance or instances.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), https://wsmelton.github.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaCycleLog
.EXAMPLE
PS C:\> Invoke-DbaCycleLog -SqlInstance sql2016 -Type agent
Cycles the current error log for the SQL Server Agent on SQL Server instance sql2016
.EXAMPLE
PS C:\> Invoke-DbaCycleLog -SqlInstance sql2016 -Type instance
Cycles the current error log for the SQL Server instance on SQL Server instance sql2016
.EXAMPLE
PS C:\> 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, ConfirmImpact = 'Low')]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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 {
$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-DbaDbccFreeCache {
<#
.SYNOPSIS
Execution of Database Console Commands that clear Server level Memory caches
.DESCRIPTION
Allows execution of Database Console Commands that act at Server Level to clear Memory caches
Allows execution of the following commands
DBCC FREEPROCCACHE
DBCC FREESESSIONCACHE
DBCC FREESYSTEMCACHE
Read more:
- https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-freeproccache-transact-sql
- https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-freesessioncache-transact-sql
- https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-freesystemcache-transact-sql
.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 Operation
DBCC Operation to Perform - Supports specific set of operations
.PARAMETER InputValue
Value used for Operation - meaning depends on Operation
DBCC FREEPROCCACHE accepts
a plan_handle of type varbinary(64)
a sql_handle of type varbinary(64)
or the name of a Resource Governor resource pool of type sysname
If blank then clears all elements from the plan cache
DBCC FREESYSTEMCACHE accepts
'ALL' for ALL specifies all supported caches
or name of a Resource Governor pool cache
Not required for other values
.PARAMETER NoInformationalMessages
Suppresses all informational messages.
.PARAMETER MarkInUseForRemoval
Used when Operation = DBCC FREESYSTEMCACHE
Asynchronously frees currently used entries from their respective caches after they become unused
.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
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: DBCC
Author: Patrick Flynn (@sqllensman)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaDbccFreeCache
.EXAMPLE
PS C:\> Invoke-DbaDbccFreeCache -SqlInstance SqlServer2017 -Operation FREEPROCCACHE
Runs the command DBCC FREEPROCCACHE against the instance SqlServer2017 using Windows Authentication
.EXAMPLE
PS C:\> Invoke-DbaDbccFreeCache -SqlInstance SqlServer2017 -Operation FREESESSIONCACHE -NoInformationalMessages
Runs the command DBCC FREESESSIONCACHE WITH NO_INFOMSGS against the instance SqlServer2017 using Windows Authentication
.EXAMPLE
PS C:\> Invoke-DbaDbccFreeCache -SqlInstance SqlServer2017 -Operation FREESYSTEMCACHE -NoInformationalMessages
Runs the command DBCC FREESYSTEMCACHE WITH NO_INFOMSGS against the instance SqlServer2017 using Windows Authentication
.EXAMPLE
PS C:\> Invoke-DbaDbccFreeCache -SqlInstance SqlServer2017 -Operation FREEPROCCACHE -InputValue 0x060006001ECA270EC0215D05000000000000000000000000
Remove a specific plan with plan_handle 0x060006001ECA270EC0215D05000000000000000000000000 from the cache via the command DBCC FREEPROCCACHE(0x060006001ECA270EC0215D05000000000000000000000000) against the instance SqlServer2017 using Windows Authentication
.EXAMPLE
PS C:\> Invoke-DbaDbccFreeCache -SqlInstance SqlServer2017 -Operation FREEPROCCACHE -InputValue default
Runs the command DBCC FREEPROCCACHE('default') against the instance SqlServer2017 using Windows Authentication. This clears all cache entries associated with a resource pool 'default'.
.EXAMPLE
PS C:\> Invoke-DbaDbccFreeCache -SqlInstance SqlServer2017 -Operation FREESYSTEMCACHE -InputValue default
Runs the command DBCC FREESYSTEMCACHE ('ALL', default) against the instance SqlServer2017 using Windows Authentication. This will clean all the caches with entries specific to the resource pool named "default".
.EXAMPLE
PS C:\> Invoke-DbaDbccFreeCache -SqlInstance SqlServer2017 -Operation FREESYSTEMCACHE -InputValue default -MarkInUseForRemoval
Runs the command DBCC FREESYSTEMCACHE ('ALL', default) WITH MARK_IN_USE_FOR_REMOVAL against the instance SqlServer2017 using Windows Authentication. This will to release entries once the entries become unused for all the caches with entries specific to the resource pool named "default".
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[ValidateSet('FreeProcCache', 'FreeSessionCache', 'FreeSystemCache')]
[string]$Operation = "FreeProcCache",
[string]$InputValue,
[switch]$NoInformationalMessages,
[switch]$MarkInUseForRemoval,
[switch]$EnableException
)
begin {
if (Test-Bound -ParameterName Operation) {
$Operation = $Operation.ToUpper()
} else {
Write-Message -Level Warning -Message "You must specify an operation "
continue
}
$stringBuilder = New-Object System.Text.StringBuilder
if ($Operation -eq 'FREESESSIONCACHE') {
$null = $stringBuilder.Append("DBCC $Operation")
if (Test-Bound -ParameterName NoInformationalMessages) {
$null = $stringBuilder.Append(" WITH NO_INFOMSGS")
}
}
if ($Operation -eq 'FREEPROCCACHE') {
if (Test-Bound -ParameterName InputValue) {
if ($InputValue.StartsWith('0x')) {
$null = $stringBuilder.Append("DBCC $Operation($InputValue)")
} else {
$null = $stringBuilder.Append("DBCC $Operation('$InputValue')")
}
if (Test-Bound -ParameterName NoInformationalMessages) {
$null = $stringBuilder.Append(" WITH NO_INFOMSGS")
}
} else {
$null = $stringBuilder.Append("DBCC $Operation")
if (Test-Bound -ParameterName NoInformationalMessages) {
$null = $stringBuilder.Append(" WITH NO_INFOMSGS")
}
}
}
if ($Operation -eq 'FREESYSTEMCACHE') {
if (Test-Bound -ParameterName InputValue) {
$null = $stringBuilder.Append("DBCC FREESYSTEMCACHE('ALL', $InputValue)")
} else {
$null = $stringBuilder.Append("DBCC FREESYSTEMCACHE('ALL')")
}
if (Test-Bound -ParameterName NoInformationalMessages) {
if (Test-Bound -ParameterName MarkInUseForRemoval) {
$null = $stringBuilder.Append(" WITH NO_INFOMSGS, MARK_IN_USE_FOR_REMOVAL")
} else {
$null = $stringBuilder.Append(" WITH NO_INFOMSGS")
}
} elseif (Test-Bound -ParameterName MarkInUseForRemoval) {
$null = $stringBuilder.Append(" WITH MARK_IN_USE_FOR_REMOVAL")
}
}
}
process {
$query = $StringBuilder.ToString()
foreach ($instance in $SqlInstance) {
Write-Message -Message "Attempting Connection to $instance" -Level Verbose
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
try {
if ($Pscmdlet.ShouldProcess($server.Name, "Execute the command $query against $instance")) {
Write-Message -Message "Query to run: $query" -Level Verbose
$results = $server | Invoke-DbaQuery -Query $query -MessagesToOutput
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
}
If ($Pscmdlet.ShouldProcess("console", "Outputting object")) {
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Operation = $Operation
Cmd = $query.ToString()
Output = $results
}
}
}
}
}
function Invoke-DbaDbClone {
<#
.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:
- https://sqlperformance.com/2016/08/sql-statistics/expanding-dbcc-clonedatabase
- https://support.microsoft.com/en-us/help/3177838/how-to-use-dbcc-clonedatabase-to-generate-a-schema-and-statistics-only
Thanks to Microsoft Tiger Team for the code and idea https://github.com/Microsoft/tigertoolbox/
.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 to clone - this list is auto-populated from the server.
.PARAMETER InputObject
Enables piping from Get-DbaDatabase
.PARAMETER CloneDatabase
The name(s) to clone to.
.PARAMETER ExcludeStatistics
Exclude the statistics in the cloned database
.PARAMETER ExcludeQueryStore
Exclude the QueryStore data in the cloned database
.PARAMETER UpdateStatistics
Update the statistics prior to cloning (per Microsoft Tiger Team formula)
.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: Statistics, Performance, Clone
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaDbClone
.EXAMPLE
PS C:\> Invoke-DbaDbClone -SqlInstance sql2016 -Database mydb -CloneDatabase myclone
Clones mydb to myclone on sql2016
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2016 -Database mydb | Invoke-DbaDbClone -CloneDatabase myclone, myclone2 -UpdateStatistics
Updates the statistics of mydb then clones to myclone and myclone2
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[string[]]$CloneDatabase,
[switch]$ExcludeStatistics,
[switch]$ExcludeQueryStore,
[switch]$UpdateStatistics,
[switch]$EnableException
)
begin {
if (-not $Database -and $SqlInstance) {
Stop-Function -Message "You must specify a database name if you did not pipe a database"
}
$sqlStats = "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
"
$noStats = "NO_STATISTICS"
$noQueryStore = "NO_QUERYSTORE"
if ( (Test-Bound -ParameterName 'ExcludeStatistics') -or (Test-Bound -ParameterName 'ExcludeQueryStore') ) {
$sqlWith = ""
if ($ExcludeStatistics) {
$sqlWith = "WITH $noStats"
}
if ($ExcludeQueryStore) {
$sqlWith = "WITH $noQueryStore"
}
if ($ExcludeStatistics -and $ExcludeQueryStore) {
$sqlWith = "WITH $noStats,$noQueryStore"
}
}
$sql2012min = [version]"11.0.7001.0" # SQL 2012 SP4
$sql2014min = [version]"12.0.5000.0" # SQL 2014 SP2
$sql2014CuMin = [version]"12.0.5538" # SQL 2014 SP2 + CU3
$sql2016min = [version]"13.0.4001.0" # SQL 2016 SP1
}
process {
if (Test-FunctionInterrupt) { return }
if ($SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential
}
foreach ($db in $InputObject) {
$server = $db.Parent
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 (Test-Bound -ParameterName 'ExcludeStatistics') {
if ($server.VersionMajor -eq 12 -and $server.Version -lt $sql2014CuMin) {
Stop-Function -Message "Unsupported version for $instance. SQL Server 2014 SP1 + CU3 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 (Test-Bound -ParameterName 'ExcludeQueryStore') {
if ($server.VersionMajor -lt 13 - ($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 ($db.IsSystemObject) {
Stop-Function -Message "Only user databases are supported" -Target $instance -Continue
}
if ( (Test-Bound -ParameterName 'UpdateStatistics') -and (Test-Bound -ParameterName 'ExcludeStatistics' -Not) ) {
if ($Pscmdlet.ShouldProcess($instance, "Update statistics in $($db.Name)")) {
try {
Write-Message -Level Verbose -Message "Updating statistics"
$null = $db.Query($sqlStats)
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
}
}
}
$dbName = $db.Name
foreach ($clonedb in $CloneDatabase) {
Write-Message -Level Verbose -Message "Cloning $clonedb from $db"
if ($server.Databases[$clonedb]) {
Stop-Function -Message "Destination clone database $clonedb already exists" -Target $instance -Continue
} else {
if ($Pscmdlet.ShouldProcess($instance, "Execute DBCC CloneDatabase($dbName, $clonedb)")) {
try {
$sql = "DBCC CLONEDATABASE('$dbName','$clonedb') $sqlWith"
Write-Message -Level Debug -Message "Sql Statement: $sql"
$null = $db.Query($sql)
Get-DbaDatabase -SqlInstance $server -Database $clonedb
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
}
}
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Invoke-DbaDatabaseClone
}
}
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaDbDecryptObject
.EXAMPLE
PS C:\> Invoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1
Decrypt object "Function1" in DB1 of instance SQLDB1 and output the data to the user.
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Invoke-DbaDbDecryptObject -SqlInstance SQLDB1 -Database DB1 -ObjectName Function1, Function2
Decrypt objects "Function1" and "Function2" and output the data to the user.
.EXAMPLE
PS C:\> "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)]
[object[]]$Database,
[string[]]$ObjectName,
[ValidateSet('ASCII', 'UTF8')]
[string]$EncodingType = 'ASCII',
[string]$ExportDestination,
[switch]$EnableException
)
begin {
function Invoke-DecryptData() {
param(
[parameter(Mandatory)]
[byte[]]$Secret,
[parameter(Mandatory)]
[byte[]]$KnownPlain,
[parameter(Mandatory)]
[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 {
$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-DbaDbLogShipping {
<#
.SYNOPSIS
Invoke-DbaDbLogShipping sets up log shipping for one or more databases
.DESCRIPTION
Invoke-DbaDbLogShipping 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaDbLogShipping
.EXAMPLE
PS C:\> $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
>> }
>>
PS C:\> Invoke-DbaDbLogShipping @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
PS C:\> $params = @{
>> SourceSqlInstance = 'sql1'
>> DestinationSqlInstance = 'sql2'
>> Database = 'db1'
>> BackupNetworkPath= '\\sql1\logshipping'
>> GenerateFullBackup = $true
>> Force = $true
>> }
>>
PS C:\> Invoke-DbaDbLogShipping @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)]
param(
[parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[Alias("SourceServerInstance", "SourceSqlServerSqlServer", "Source")]
[object]$SourceSqlInstance,
[parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[Alias("DestinationServerInstance", "DestinationSqlServer", "Destination")]
[object[]]$DestinationSqlInstance,
[System.Management.Automation.PSCredential]
$SourceSqlCredential,
[System.Management.Automation.PSCredential]
$SourceCredential,
[System.Management.Automation.PSCredential]
$DestinationSqlCredential,
[System.Management.Automation.PSCredential]
$DestinationCredential,
[Parameter(Mandatory, ValueFromPipeline)]
[object[]]$Database,
[parameter(Mandatory)]
[string]$BackupNetworkPath,
[string]$BackupLocalPath,
[string]$BackupJob,
[int]$BackupRetention,
[string]$BackupSchedule,
[switch]$BackupScheduleDisabled,
[ValidateSet("Daily", "Weekly", "AgentStart", "IdleComputer")]
[object]$BackupScheduleFrequencyType,
[object[]]$BackupScheduleFrequencyInterval,
[ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
[object]$BackupScheduleFrequencySubdayType,
[int]$BackupScheduleFrequencySubdayInterval,
[ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
[object]$BackupScheduleFrequencyRelativeInterval,
[int]$BackupScheduleFrequencyRecurrenceFactor,
[string]$BackupScheduleStartDate,
[string]$BackupScheduleEndDate,
[string]$BackupScheduleStartTime,
[string]$BackupScheduleEndTime,
[int]$BackupThreshold,
[switch]$CompressBackup,
[string]$CopyDestinationFolder,
[string]$CopyJob,
[int]$CopyRetention,
[string]$CopySchedule,
[switch]$CopyScheduleDisabled,
[ValidateSet("Daily", "Weekly", "AgentStart", "IdleComputer")]
[object]$CopyScheduleFrequencyType,
[object[]]$CopyScheduleFrequencyInterval,
[ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
[object]$CopyScheduleFrequencySubdayType,
[int]$CopyScheduleFrequencySubdayInterval,
[ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
[object]$CopyScheduleFrequencyRelativeInterval,
[int]$CopyScheduleFrequencyRecurrenceFactor,
[string]$CopyScheduleStartDate,
[string]$CopyScheduleEndDate,
[string]$CopyScheduleStartTime,
[string]$CopyScheduleEndTime,
[switch]$DisconnectUsers,
[string]$FullBackupPath,
[switch]$GenerateFullBackup,
[int]$HistoryRetention,
[switch]$NoRecovery,
[switch]$NoInitialization,
[string]$PrimaryMonitorServer,
[System.Management.Automation.PSCredential]
$PrimaryMonitorCredential,
[ValidateSet(0, "sqlserver", 1, "windows")]
[object]$PrimaryMonitorServerSecurityMode,
[switch]$PrimaryThresholdAlertEnabled,
[string]$RestoreDataFolder,
[string]$RestoreLogFolder,
[int]$RestoreDelay,
[int]$RestoreAlertThreshold,
[string]$RestoreJob,
[int]$RestoreRetention,
[string]$RestoreSchedule,
[switch]$RestoreScheduleDisabled,
[ValidateSet("Daily", "Weekly", "AgentStart", "IdleComputer")]
[object]$RestoreScheduleFrequencyType,
[object[]]$RestoreScheduleFrequencyInterval,
[ValidateSet('Time', 'Seconds', 'Minutes', 'Hours')]
[object]$RestoreScheduleFrequencySubdayType,
[int]$RestoreScheduleFrequencySubdayInterval,
[ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
[object]$RestoreScheduleFrequencyRelativeInterval,
[int]$RestoreScheduleFrequencyRecurrenceFactor,
[string]$RestoreScheduleStartDate,
[string]$RestoreScheduleEndDate,
[string]$RestoreScheduleStartTime,
[string]$RestoreScheduleEndTime,
[int]$RestoreThreshold,
[string]$SecondaryDatabasePrefix,
[string]$SecondaryDatabaseSuffix,
[string]$SecondaryMonitorServer,
[System.Management.Automation.PSCredential]
$SecondaryMonitorCredential,
[ValidateSet(0, "sqlserver", 1, "windows")]
[object]$SecondaryMonitorServerSecurityMode,
[switch]$SecondaryThresholdAlertEnabled,
[switch]$Standby,
[string]$StandbyDirectory,
[switch]$UseExistingFullBackup,
[string]$UseBackupFolder,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
begin {
Write-Message -Message "Started log shipping for $SourceSqlInstance to $DestinationSqlInstance" -Level Verbose
# Try connecting to the instance
try {
$SourceServer = Connect-SqlInstance -SqlInstance $SourceSqlInstance -SqlCredential $SourceSqlCredential
} catch {
Stop-Function -Message "Could not connect to Sql Server instance $SourceSqlInstance" -ErrorRecord $_ -Target $SourceSqlInstance
return
}
# Check the instance if it is a named instance
$SourceServerName, $SourceInstanceName = $SourceSqlInstance.Split("\")
if ($null -eq $SourceInstanceName) {
$SourceInstanceName = "MSSQLSERVER"
}
# 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 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
}
# Check the backup network path
Write-Message -Message "Testing backup network path $BackupNetworkPath" -Level Verbose
if ((Test-DbaPath -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 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
}
# 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
}
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($destInstance in $DestinationSqlInstance) {
$setupResult = "Success"
$comment = ""
# Try connecting to the instance
try {
$DestinationServer = Connect-SqlInstance -SqlInstance $destInstance -SqlCredential $DestinationSqlCredential
} catch {
Stop-Function -Message "Could not connect to Sql Server instance $destInstance" -ErrorRecord $_ -Target $destInstance
return
}
$DestinationServerName, $DestinationInstanceName = $destInstance.Split("\")
if ($null -eq $DestinationInstanceName) {
$DestinationInstanceName = "MSSQLSERVER"
}
$IsDestinationLocal = $false
# Check if it's local or remote
if ($DestinationServerName -in ".", "localhost", $env:ServerName, "127.0.0.1") {
$IsDestinationLocal = $true
}
# Check the instance names and the database settings
if (($SourceSqlInstance -eq $destInstance) -and (-not $SecondaryDatabasePrefix -or $SecondaryDatabaseSuffix)) {
$setupResult = "Failed"
$comment = "The destination database is the same as the source"
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
}
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 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-DbaPath -Path $CopyDestinationFolder -SqlInstance $destInstance -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 {
$setupResult = "Failed"
$comment = "Something went wrong creating the copy destination folder"
Stop-Function -Message "Something went wrong creating the copy destination folder $CopyDestinationFolder. `n$_" -Target $destInstance -ErrorRecord $_
return
}
}
1 {
$setupResult = "Failed"
$comment = "Copy destination is a mandatory parameter"
Stop-Function -Message "Copy destination is a mandatory parameter. Please make sure the value is entered." -Target $destInstance
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 {
$setupResult = "Failed"
$comment = "Something went wrong creating the copy destination folder"
Stop-Function -Message "Something went wrong creating the copy destination folder $CopyDestinationFolder. `n$_" -Target $destInstance -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-DbaPath -Path $CopyDestinationFolder -SqlInstance $destInstance -SqlCredential $DestinationCredential) -ne $true) {
$setupResult = "Failed"
$comment = "Copy destination folder $CopyDestinationFolder is not valid or can't be reached"
Stop-Function -Message "Copy destination folder $CopyDestinationFolder is not valid or can't be reached." -Target $destInstance
return
} elseif ($CopyDestinationFolder.StartsWith("\\") -and $CopyDestinationFolder -notmatch $RegexUnc) {
$setupResult = "Failed"
$comment = "Copy destination folder $CopyDestinationFolder has to be in the form of \\server\share"
Stop-Function -Message "Copy destination folder $CopyDestinationFolder has to be in the form of \\server\share." -Target $destInstance
return
}
if (-not ($SecondaryDatabasePrefix -or $SecondaryDatabaseSuffix) -and ($SourceServer.Name -eq $DestinationServer.Name) -and ($SourceServer.InstanceName -eq $DestinationServer.InstanceName)) {
if ($Force) {
$SecondaryDatabaseSuffix = "_LS"
} else {
$setupResult = "Failed"
$comment = "Destination database is the same as source database"
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 database using a suffix." -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-DbaPath -Path $StandbyDirectory -SqlInstance $destInstance -SqlCredential $DestinationCredential) -ne $true) {
$setupResult = "Failed"
$comment = "The directory $StandbyDirectory cannot be reached by the destination instance"
Stop-Function -Message "The directory $StandbyDirectory cannot be reached by the destination instance. Please check the permission and credentials." -Target $destInstance
return
}
} elseif (-not $StandbyDirectory -and $Force) {
$StandbyDirectory = $destInstance.BackupDirectory
Write-Message -Message "Stand-by directory was not set. Setting it to $StandbyDirectory" -Level Verbose
} else {
$setupResult = "Failed"
$comment = "Please set the parameter -StandbyDirectory when using -Standby"
Stop-Function -Message "Please set the parameter -StandbyDirectory when using -Standby" -Target $SourceSqlInstance
return
}
}
# Loop through each of the databases
foreach ($db in $DatabaseCollection) {
# Check the status of the database
if ($db.RecoveryModel -ne 'Full') {
$setupResult = "Failed"
$comment = "Database $db is not in FULL recovery mode"
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 a check if the database exists on the secondary instance
if ($NoInitialization -and ($DestinationServer.Databases.Name -notcontains $SecondaryDatabase)) {
$setupResult = "Failed"
$comment = "Database $SecondaryDatabase needs to be initialized before log shipping setting can continue"
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
if ($setupResult -ne 'Failed') {
Write-Message -Message "Testing database backup network path $DatabaseBackupNetworkPath" -Level Verbose
if ((Test-DbaPath -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
$null = New-Item -Path $DatabaseBackupNetworkPath -ItemType Directory -Credential $SourceCredential -Force:$Force
}
} catch {
$setupResult = "Failed"
$comment = "Something went wrong creating the backup directory"
Stop-Function -Message "Something went wrong creating the backup 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)) {
$setupResult = "Failed"
$comment = "Secondary database already exists on instance"
Stop-Function -Message "Secondary database already exists on instance $destInstance." -ErrorRecord $_ -Target $destInstance -Continue
}
# Check if the secondary database needs tobe initialized
if ($setupResult -ne 'Failed') {
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 $destInstance. `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 {
$setupResult = "Failed"
$comment = "The database is not initialized on the secondary instance"
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 $destInstance
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 ($setupResult -ne 'Failed') {
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-DbaPath -Path $DatabaseRestoreDataFolder -SqlInstance $destInstance -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
$null = New-Item -Path $DatabaseRestoreDataFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force
}
} catch {
$setupResult = "Failed"
$comment = "Something went wrong creating the restore data directory"
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-DbaPath -Path $DatabaseRestoreLogFolder -SqlInstance $destInstance -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
$null = New-Item -Path $DatabaseRestoreLogFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force
}
} catch {
$setupResult = "Failed"
$comment = "Something went wrong creating the restore log directory"
Stop-Function -Message "Something went wrong creating the restore log directory" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
}
}
}
}
}
# Check if the full backup path can be reached
if ($setupResult -ne 'Failed') {
if ($FullBackupPath) {
Write-Message -Message "Testing full backup path $FullBackupPath" -Level Verbose
if ((Test-DbaPath -Path $FullBackupPath -SqlInstance $destInstance -SqlCredential $DestinationCredential) -ne $true) {
$setupResult = "Failed"
$comment = "The path to the full backup could not be reached"
Stop-Function -Message ("The path to the full backup could not be reached. Check the path and/or the crdential") -ErrorRecord $_ -Target $destInstance -Continue
}
} elseif ($UseBackupFolder.Length -ge 1) {
Write-Message -Message "Testing backup folder $UseBackupFolder" -Level Verbose
if ((Test-DbaPath -Path $UseBackupFolder -SqlInstance $destInstance -SqlCredential $DestinationCredential) -ne $true) {
$setupResult = "Failed"
$comment = "The path to the backup folder could not be reached"
Stop-Function -Message ("The path to the backup folder could not be reached. Check the path and/or the crdential") -ErrorRecord $_ -Target $destInstance -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 -SqlInstance $SourceSqlInstance -Databases $($db.Name) -LastFull -Credential $SourceSqlCredential
# Check if there was a last backup
if ($null -eq $LastBackup) {
# Test the path to the backup
Write-Message -Message "Testing last backup path $(($LastBackup[-1]).Path[-1])" -Level Verbose
if ((Test-DbaPath -Path ($LastBackup[-1]).Path[-1] -SqlInstance $SourceSqlInstance -SqlCredential $SourceCredential) -ne $true) {
$setupResult = "Failed"
$comment = "The full backup could not be found"
Stop-Function -Message "The full backup could not be found on $($LastBackup.Path). Check path and/or credentials" -ErrorRecord $_ -Target $destInstance -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)) {
$setupResult = "Failed"
$comment = "The last full backup is not located on shared location"
Stop-Function -Message "The last full backup is not located on shared location. `n$($_.Exception.Message)" -ErrorRecord $_ -Target $destInstance -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 Verbose
}
}
}
}
# 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
if ($setupResult -ne 'Failed') {
Write-Message -Message "Testing database copy destination path $DatabaseCopyDestinationFolder" -Level Verbose
if ((Test-DbaPath -Path $DatabaseCopyDestinationFolder -SqlInstance $destInstance -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
$null = New-Item -Path $DatabaseCopyDestinationFolder -ItemType Directory -Credential $DestinationCredential -Force:$Force
}
} catch {
$setupResult = "Failed"
$comment = "Something went wrong creating the database copy destination folder"
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 ($setupResult -ne 'Failed') {
if ($GenerateFullBackup) {
if ($PSCmdlet.ShouldProcess($SourceSqlInstance, "Backing up database $db")) {
Write-Message -Message "Generating full backup." -Level Verbose
Write-Message -Message "Backing up database $db to $DatabaseBackupNetworkPath" -Level Verbose
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 Verbose
# Get the last full backup path
#$FullBackupPath = $LastBackup.BackupPath
$BackupPath = $LastBackup.BackupPath
Write-Message -Message "Backup is located at $BackupPath" -Level Verbose
} catch {
$setupResult = "Failed"
$comment = "Something went wrong generating the full backup"
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 $null -eq $PrimaryMonitorServer)) {
Write-Message -Message "Setting monitor server for primary server to $SourceSqlInstance." -Level Verbose
$PrimaryMonitorServer = $SourceSqlInstance
}
# Check the PrimaryMonitorServerSecurityMode if it's SQL Server authentication
if ($PrimaryMonitorServerSecurityMode -eq 0) {
if ($PrimaryMonitorServerLogin) {
$setupResult = "Failed"
$comment = "The PrimaryMonitorServerLogin cannot be empty"
Stop-Function -Message "The PrimaryMonitorServerLogin cannot be empty when using SQL Server authentication." -Target $SourceSqlInstance -Continue
}
if ($PrimaryMonitorServerPassword) {
$setupResult = "Failed"
$comment = "The PrimaryMonitorServerPassword cannot be empty"
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 $null -eq $SecondaryMonitorServer)) {
Write-Message -Message "Setting secondary monitor server for $destInstance to $SourceSqlInstance." -Level Verbose
$SecondaryMonitorServer = $SourceSqlInstance
}
# Check the MonitorServerSecurityMode if it's SQL Server authentication
if ($SecondaryMonitorServerSecurityMode -eq 0) {
if ($SecondaryMonitorServerLogin) {
$setupResult = "Failed"
$comment = "The SecondaryMonitorServerLogin cannot be empty"
Stop-Function -Message "The SecondaryMonitorServerLogin cannot be empty when using SQL Server authentication." -Target $SourceSqlInstance -Continue
}
if ($SecondaryMonitorServerPassword) {
$setupResult = "Failed"
$comment = "The SecondaryMonitorServerPassword cannot be empty"
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 ($setupResult -ne 'Failed') {
if ($PSCmdlet.ShouldProcess($destInstance, "Restoring database $db to $SecondaryDatabase on $destInstance")) {
if ($GenerateFullBackup -or $UseExistingFullBackup -or $UseBackupFolder) {
try {
Write-Message -Message "Start database restore" -Level Verbose
if ($NoRecovery -or (-not $Standby)) {
if ($Force) {
$null = Restore-DbaDatabase -SqlInstance $destInstance `
-SqlCredential $DestinationSqlCredential `
-Path $BackupPath `
-DestinationFilePrefix $SecondaryDatabasePrefix `
-DestinationFileSuffix $SecondaryDatabaseSuffix `
-DestinationDataDirectory $DatabaseRestoreDataFolder `
-DestinationLogDirectory $DatabaseRestoreLogFolder `
-DatabaseName $SecondaryDatabase `
-DirectoryRecurse `
-NoRecovery `
-WithReplace
} else {
$null = Restore-DbaDatabase -SqlInstance $destInstance `
-SqlCredential $DestinationSqlCredential `
-Path $BackupPath `
-DestinationFilePrefix $SecondaryDatabasePrefix `
-DestinationFileSuffix $SecondaryDatabaseSuffix `
-DestinationDataDirectory $DatabaseRestoreDataFolder `
-DestinationLogDirectory $DatabaseRestoreLogFolder `
-DatabaseName $SecondaryDatabase `
-DirectoryRecurse `
-NoRecovery
}
}
# 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) {
$null = Restore-DbaDatabase -SqlInstance $destInstance `
-SqlCredential $DestinationSqlCredential `
-Path $BackupPath `
-DestinationFilePrefix $SecondaryDatabasePrefix `
-DestinationFileSuffix $SecondaryDatabaseSuffix `
-DestinationDataDirectory $DatabaseRestoreDataFolder `
-DestinationLogDirectory $DatabaseRestoreLogFolder `
-DatabaseName $SecondaryDatabase `
-DirectoryRecurse `
-StandbyDirectory $StandbyDirectory
} else {
$null = Restore-DbaDatabase -SqlInstance $destInstance `
-Path $BackupPath `
-DestinationFilePrefix $SecondaryDatabasePrefix `
-DestinationFileSuffix $SecondaryDatabaseSuffix `
-DestinationDataDirectory $DatabaseRestoreDataFolder `
-DestinationLogDirectory $DatabaseRestoreLogFolder `
-DatabaseName $SecondaryDatabase `
-DirectoryRecurse `
-StandbyDirectory $StandbyDirectory
}
}
} catch {
$setupResult = "Failed"
$comment = "Something went wrong restoring the secondary database"
Stop-Function -Message "Something went wrong restoring the secondary database" -ErrorRecord $_ -Target $SourceSqlInstance -Continue
}
Write-Message -Message "Restore completed." -Level Verbose
}
}
}
#region Set up log shipping on the primary instance
# Set up log shipping on the primary instance
if ($setupResult -ne 'Failed') {
if ($PSCmdlet.ShouldProcess($SourceSqlInstance, "Configuring logshipping for primary database $db on $SourceSqlInstance")) {
try {
Write-Message -Message "Configuring logshipping for primary database" -Level Verbose
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) {
$null = Set-DbaAgentJob -SqlInstance $SourceSqlInstance -SqlCredential $SourceSqlCredential -Job $DatabaseBackupJob -Disabled
Write-Message -Message "Disabling backup job $DatabaseBackupJob" -Level Verbose
} else {
$null = Set-DbaAgentJob -SqlInstance $SourceSqlInstance -SqlCredential $SourceSqlCredential -Job $DatabaseBackupJob -Enabled
Write-Message -Message "Enabling backup job $DatabaseBackupJob" -Level Verbose
}
Write-Message -Message "Create backup job schedule $DatabaseBackupSchedule" -Level Verbose
#Variable $BackupJobSchedule marked as unused by PSScriptAnalyzer replaced with $null for catching output
$null = 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 Verbose
New-DbaLogShippingPrimarySecondary -SqlInstance $SourceSqlInstance `
-SqlCredential $SourceSqlCredential `
-PrimaryDatabase $($db.Name) `
-SecondaryDatabase $SecondaryDatabase `
-SecondaryServer $destInstance `
-SecondarySqlCredential $DestinationSqlCredential
} catch {
$setupResult = "Failed"
$comment = "Something went wrong setting up log shipping for primary instance"
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 ($setupResult -ne 'Failed') {
if ($PSCmdlet.ShouldProcess($destInstance, "Configuring logshipping for secondary database $SecondaryDatabase on $destInstance")) {
try {
Write-Message -Message "Configuring logshipping from secondary database $SecondaryDatabase to primary database $db." -Level Verbose
New-DbaLogShippingSecondaryPrimary -SqlInstance $destInstance `
-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 Verbose
#Variable $CopyJobSchedule marked as unused by PSScriptAnalyzer replaced with $null for catching output
$null = New-DbaAgentSchedule -SqlInstance $destInstance `
-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 Verbose
#Variable $RestoreJobSchedule marked as unused by PSScriptAnalyzer replaced with $null for catching output
$null = New-DbaAgentSchedule -SqlInstance $destInstance `
-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 Verbose
New-DbaLogShippingSecondaryDatabase -SqlInstance $destInstance `
-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) {
$null = Set-DbaAgentJob -SqlInstance $destInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseCopyJob -Disabled
} else {
$null = Set-DbaAgentJob -SqlInstance $destInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseCopyJob -Enabled
}
# Check if the restore job needs to be enabled or disabled
if ($RestoreScheduleDisabled) {
$null = Set-DbaAgentJob -SqlInstance $destInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseRestoreJob -Disabled
} else {
$null = Set-DbaAgentJob -SqlInstance $destInstance -SqlCredential $DestinationSqlCredential -Job $DatabaseRestoreJob -Enabled
}
} catch {
$setupResult = "Failed"
$comment = "Something went wrong setting up log shipping for secondary instance"
Stop-Function -Message "Something went wrong setting up log shipping for secondary instance.`n$($_.Exception.Message)" -ErrorRecord $_ -Target $destInstance -Continue
}
}
}
#endregion Set up log shipping on the secondary instance
Write-Message -Message "Completed configuring log shipping for database $db" -Level Verbose
[PSCustomObject]@{
PrimaryInstance = $SourceServer.DomainInstanceName
SecondaryInstance = $DestinationServer.DomainInstanceName
PrimaryDatabase = $($db.Name)
SecondaryDatabase = $SecondaryDatabase
Result = $setupResult
Comment = $comment
}
} # for each database
} # end for each destination server
} # end process
end {
Write-Message -Message "Finished setting up log shipping." -Level Verbose
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Invoke-DbaLogShipping
}
}
function Invoke-DbaDbLogShipRecovery {
<#
.SYNOPSIS
Invoke-DbaDbLogShipRecovery 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
The target SQL Server instance or instances.
.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 InputObject
Allows piped input 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.
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaDbLogShipRecovery
.EXAMPLE
PS C:\> Invoke-DbaDbLogShipRecovery -SqlInstance server1
Recovers all the databases on the instance that are enabled for log shipping
.EXAMPLE
PS C:\> Invoke-DbaDbLogShipRecovery -SqlInstance server1 -SqlCredential $cred -Verbose
Recovers all the databases on the instance that are enabled for log shipping using a credential
.EXAMPLE
PS C:\> Invoke-DbaDbLogShipRecovery -SqlInstance server1 -database db_logship -Verbose
Recovers the database "db_logship" to a normal status
.EXAMPLE
PS C:\> db1, db2, db3, db4 | Invoke-DbaDbLogShipRecovery -SqlInstance server1 -Verbose
Recovers the database db1, db2, db3, db4 to a normal status
.EXAMPLE
PS C:\> Invoke-DbaDbLogShipRecovery -SqlInstance server1 -WhatIf
Shows what would happen if the command were executed.
#>
[CmdletBinding(SupportsShouldProcess)]
param
(
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[string[]]$Database,
[PSCredential]$SqlCredential,
[switch]$NoRecovery,
[Alias('Silent')]
[switch]$EnableException,
[switch]$Force,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[int]$Delay = 5
)
begin {
$stepCounter = 0
}
process {
foreach ($instance in $SqlInstance) {
if (-not $Force -and -not $Database) {
Stop-Function -Message "You must specify a -Database or -Force for all databases" -Target $server.name
return
}
$InputObject += Get-DbaDatabase -SqlInstance $instance -SqlCredential $SqlCredential -Database $Database
}
# Loop through all the databases
foreach ($db in $InputObject) {
$stepCounter = 0
$server = $db.Parent
$instance = $server.Name
$activity = "Performing log shipping recovery for $($db.Name) on $($server.Name)"
# Try to get the agent service details
try {
# Get the service details
$agentStatus = $server.Query("SELECT COUNT(*) as AgentCount FROM master.dbo.sysprocesses WITH (nolock) WHERE Program_Name LIKE 'SQLAgent%'")
if ($agentStatus.AgentCount -lt 1) {
Stop-Function -Message "The agent service is not in a running state. Please start the service." -ErrorRecord $_ -Target $server.name
return
}
} catch {
Stop-Function -Message "Unable to get SQL Server Agent Service status" -ErrorRecord $_ -Target $server.name
return
}
# 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
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Retrieving log shipping information from the secondary instance"
$logshipping_details = $server.Query($query)
} catch {
Stop-Function -Message "Error retrieving the log shipping details: $($_.Exception.Message)" -ErrorRecord $_ -Target $server.name
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
$recoverResult = "Success"
$comment = ""
$jobOutputs = @()
# 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
# Start the job to get the latest files
if ($PSCmdlet.ShouldProcess($server.name, ("Starting copy job $($ls.copyjob)"))) {
Write-Message -Message "Starting copy job $($ls.copyjob)" -Level Verbose
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Starting copy job"
try {
$null = Start-DbaAgentJob -SqlInstance $instance -SqlCredential $sqlcredential -Job $ls.copyjob
} catch {
$recoverResult = "Failed"
$comment = "Something went wrong starting the copy job $($ls.copyjob)"
Stop-Function -Message "Something went wrong starting the copy job.`n$($_)" -ErrorRecord $_ -Target $server.name
}
if ($recoverResult -ne 'Failed') {
Write-Message -Message "Copying files to $($ls.backup_destination_directory)" -Level Verbose
Write-Message -Message "Waiting for the copy action to complete.." -Level Verbose
# Get the job status
$jobStatus = Get-DbaAgentJob -SqlInstance $instance -SqlCredential $sqlcredential -Job $ls.copyjob
while ($jobStatus.CurrentRunStatus -ne 'Idle') {
# Sleep for while to let the files be copied
Start-Sleep -Seconds $Delay
# Get the job status
$jobStatus = Get-DbaAgentJob -SqlInstance $instance -SqlCredential $sqlcredential -Job $ls.copyjob
}
# Check the lat outcome of the job
if ($jobStatus.LastRunOutcome -eq 'Failed') {
$recoverResult = "Failed"
$comment = "The copy job for database $db failed. Please check the error log."
Stop-Function -Message "The copy job for database $db failed. Please check the error log."
}
$jobOutputs += $jobStatus
Write-Message -Message "Copying of backup files finished" -Level Verbose
}
} # if should process
# Disable the log shipping copy job on the secondary instance
if ($recoverResult -ne 'Failed') {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Disabling copy job"
if ($PSCmdlet.ShouldProcess($server.name, "Disabling copy job $($ls.copyjob)")) {
try {
Write-Message -Message "Disabling copy job $($ls.copyjob)" -Level Verbose
$null = Set-DbaAgentJob -SqlInstance $instance -SqlCredential $sqlcredential -Job $ls.copyjob -Disabled
} catch {
$recoverResult = "Failed"
$comment = "Something went wrong disabling the copy job."
Stop-Function -Message "Something went wrong disabling the copy job.`n$($_)" -ErrorRecord $_ -Target $server.name
}
}
}
if ($recoverResult -ne 'Failed') {
# Start the restore job
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Starting restore job"
if ($PSCmdlet.ShouldProcess($server.name, ("Starting restore job " + $ls.restorejob))) {
Write-Message -Message "Starting restore job $($ls.restorejob)" -Level Verbose
try {
$null = Start-DbaAgentJob -SqlInstance $instance -SqlCredential $sqlcredential -Job $ls.restorejob
} catch {
$comment = "Something went wrong starting the restore job."
Stop-Function -Message "Something went wrong starting the restore job.`n$($_)" -ErrorRecord $_ -Target $server.name
}
Write-Message -Message "Waiting for the restore action to complete.." -Level Verbose
# Get the job status
$jobStatus = Get-DbaAgentJob -SqlInstance $instance -SqlCredential $sqlcredential -Job $ls.restorejob
while ($jobStatus.CurrentRunStatus -ne 'Idle') {
# Sleep for while to let the files be copied
Start-Sleep -Seconds $Delay
# Get the job status
$jobStatus = Get-DbaAgentJob -SqlInstance $instance -SqlCredential $sqlcredential -Job $ls.restorejob
}
# Check the lat outcome of the job
if ($jobStatus.LastRunOutcome -eq 'Failed') {
$recoverResult = "Failed"
$comment = "The restore job for database $db failed. Please check the error log."
Stop-Function -Message "The restore job for database $db failed. Please check the error log."
}
$jobOutputs += $jobStatus
}
}
if ($recoverResult -ne 'Failed') {
# Disable the log shipping restore job on the secondary instance
if ($PSCmdlet.ShouldProcess($server.name, "Disabling restore job $($ls.restorejob)")) {
try {
Write-Message -Message ("Disabling restore job " + $ls.restorejob) -Level Verbose
$null = Set-DbaAgentJob -SqlInstance $instance -SqlCredential $sqlcredential -Job $ls.restorejob -Disabled
} catch {
$recoverResult = "Failed"
$comment = "Something went wrong disabling the restore job."
Stop-Function -Message "Something went wrong disabling the restore job.`n$($_)" -ErrorRecord $_ -Target $server.name
}
}
}
if ($recoverResult -ne 'Failed') {
# 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
try {
$query = "RESTORE DATABASE [$secondarydb] WITH RECOVERY"
$server.Query($query)
} catch {
$recoverResult = "Failed"
$comment = "Something went wrong restoring the database to a normal state."
Stop-Function -Message "Something went wrong restoring the database to a normal state.`n$($_)" -ErrorRecord $_ -Target $secondarydb
}
}
} else {
$comment = "Skipping restore with recovery."
Write-Message -Message "Skipping restore with recovery" -Level Verbose
}
Write-Message -Message ("Finished Recovery for $secondarydb") -Level Verbose
}
# Reset the log ship details
$logshipping_details = $null
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.InstanceName
SqlInstance = $server.DomainInstanceName
Database = $secondarydb
RecoverResult = $recoverResult
Comment = $comment
}
}
}
}
Write-Progress -Activity $activity -Completed
$stepCounter = 0
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Invoke-DbaLogShippingRecovery
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-DbaDbMirrorFailover {
<#
.SYNOPSIS
Failover a mirrored database
.DESCRIPTION
Failover a mirrored database
.PARAMETER SqlInstance
SQL Server name or SMO object representing the primary SQL Server.
.PARAMETER SqlCredential
Login to the primary instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Database
The database or databases to mirror
.PARAMETER InputObject
Allows piping from Get-DbaDatabase
.PARAMETER Force
Force Failover and allow data loss
.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: Mirror, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
TODO: add service accounts
.LINK
https://dbatools.io/Invoke-DbaDbMirrorFailover
.EXAMPLE
PS C:\> Invoke-DbaDbMirrorFailover -SqlInstance sql2016 -Database pubs
Fails over the pubs database on sql2016. Prompts for confirmation.
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2016 -Database pubs | Invoke-DbaDbMirrorFailover -Force -Confirm:$false
Forces the failover of the pubs database on sql2016 and allows data loss.
Does not prompt for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$Force,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName Database)) {
Stop-Function -Message "Database is required when SqlInstance is specified"
return
}
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database
foreach ($db in $InputObject) {
# if it's async, you have to break the mirroring and allow data loss
# alter database set partner force_service_allow_data_loss
# if it's sync mirroring you know it's all in sync, so you can just do alter database [dbname] set partner failover
if ($Force) {
if ($Pscmdlet.ShouldProcess($db.Parent.Name, "Forcing failover of $db and allowing data loss")) {
$db | Set-DbaDbMirror -State ForceFailoverAndAllowDataLoss
}
} else {
if ($Pscmdlet.ShouldProcess($db.Parent.Name, "Setting safety level to full and failing over $db to partner server")) {
$db | Set-DbaDbMirror -SafetyLevel Full
$db | Set-DbaDbMirror -State Failover
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Invoke-DbaDbMirroring {
<#
.SYNOPSIS
Automates the creation of database mirrors.
.DESCRIPTION
Automates the creation of database mirrors.
* Verifies that a mirror is possible
* Sets the recovery model to Full if needed
* If the database does not exist on mirror or witness, a backup/restore is performed
* Sets up endpoints if necessary
* Creates a login and grants permissions to service accounts if needed
* Starts endpoints if needed
* Sets up partner for mirror
* Sets up partner for primary
* Sets up witness if one is specified
NOTE: If a backup / restore is performed, the backups will be left in tact on the network share.
.PARAMETER Primary
SQL Server name or SMO object representing the primary SQL Server.
.PARAMETER PrimarySqlCredential
Login to the primary instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Mirror
SQL Server name or SMO object representing the mirror SQL Server.
.PARAMETER MirrorSqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Witness
SQL Server name or SMO object representing the witness SQL Server.
.PARAMETER WitnessSqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Database
The database or databases to mirror.
.PARAMETER SharedPath
The network share where the backups will be backed up and restored from.
Each SQL Server service account must have access to this share.
NOTE: If a backup / restore is performed, the backups will be left in tact on the network share.
.PARAMETER InputObject
Enables piping from Get-DbaDatabase.
.PARAMETER UseLastBackup
Use the last full backup of database.
.PARAMETER Force
Drop and recreate the database on remote servers using fresh backup.
.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: Mirror, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaDbMirroring
.EXAMPLE
PS C:\> $params = @{
>> Primary = 'sql2017a'
>> Mirror = 'sql2017b'
>> MirrorSqlCredential = 'sqladmin'
>> Witness = 'sql2019'
>> Database = 'pubs'
>> SharedPath = '\\nas\sql\share'
>> }
>>
PS C:\> Invoke-DbaDbMirror @params
Performs a bunch of checks to ensure the pubs database on sql2017a
can be mirrored from sql2017a to sql2017b. Logs in to sql2019 and sql2017a
using Windows credentials and sql2017b using a SQL credential.
Prompts for confirmation for most changes. To avoid confirmation, use -Confirm:$false or
use the syntax in the second example.
.EXAMPLE
PS C:\> $params = @{
>> Primary = 'sql2017a'
>> Mirror = 'sql2017b'
>> MirrorSqlCredential = 'sqladmin'
>> Witness = 'sql2019'
>> Database = 'pubs'
>> SharedPath = '\\nas\sql\share'
>> Force = $true
>> Confirm = $false
>> }
>>
PS C:\> Invoke-DbaDbMirror @params
Performs a bunch of checks to ensure the pubs database on sql2017a
can be mirrored from sql2017a to sql2017b. Logs in to sql2019 and sql2017a
using Windows credentials and sql2017b using a SQL credential.
Drops existing pubs database on Mirror and Witness and restores them with
a fresh backup.
Does all the things in the decription, does not prompt for confirmation.
.EXAMPLE
PS C:\> $map = @{ 'database_data' = 'M:\Data\database_data.mdf' 'database_log' = 'L:\Log\database_log.ldf' }
PS C:\> Get-ChildItem \\nas\seed | Restore-DbaDatabase -SqlInstance sql2017b -FileMapping $map -NoRecovery
PS C:\> Get-DbaDatabase -SqlInstance sql2017a -Database pubs | Invoke-DbaDbMirroring -Mirror sql2017b -Confirm:$false
Restores backups from sql2017a to a specific file struture on sql2017b then creates mirror with no prompts for confirmation.
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2017a -Database pubs |
>> Invoke-DbaDbMirroring -Mirror sql2017b -UseLastBackup -Confirm:$false
Mirrors pubs on sql2017a to sql2017b and uses the last full and logs from sql2017a to seed. Doesn't prompt for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter]$Primary,
[PSCredential]$PrimarySqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Mirror,
[PSCredential]$MirrorSqlCredential,
[DbaInstanceParameter]$Witness,
[PSCredential]$WitnessSqlCredential,
[string[]]$Database,
[Alias("NetworkShare")]
[string]$SharedPath,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$UseLastBackup,
[switch]$Force,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter NetworkShare -CustomMessage "Using the parameter NetworkShare is deprecated. This parameter will be removed in version 1.0.0 or before. Use SharedPath instead."
$params = $PSBoundParameters
$null = $params.Remove('UseLastBackup')
$null = $params.Remove('Force')
$null = $params.Remove('Confirm')
$null = $params.Remove('Whatif')
}
process {
if ((Test-Bound -ParameterName Primary) -and (Test-Bound -Not -ParameterName Database)) {
Stop-Function -Message "Database is required when Primary is specified"
return
}
if ($Force -and (-not $SharedPath -and -not $UseLastBackup)) {
Stop-Function -Message "SharedPath or UseLastBackup is required when Force is used"
return
}
if ($Primary) {
$InputObject += Get-DbaDatabase -SqlInstance $Primary -SqlCredential $PrimarySqlCredential -Database $Database
}
foreach ($primarydb in $InputObject) {
$stepCounter = 0
$Primary = $source = $primarydb.Parent
try {
$dest = Connect-SqlInstance -SqlInstance $Mirror -SqlCredential $MirrorSqlCredential
if ($Witness) {
$witserver = Connect-SqlInstance -SqlInstance $Witness -SqlCredential $WitnessSqlCredential
}
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$dbName = $primarydb.Name
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Validating mirror setup"
# Thanks to https://github.com/mmessano/PowerShell/blob/master/SQL-ConfigureDatabaseMirroring.ps1 for the tips
$validation = Invoke-DbMirrorValidation @params
if ((Test-Bound -ParameterName SharedPath) -and -not $validation.AccessibleShare) {
Stop-Function -Continue -Message "Cannot access $SharedPath from $($dest.Name)"
}
if (-not $validation.EditionMatch) {
Stop-Function -Continue -Message "This mirroring configuration is not supported. Because the principal server instance, $source, is $($source.EngineEdition) Edition, the mirror server instance must also be $($source.EngineEdition) Edition."
}
if ($validation.MirroringStatus -ne "None") {
Stop-Function -Continue -Message "Cannot setup mirroring on database ($dbname) due to its current mirroring state: $($primarydb.MirroringStatus)"
}
if ($primarydb.Status -ne "Normal") {
Stop-Function -Continue -Message "Cannot setup mirroring on database ($dbname) due to its current state: $($primarydb.Status)"
}
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Setting recovery model for $dbName on $($source.Name) to Full"
if ($primarydb.RecoveryModel -ne "Full") {
if ((Test-Bound -ParameterName UseLastBackup)) {
Stop-Function -Continue -Message "$dbName not set to full recovery. UseLastBackup cannot be used."
} else {
Set-DbaDbRecoveryModel -SqlInstance $source -Database $primarydb.Name -RecoveryModel Full
}
}
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Copying $dbName from primary to mirror"
if (-not $validation.DatabaseExistsOnMirror -or $Force) {
if ($UseLastBackup) {
$allbackups = Get-DbaBackupHistory -SqlInstance $primarydb.Parent -Database $primarydb.Name -IncludeCopyOnly -Last
} else {
if ($Force -or $Pscmdlet.ShouldProcess("$Primary", "Creating full and log backups of $primarydb on $SharedPath")) {
$fullbackup = $primarydb | Backup-DbaDatabase -BackupDirectory $SharedPath -Type Full
$logbackup = $primarydb | Backup-DbaDatabase -BackupDirectory $SharedPath -Type Log
$allbackups = $fullbackup, $logbackup
}
}
if ($Pscmdlet.ShouldProcess("$Mirror", "Restoring full and log backups of $primarydb from $Primary")) {
foreach ($mirrorinstance in $Mirror) {
try {
$null = $allbackups | Restore-DbaDatabase -SqlInstance $mirrorinstance -SqlCredential $MirrorSqlCredential -WithReplace -NoRecovery -TrustDbBackupHistory -EnableException
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $dest -Continue
}
}
}
if ($SharedPath) {
Write-Message -Level Verbose -Message "Backups still exist on $SharedPath"
}
}
$mirrordb = Get-DbaDatabase -SqlInstance $dest -Database $dbName
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Copying $dbName from primary to witness"
if ($Witness -and (-not $validation.DatabaseExistsOnWitness -or $Force)) {
if (-not $allbackups) {
if ($UseLastBackup) {
$allbackups = Get-DbaBackupHistory -SqlInstance $primarydb.Parent -Database $primarydb.Name -IncludeCopyOnly -Last
} else {
if ($Force -or $Pscmdlet.ShouldProcess("$Primary", "Creating full and log backups of $primarydb on $SharedPath")) {
$fullbackup = $primarydb | Backup-DbaDatabase -BackupDirectory $SharedPath -Type Full
$logbackup = $primarydb | Backup-DbaDatabase -BackupDirectory $SharedPath -Type Log
$allbackups = $fullbackup, $logbackup
}
}
}
if ($Pscmdlet.ShouldProcess("$Witness", "Restoring full and log backups of $primarydb from $Primary")) {
try {
$null = $allbackups | Restore-DbaDatabase -SqlInstance $Witness -SqlCredential $WitnessSqlCredential -WithReplace -NoRecovery -TrustDbBackupHistory -EnableException
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $witserver -Continue
}
}
}
$primaryendpoint = Get-DbaEndpoint -SqlInstance $source | Where-Object EndpointType -eq DatabaseMirroring
$mirrorendpoint = Get-DbaEndpoint -SqlInstance $dest | Where-Object EndpointType -eq DatabaseMirroring
if (-not $primaryendpoint) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Setting up endpoint for primary"
$primaryendpoint = New-DbaEndpoint -SqlInstance $source -Type DatabaseMirroring -Role Partner -Name Mirroring -EncryptionAlgorithm RC4
$null = $primaryendpoint | Stop-DbaEndpoint
$null = $primaryendpoint | Start-DbaEndpoint
}
if (-not $mirrorendpoint) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Setting up endpoint for mirror"
$mirrorendpoint = New-DbaEndpoint -SqlInstance $dest -Type DatabaseMirroring -Role Partner -Name Mirroring -EncryptionAlgorithm RC4
$null = $mirrorendpoint | Stop-DbaEndpoint
$null = $mirrorendpoint | Start-DbaEndpoint
}
if ($witserver) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Setting up endpoint for witness"
$witnessendpoint = Get-DbaEndpoint -SqlInstance $witserver | Where-Object EndpointType -eq DatabaseMirroring
if (-not $witnessendpoint) {
$witnessendpoint = New-DbaEndpoint -SqlInstance $witserver -Type DatabaseMirroring -Role Witness -Name Mirroring -EncryptionAlgorithm RC4
$null = $witnessendpoint | Stop-DbaEndpoint
$null = $witnessendpoint | Start-DbaEndpoint
}
}
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Granting permissions to service account"
$serviceaccounts = $source.ServiceAccount, $dest.ServiceAccount, $witserver.ServiceAccount | Select-Object -Unique
foreach ($account in $serviceaccounts) {
if ($Pscmdlet.ShouldProcess("primary, mirror and witness (if specified)", "Creating login $account and granting CONNECT ON ENDPOINT")) {
$null = New-DbaLogin -SqlInstance $source -Login $account -WarningAction SilentlyContinue
$null = New-DbaLogin -SqlInstance $dest -Login $account -WarningAction SilentlyContinue
try {
$null = $source.Query("GRANT CONNECT ON ENDPOINT::$primaryendpoint TO [$account]")
$null = $dest.Query("GRANT CONNECT ON ENDPOINT::$mirrorendpoint TO [$account]")
if ($witserver) {
$null = New-DbaLogin -SqlInstance $witserver -Login $account -WarningAction SilentlyContinue
$witserver.Query("GRANT CONNECT ON ENDPOINT::$witnessendpoint TO [$account]")
}
} catch {
Stop-Function -Continue -Message "Failure" -ErrorRecord $_
}
}
}
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Starting endpoints if necessary"
try {
$null = $primaryendpoint, $mirrorendpoint, $witnessendpoint | Start-DbaEndpoint -EnableException
} catch {
Stop-Function -Continue -Message "Failure" -ErrorRecord $_
}
try {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Setting up partner for mirror"
$null = $mirrordb | Set-DbaDbMirror -Partner $primaryendpoint.Fqdn -EnableException
} catch {
Stop-Function -Continue -Message "Failure on mirror" -ErrorRecord $_
}
try {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Setting up partner for primary"
$null = $primarydb | Set-DbaDbMirror -Partner $mirrorendpoint.Fqdn -EnableException
} catch {
Stop-Function -Continue -Message "Failure on primary" -ErrorRecord $_
}
try {
if ($witnessendpoint) {
$null = $primarydb | Set-DbaDbMirror -Witness $witnessendpoint.Fqdn -EnableException
}
} catch {
Stop-Function -Continue -Message "Failure with the new last part" -ErrorRecord $_
}
if ($Pscmdlet.ShouldProcess("console", "Showing results")) {
$results = [pscustomobject]@{
Primary = $Primary
Mirror = $Mirror -join ", "
Witness = $Witness
Database = $primarydb.Name
Status = "Success"
}
if ($Witness) {
$results | Select-DefaultView -Property Primary, Mirror, Witness, Database, Status
} else {
$results | Select-DefaultView -Property Primary, Mirror, Database, Status
}
}
}
}
}
function Invoke-DbaDbShrink {
<#
.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 instance or instances. Defaults to the default instance on localhost.
.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 AllUserDatabases
Run command against all user databases.
.PARAMETER PercentFreeSpace
Specifies how much free space to leave, 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 StepSize
Measured in bits - but no worries! PowerShell has a very cool way of formatting bits. Just specify something like: 1MB or 10GB. See the examples for more information.
If specified, this will chunk a larger shrink operation into multiple smaller shrinks.
If shrinking a file by a large amount there are benefits of doing multiple smaller chunks.
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaDbShrink
.EXAMPLE
PS C:\> Invoke-DbaDbShrink -SqlInstance sql2016 -Database Northwind,pubs,Adventureworks2014
Shrinks Northwind, pubs and Adventureworks2014 to have as little free space as possible.
.EXAMPLE
PS C:\> Invoke-DbaDbShrink -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
PS C:\> Invoke-DbaDbShrink -SqlInstance sql2014 -Database AdventureWorks2014 -PercentFreeSpace 50 -FileType Data -StepSize 25MB
Shrinks AdventureWorks2014 to have 50% free space, runs shrinks in 25MB chunks for improved performance.
.EXAMPLE
PS C:\> Invoke-DbaDbShrink -SqlInstance sql2012 -AllUserDatabases
Shrinks all databases on SQL2012 (not ideal for production)
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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]$StepSize,
[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'
}
if (-not $Database -and -not $ExcludeDatabase -and -not $AllUserDatabases) {
Stop-Function -Message "You must specify databases to execute against using either -Database, -Exclude or -AllUserDatabases"
return
}
if ((Test-Bound -ParameterName StepSize) -and $stepsize -lt 1024) {
Stop-Function -Message "StepSize is measured in bits. Did you mean $StepSize bits? If so, please use 1024 or above. If not, then use the PowerShell bit notation like $($StepSize)MB or $($StepSize)GB"
return
}
if ($stepsize) {
$StepSizeKB = ([dbasize]($StepSize)).Kilobyte
}
$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 (Test-FunctionInterrupt) { return }
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$server.ConnectionContext.StatementTimeout = $StatementTimeoutSeconds
Write-Message -Level Verbose -Message "Connection timeout set to $StatementTimeout"
$dbs = $server.Databases | Where-Object {$_.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
}
$files = @()
if ($FileType -in ('Log', 'All')) {
$files += $db.LogFiles
}
if ($FileType -in ('Data', 'All')) {
$files += $db.FileGroups.Files
}
foreach ($file in $files) {
$startingSize = $file.Size
$spaceUsed = $file.UsedSpace
$spaceAvailable = ($file.Size - $file.UsedSpace)
$desiredSpaceAvailable = [math]::ceiling((($PercentFreeSpace / 100)) * $spaceUsed)
$desiredFileSize = $spaceUsed + $desiredSpaceAvailable
Write-Message -Level Verbose -Message "File: $($file.Name)"
Write-Message -Level Verbose -Message "Initial Size (KB): $([int]$startingSize)"
Write-Message -Level Verbose -Message "Space Used (KB): $([int]$spaceUsed)"
Write-Message -Level Verbose -Message "Initial Freespace (KB): $([int]$spaceAvailable)"
Write-Message -Level Verbose -Message "Target Freespace (KB): $([int]$desiredSpaceAvailable)"
Write-Message -Level Verbose -Message "Target FileSize (KB): $([int]$desiredFileSize)"
if ($spaceAvailable -le $desiredSpaceAvailable) {
Write-Message -Level Warning -Message "File size of ($startingSize) is less than or equal to the desired outcome ($desiredFileSize) for $($file.Name)"
} else {
if ($Pscmdlet.ShouldProcess("$db on $instance", "Shrinking from $([int]$startingSize)KB to $([int]$desiredFileSize)KB")) {
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
try {
Write-Message -Level Verbose -Message "Beginning shrink of files"
$shrinkGap = ($startingSize - $desiredFileSize)
Write-Message -Level Verbose -Message "ShrinkGap: $([int]$shrinkGap) KB"
Write-Message -Level Verbose -Message "Step Size: $(([dbasize]$StepSize).Megabyte) MB"
if ($StepSizeKB -and ($shrinkGap -gt $stepSizeKB)) {
for ($i = 1; $i -le [int](($shrinkGap) / $stepSizeKB); $i++) {
Write-Message -Level Verbose -Message "Step: $i"
$shrinkSize = $startingSize - (($StepSizeKB * 1024 * 1024) * $i)
if ($shrinkSize -lt $desiredFileSize) {
$shrinkSize = $desiredFileSize
}
Write-Message -Level Verbose -Message ("Shrinking {0} to {1}" -f $file.Name, $shrinkSize)
$file.Shrink(($shrinkSize / 1024), $ShrinkMethod)
$file.Refresh()
if ($startingSize -eq $file.Size) {
Write-Message -Level Verbose -Message ("Unable to shrink further")
break
}
}
} else {
$file.Shrink(($desiredFileSize / 1024), $ShrinkMethod)
$file.Refresh()
}
$success = $true
} catch {
$success = $false
Stop-Function -message "Failure" -EnableException $EnableException -ErrorRecord $_ -Continue
continue
}
$end = Get-Date
$finalFileSize = $file.Size
$finalSpaceAvailable = ($file.Size - $file.UsedSpace)
Write-Message -Level Verbose -Message "Final file size: $([int]$finalFileSize) KB"
Write-Message -Level Verbose -Message "Final file space available: $($finalSpaceAvailable) KB"
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)
$object = [PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Database = $db.name
File = $file.name
Start = $start
End = $end
Elapsed = $elapsed
Success = $success
InitialSize = [dbasize]($startingSize * 1024)
InitialUsed = [dbasize]($spaceUsed * 1024)
InitialAvailable = [dbasize]($spaceAvailable * 1024)
TargetAvailable = [dbasize]($desiredSpaceAvailable * 1024)
FinalAvailable = [dbasize]($finalSpaceAvailable * 1024)
FinalSize = [dbasize]($finalFileSize * 1024)
InitialAverageFragmentation = [math]::Round($startingFrag, 1)
FinalAverageFragmentation = [math]::Round($endingDefrag, 1)
InitialTopFragmentation = [math]::Round($startingTopFrag, 1)
FinalTopFragmentation = [math]::Round($endingTopDefrag, 1)
Notes = "Database shrinks can cause massive index fragmentation and negatively impact performance. You should now run DBCC INDEXDEFRAG or ALTER INDEX ... REORGANIZE"
}
if ($ExcludeIndexStats) {
Select-DefaultView -InputObject $object -ExcludeProperty InitialAverageFragmentation, FinalAverageFragmentation, InitialTopFragmentation, FinalTopFragmentation
} else {
$object
}
}
}
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Invoke-DbaDatabaseShrink
}
}
function Invoke-DbaDbUpgrade {
<#
.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 target SQL Server instance or 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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaDbUpgrade
.EXAMPLE
PS C:\> Invoke-DbaDbUpgrade -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
PS C:\> Invoke-DbaDbUpgrade -SqlInstance PRD-SQL-INT01 -Database Test -NoRefreshView
Runs the upgrade command skipping the sp_refreshview update on all views
.EXAMPLE
PS C:\> Invoke-DbaDbUpgrade -SqlInstance PRD-SQL-INT01 -Database Test -Force
If database Test is already at the correct compatibility, runs every necessary step
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2016 | Out-GridView -Passthru | Invoke-DbaDbUpgrade
Get only specific databases using GridView and pass those to Invoke-DbaDbUpgrade
#>
[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 {
$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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Invoke-DbaDatabaseUpgrade
}
}
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 instance or instances. 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 ExcludeQuery
The Queries 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 ExcludeQueryTextColumn
Use this switch to exclude the [Complete Query Text] column from relevant queries
.PARAMETER ExcludePlanColumn
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 ExcludeQueryTextColumn 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: Andre Kamman (@AndreKamman), http://clouddba.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaDiagnosticQuery
.EXAMPLE
PS C:\>Invoke-DbaDiagnosticQuery -SqlInstance sql2016
Run the selection made by the user on the Sql Server instance specified.
.EXAMPLE
PS C:\>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
PS C:\> Invoke-DbaDiagnosticQuery -sqlinstance localhost -ExportQueries -outputpath "C:\temp\DiagnosticQueries"
Export All Queries to Disk
.EXAMPLE
PS C:\> Invoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName 'tempdb' -ExportQueries -outputpath "C:\temp\DiagnosticQueries"
Export Database Specific Queries for all User Dbs
.EXAMPLE
PS C:\> Invoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName 'tempdb' -ExportQueries -outputpath "C:\temp\DiagnosticQueries"
Export Database Specific Queries For One Target Database
.EXAMPLE
PS C:\> Invoke-DbaDiagnosticQuery -sqlinstance localhost -DatabaseSpecific -DatabaseName 'tempdb' -ExportQueries -outputpath "C:\temp\DiagnosticQueries" -queryname 'Database-scoped Configurations'
Export Database Specific Queries For One Target Database and One Specific Query
.EXAMPLE
PS C:\> Invoke-DbaDiagnosticQuery -sqlinstance localhost -UseSelectionHelper
Choose Queries To Export
.EXAMPLE
PS C:\> [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
PS C:\> $results = Invoke-DbaDiagnosticQuery -SqlInstance Sql2017 -DatabaseSpecific -queryname 'Database-scoped Configurations' -databasename TestStuff
Run diagnostic queries targeted at specific database, and only run database level queries against this database.
#>
[CmdletBinding(SupportsShouldProcess)]
[outputtype([pscustomobject[]])]
param (
[parameter(Mandatory, ValueFromPipeline, Position = 0)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias('DatabaseName')]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[object[]]$ExcludeQuery,
[Alias('Credential')]
[PSCredential]$SqlCredential,
[System.IO.FileInfo]$Path,
[string[]]$QueryName,
[switch]$UseSelectionHelper,
[switch]$InstanceOnly,
[switch]$DatabaseSpecific,
[Switch]$ExcludeQueryTextColumn,
[Switch]$ExcludePlanColumn,
[Switch]$NoColumnParsing,
[string]$OutputPath,
[switch]$ExportQueries,
[switch][Alias('Silent')]
[switch]$EnableException
)
begin {
$ProgressId = Get-Random
function Invoke-DiagnosticQuerySelectionHelper {
[CmdletBinding()]
param (
[parameter(Mandatory)]
$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 -ExcludeQueryTextColumn:$ExcludeQueryTextColumn -ExcludePlanColumn:$ExcludePlanColumn -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 {
$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 ($version -eq "2016" -and $server.VersionMinor -gt 5026 ) {
$version = "2016SP2"
}
if ($server.DatabaseEngineType -eq "SqlAzureDatabase") {
$version = "AzureSQLDatabase"
}
if (!$instanceOnly) {
if (-not $Database) {
$databases = (Get-DbaDatabase -SqlInstance $server -ExcludeSystem -ExcludeDatabase $ExcludeDatabase).Name
} else {
$databases = (Get-DbaDatabase -SqlInstance $server -ExcludeSystem -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
if ($QueryName.Count -eq 0) {
Write-Message -Level Output -Message "No query selected through SelectionHelper, halting script execution"
return
}
}
if ($QueryName.Count -eq 0) {
$QueryName = $parsedscript | Select-Object -ExpandProperty QueryName
}
if ($ExcludeQuery) {
$QueryName = Compare-Object -ReferenceObject $QueryName -DifferenceObject $ExcludeQuery | Where-Object SideIndicator -eq "<=" | Select-Object -ExpandProperty InputObject
}
#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) {
# ensure results are null with each part, otherwise duplicated information may be returned
$result = $null
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 (-not $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 (-not $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
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaPfRelog
.EXAMPLE
PS C:\> Invoke-DbaPfRelog -Path C:\temp\perfmon.blg
Creates C:\temp\perfmon.tsv from C:\temp\perfmon.blg.
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Invoke-DbaPfRelog -Path C:\temp\perfmon.blg -Destination C:\temp\a\b\c -Raw
```
[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.
```
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.
.EXAMPLE
PS C:\> 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
PS C:\> $servers | Get-DbaPfDataCollectorSet | Get-DbaPfDataCollector | Invoke-DbaPfRelog -Multithread -AllowClobber
Relogs latest data files from all collectors within the servers listed in $servers.
.EXAMPLE
PS C:\> 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-DbaQuery {
<#
.SYNOPSIS
A command to run explicit T-SQL commands or files.
.DESCRIPTION
This function is a wrapper command around Invoke-DbaAsync, 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
The target SQL Server instance or instances. 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: Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaQuery
.EXAMPLE
PS C:\> Invoke-DbaQuery -SqlInstance server\instance -Query 'SELECT foo FROM bar'
Runs the sql query 'SELECT foo FROM bar' against the instance 'server\instance'
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance [SERVERNAME] -Group [GROUPNAME] | Invoke-DbaQuery -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
PS C:\> "server1", "server1\nordwind", "server2" | Invoke-DbaQuery -File "C:\scripts\sql\rebuild.sql"
Runs the sql commands stored in rebuild.sql against the instances "server1", "server1\nordwind" and "server2"
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance "server1", "server1\nordwind", "server2" | Invoke-DbaQuery -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)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstance[]]$SqlInstance,
[Alias("Credential")]
[PsCredential]$SqlCredential,
[string]$Database,
[Parameter(Mandatory, Position = 0, ParameterSetName = "Query")]
[string]$Query,
[Int32]$QueryTimeout = 600,
[Parameter(Mandatory, ParameterSetName = "File")]
[Alias("InputFile")]
[object[]]$File,
[Parameter(Mandatory, 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)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[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 {
try {
Invoke-TlsWebRequest -Uri $item -OutFile $tempfile -ErrorAction Stop
} catch {
(New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
Invoke-TlsWebRequest -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-DbaAsync -SQLConnection $conncontext @splatInvokeDbaSqlAsync -Query $QueryfromFile
}
} else { Invoke-DbaAsync -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-DbaAsync -SQLConnection $conncontext @splatInvokeDbaSqlAsync -Query $QueryfromFile
}
} else {
Invoke-DbaAsync -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-DbaCmd
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Invoke-DbaSqlQuery
}
}
function Invoke-DbatoolsFormatter {
<#
.SYNOPSIS
Helps formatting function files to dbatools' standards
.DESCRIPTION
Uses PSSA's Invoke-Formatter to format the target files and saves it without the BOM.
.PARAMETER Path
The path to the ps1 file that needs to be formatted
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Formatting
Author: Simone Bizzotto
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbatoolsFormatter
.EXAMPLE
PS C:\> Invoke-DbatoolsFormatter -Path C:\dbatools\functions\Get-DbaDatabase.ps1
Reformats C:\dbatools\functions\Get-DbaDatabase.ps1 to dbatools' standards
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[object[]]$Path,
[switch]$EnableException
)
begin {
$HasInvokeFormatter = $null -ne (Get-Command Invoke-Formatter -ErrorAction SilentlyContinue).Version
if (!($HasInvokeFormatter)) {
Stop-Function -Message "You need a recent version of PSScriptAnalyzer installed"
}
$CBHRex = [regex]'(?smi)\s+<#[^#]*#>'
$CBHStartRex = [regex]'(?<spaces>[ ]+)<#'
$CBHEndRex = [regex]'(?<spaces>[ ]*)#>'
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($p in $Path) {
try {
$realPath = (Resolve-Path -Path $p -ErrorAction Stop).Path
} catch {
Stop-Function -Message "Cannot find or resolve $p" -Continue
}
$content = Get-Content -Path $realPath -Raw -Encoding UTF8
#strip ending empty lines
$content = $content -replace "(?s)`r`n\s*$"
try {
$content = Invoke-Formatter -ScriptDefinition $content -Settings CodeFormattingOTBS -ErrorAction Stop
} catch {
Write-Message -Level Warning "Unable to format $p"
}
#match the ending indentation of CBH with the starting one, see #4373
$CBH = $CBHRex.Match($content).Value
if ($CBH) {
#get starting spaces
$startSpaces = $CBHStartRex.Match($CBH).Groups['spaces']
if ($startSpaces) {
#get end
$newCBH = $CBHEndRex.Replace($CBH, "$startSpaces#>")
if ($newCBH) {
#replace the CBH
$content = $content.Replace($CBH, $newCBH)
}
}
}
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
$realContent = @()
#trim whitespace lines
foreach ($line in $content.Split("`n")) {
$realContent += $line.TrimEnd()
}
[System.IO.File]::WriteAllText($realPath, ($realContent -Join "`r`n"), $Utf8NoBomEncoding)
}
}
}
function Invoke-DbatoolsRenameHelper {
<#
.SYNOPSIS
Older dbatools command names have been changed. This script helps keep up.
.DESCRIPTION
Older dbatools command names have been changed. This script helps keep up.
.PARAMETER InputObject
A piped in object from Get-ChildItem
.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 EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: Module
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbatoolsRenameHelper
.EXAMPLE
PS C:\> Get-ChildItem C:\temp\ps\*.ps1 -Recurse | Invoke-DbatoolsRenameHelper
Checks to see if any ps1 file in C:\temp\ps matches an old command name.
If so, then the command name within the text is updated and the resulting changes are written to disk in UTF-8.
.EXAMPLE
PS C:\> Get-ChildItem C:\temp\ps\*.ps1 -Recurse | Invoke-DbatoolsRenameHelper -Encoding Ascii -WhatIf
Shows what would happen if the command would run. If the command would run and there were matches,
the resulting changes would be written to disk as Ascii encoded.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[System.IO.FileInfo[]]$InputObject,
[ValidateSet('ASCII', 'BigEndianUnicode', 'Byte', 'String', 'Unicode', 'UTF7', 'UTF8', 'Unknown')]
[string]$Encoding = 'UTF8',
[switch]$EnableException
)
begin {
$morerenames = @(
@{
"AliasName" = "Invoke-Sqlcmd2"
"Definition" = "Invoke-DbaQuery"
},
@{
"AliasName" = "UseLastBackups"
"Definition" = "UseLastBackup"
},
@{
"AliasName" = "NetworkShare"
"Definition" = "SharedPath"
},
@{
"AliasName" = "NoSystemLogins"
"Definition" = "ExcludeSystemLogins"
},
@{
"AliasName" = "NoJobSteps"
"Definition" = "ExcludeJobSteps"
},
@{
"AliasName" = "NoSystemObjects"
"Definition" = "ExcludeSystemObjects"
},
@{
"AliasName" = "NoJobs"
"Definition" = "ExcludeJobs"
},
@{
"AliasName" = "NoDatabases"
"Definition" = "ExcludeDatabases"
},
@{
"AliasName" = "NoDisabledJobs"
"Definition" = "ExcludeDisabledJobs"
},
@{
"AliasName" = "NoJobSteps"
"Definition" = "ExcludeJobSteps"
},
@{
"AliasName" = "NoSystem"
"Definition" = "ExcludeSystemLogins"
},
@{
"AliasName" = "NoSystemDb"
"Definition" = "ExcludeSystem"
},
@{
"AliasName" = "NoSystemObjects"
"Definition" = "ExcludeSystemObjects"
},
@{
"AliasName" = "NoSystemSpid"
"Definition" = "ExcludeSystemSpids"
},
@{
"AliasName" = "NoQueryTextColumn"
"Definition" = "ExcludeQueryTextColumn"
},
@{
"AliasName" = "ExcludeAllSystemDb"
"Definition" = "ExcludeSystem"
},
@{
"AliasName" = "ExcludeAllUserDb"
"Definition" = "ExcludeUser"
}
)
$allrenames = $script:renames + $morerenames
}
process {
foreach ($fileobject in $InputObject) {
$file = $fileobject.FullName
foreach ($name in $allrenames) {
if ((Select-String -Pattern $name.AliasName -Path $file)) {
if ($Pscmdlet.ShouldProcess($file, "Replacing $($name.AliasName) with $($name.Definition)")) {
$content = (Get-Content -Path $file -Raw).Replace($name.AliasName, $name.Definition).Trim()
Set-Content -Path $file -Encoding $Encoding -Value $content
[pscustomobject]@{
Path = $file
Pattern = $name.AliasName
ReplacedWith = $name.Definition
}
}
}
}
}
}
}
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 target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaWhoIsActive
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Invoke-DbaWhoIsActive -SqlInstance sqlserver2014a -GetAverageTime
Similar to running sp_WhoIsActive @get_avg_time
.EXAMPLE
PS C:\> Invoke-DbaWhoIsActive -SqlInstance sqlserver2014a -GetOuterCommand -FindBlockLeaders
Similar to running sp_WhoIsActive @get_outer_command = 1, @find_block_leaders = 1
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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 {
$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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Invoke-DbaXEReplay
.EXAMPLE
PS C:\> Read-DbaXEFile -Path C:\temp\sample.xel | Invoke-DbaXeReplay -SqlInstance sql2017
Runs all batch_text for sql_batch_completed against tempdb on sql2017.
.EXAMPLE
PS C:\> 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
PS C:\> 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.
#>
[Cmdletbinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
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 {
#Variable marked as unused by PSScriptAnalyzer
#$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 {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance -Continue
}
if ($Raw) {
Write-Message -Message "Invoking XEReplay against $instance running on $($server.name) with raw output" -Level Verbose
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
}
}
Write-Message -Message "Invoking XEReplay against $instance running on $($server.name)" -Level Verbose
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,
ValueFromPipeline,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false,
HelpMessage = 'SQL Server Instance required...')]
[Parameter(ParameterSetName = 'Ins-Fil',
Position = 0,
Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false,
HelpMessage = 'SQL Server Instance required...')]
[Alias('Instance', 'Instances', 'ComputerName', 'Server', 'Servers', 'SqlInstance')]
[ValidateNotNullOrEmpty()]
[string[]]$ServerInstance,
[Parameter(Position = 1,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[string]$Database,
[Parameter(ParameterSetName = 'Ins-Que',
Position = 2,
Mandatory,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[Parameter(ParameterSetName = 'Con-Que',
Position = 2,
Mandatory,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[string]$Query,
[Parameter(ParameterSetName = 'Ins-Fil',
Position = 2,
Mandatory,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[Parameter(ParameterSetName = 'Con-Fil',
Position = 2,
Mandatory,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[ValidateScript( { Test-Path -LiteralPath $_ })]
[string]$InputFile,
[Parameter(ParameterSetName = 'Ins-Que',
Position = 3,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[Parameter(ParameterSetName = 'Ins-Fil',
Position = 3,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[Alias('SqlCredential')]
[System.Management.Automation.PSCredential]$Credential,
[Parameter(ParameterSetName = 'Ins-Que',
Position = 4,
ValueFromRemainingArguments = $false)]
[Parameter(ParameterSetName = 'Ins-Fil',
Position = 4,
ValueFromRemainingArguments = $false)]
[switch]$Encrypt,
[Parameter(Position = 5,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[Int32]$QueryTimeout = 600,
[Parameter(ParameterSetName = 'Ins-Fil',
Position = 6,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[Parameter(ParameterSetName = 'Ins-Que',
Position = 6,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[Int32]$ConnectionTimeout = 15,
[Parameter(Position = 7,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[ValidateSet("DataSet", "DataTable", "DataRow", "PSObject", "SingleValue")]
[string]$As = "DataRow",
[Parameter(Position = 8,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[System.Collections.IDictionary]$SqlParameters,
[Parameter(Position = 9)]
[switch]$AppendServerInstance,
[Parameter(Position = 10)]
[switch]$ParseGO,
[Parameter(ParameterSetName = 'Con-Que',
Position = 11,
ValueFromPipeline = $false,
ValueFromPipelineByPropertyName = $false,
ValueFromRemainingArguments = $false)]
[Parameter(ParameterSetName = 'Con-Fil',
Position = 11,
ValueFromPipeline = $false,
ValueFromPipelineByPropertyName = $false,
ValueFromRemainingArguments = $false)]
[Alias('Connection', 'Conn')]
[ValidateNotNullOrEmpty()]
[System.Data.SqlClient.SQLConnection]$SQLConnection
)
process {
Write-Message -Level Warning -Message "This command is no longer supported. Please use Invoke-DbaQuery instead."
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Join-DbaAvailabilityGroup {
<#
.SYNOPSIS
Joins a secondary replica to an availability group on a SQL Server instance.
.DESCRIPTION
Joins a secondary replica to an availability group on a SQL Server instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER AvailabilityGroup
The availability group to join.
.PARAMETER ClusterType
Cluster type of the Availability Group. Only supported in SQL Server 2017 and above.
Options include: External, Wsfc or None.
.PARAMETER InputObject
Enables piped input from Get-DbaAvailabilityGroup.
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Join-DbaAvailabilityGroup
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql01 -AvailabilityGroup SharePoint | Join-DbaAvailabilityGroup -SqlInstance sql02
Joins sql02 to the SharePoint availability group on sql01
.EXAMPLE
PS C:\> $ag = Get-DbaAvailabilityGroup -SqlInstance sql01 -AvailabilityGroup SharePoint
PS C:\> Join-DbaAvailabilityGroup -SqlInstance sql02 -InputObject $ag
Joins sql02 to the SharePoint availability group on sql01
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql01 -AvailabilityGroup SharePoint | Join-DbaAvailabilityGroup -SqlInstance sql02 -WhatIf
Shows what would happen if the command were to run. No actions are actually performed.
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql01 -AvailabilityGroup SharePoint | Join-DbaAvailabilityGroup -SqlInstance sql02 -Confirm
Prompts for confirmation then joins sql02 to the SharePoint availability group on sql01.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[ValidateSet('External', 'Wsfc', 'None')]
[string]$ClusterType,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup[]]$InputObject,
[switch]$EnableException
)
process {
if ($InputObject) {
$AvailabilityGroup += $InputObject.Name
if (-not $ClusterType) {
$tempclustertype = ($InputObject | Select-Object -First 1).ClusterType
if ($tempclustertype) {
$ClusterType = $tempclustertype
}
}
}
if (-not $AvailabilityGroup) {
Stop-Function -Message "No availability group to add"
return
}
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
foreach ($ag in $AvailabilityGroup) {
if ($Pscmdlet.ShouldProcess($server.Name, "Joining $ag")) {
try {
if ($ClusterType -and $server.VersionMajor -ge 14) {
$server.Query("ALTER AVAILABILITY GROUP [$ag] JOIN WITH (CLUSTER_TYPE = $ClusterType)")
} else {
$server.JoinAvailabilityGroup($ag)
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
}
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
AvgThroughput : 1.07 MB
AvgSize : 24.17
AvgDuration : 00:00:01.1000000
MinThroughput : 0.02 MB
MaxThroughput : 2.26 MB
MinBackupDate : 8/6/2015 10:22:01 PM
MaxBackupDate : 6/19/2016 12:57:45 PM
BackupCount : 10
.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. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Measure-DbaBackupThroughput
.EXAMPLE
PS C:\> Measure-DbaBackupThroughput -SqlInstance sql2016
Parses every backup in msdb's backuphistory for stats on all databases.
.EXAMPLE
PS C:\> Measure-DbaBackupThroughput -SqlInstance sql2016 -Database AdventureWorks2014
Parses every backup in msdb's backuphistory for stats on AdventureWorks2014.
.EXAMPLE
PS C:\> Measure-DbaBackupThroughput -SqlInstance sql2005 -Last
Processes the last full, diff and log backups every backup for all databases on sql2005.
.EXAMPLE
PS C:\> Measure-DbaBackupThroughput -SqlInstance sql2005 -Last -Type Log
Processes the last log backups every backup for all databases on sql2005.
.EXAMPLE
PS C:\> Measure-DbaBackupThroughput -SqlInstance sql2016 -Since (Get-Date).AddDays(-7) | Where-Object { $_.MinThroughput.Gigabyte -gt 1 }
Gets backup calculations for the last week and filters results that have a minimum of 1GB throughput
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[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 {
$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
AvgThroughput = [dbasize]([System.Math]::Round($measuremb.Average, 2) * 1024 * 1024)
AvgSize = [dbasize]([System.Math]::Round($measuresize.Average, 2) * 1024 * 1024)
AvgDuration = [dbatimespan](New-TimeSpan -Seconds $avgduration.Average)
MinThroughput = [dbasize]([System.Math]::Round($measuremb.Minimum, 2) * 1024 * 1024)
MaxThroughput = [dbasize]([System.Math]::Round($measuremb.Maximum, 2) * 1024 * 1024)
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Measure-DbaDiskSpaceRequirement
.EXAMPLE
PS C:\> 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
PS C:\> @(
>> [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
PS C:\> 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
PS C:\> $qry = "SELECT Source, Destination, Database FROM dbo.Migrations"
PS C:\> Invoke-DbaCmd -SqlInstance DBA -Database Migrations -Query $qry | Measure-DbaDiskSpaceRequirement
Using a SQL table. We are DBA after all!
#>
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param(
[Parameter(Mandatory, ValueFromPipelineByPropertyName = $true)]
[DbaInstanceParameter]$Source,
[Parameter(Mandatory, ValueFromPipelineByPropertyName = $true)]
[string]$Database,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[PSCredential]$SourceSqlCredential,
[Parameter(Mandatory, ValueFromPipelineByPropertyName = $true)]
[DbaInstanceParameter]$Destination,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[string]$DestinationDatabase,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[PSCredential]$DestinationSqlCredential,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[PSCredential]$Credential,
[Alias('Silent')]
[switch]$EnableException
)
begin {
$local:cacheMP = @{}
$local:cacheDP = @{}
function Get-MountPoint {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
$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)]
$path,
[Parameter(Mandatory)]
$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)]
[ValidateSet('Log', 'Data')]
$DefaultPathType,
[Parameter(Mandatory)]
$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 {
try {
$sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
}
try {
$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 Measure-DbatoolsImport {
<#
.SYNOPSIS
Displays the import load times of the dbatools PowerShell module
.DESCRIPTION
Displays the import load times of the dbatools PowerShell module
.NOTES
Tags: Debug
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Measure-DbatoolsImport
.EXAMPLE
PS C:\> Measure-DbatoolsImport
Displays the import load times of the dbatools PowerShell module
.EXAMPLE
PS C:\> Import-Module dbatools
PS C:\> Measure-DbatoolsImport
Displays the import load times of the dbatools PowerShell module
#>
[Sqlcollaborative.Dbatools.dbaSystem.DebugHost]::ImportTime
}
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 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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Mount-DbaDatabase
.EXAMPLE
PS C:\> $fileStructure = New-Object System.Collections.Specialized.StringCollection
PS C:\> $fileStructure.Add("E:\archive\example.mdf")
PS C:\> $filestructure.Add("E:\archive\example.ldf")
PS C:\> $filestructure.Add("E:\archive\example.ndf")
PS C:\> 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
PS C:\> 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
PS C:\> 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-Dbapath -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-DbaCmsRegServer {
<#
.SYNOPSIS
Moves registered servers around SQL Server Central Management Server (CMS)
.DESCRIPTION
Moves registered servers around SQL Server Central Management Server (CMS)
.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 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-DbaCmsRegServer 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
Tags: RegisteredServer, CMS
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Move-DbaCmsRegServer
.EXAMPLE
PS C:\> Move-DbaCmsRegServer -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
PS C:\> Get-DbaCmsRegServer -SqlInstance sql2017 -Name 'Web SQL Cluster' | Move-DbaCmsRegServer -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-DbaCmsRegServer -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-DbaCmsRegServerGroup -SqlInstance $server -Group $NewGroup
if (-not $group) {
Stop-Function -Message "$NewGroup not found on $server" -Continue
}
} else {
$group = Get-DbaCmsRegServerGroup -SqlInstance $server -Id 1
}
if ($Pscmdlet.ShouldProcess($regserver.SqlInstance, "Moving $($regserver.Name) to $group")) {
try {
$null = $parentserver.ServerConnection.ExecuteNonQuery($regserver.ScriptMove($group).GetScript())
Get-DbaCmsRegServer -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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Move-DbaRegisteredServer
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Move-DbaCmsRegServerGroup {
<#
.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
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 Group
Specifies one or more groups to include from SQL Server Central Management Server.
.PARAMETER NewGroup
The new location.
.PARAMETER InputObject
Allows results from Get-DbaCmsRegServerGroup 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
Tags: RegisteredServer, CMS
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Move-DbaCmsRegServerGroup
.EXAMPLE
PS C:\> Move-DbaCmsRegServerGroup -SqlInstance sql2012 -Group HR\Development -NewGroup AD\Prod
Moves the Development group within HR to the Prod group within AD
.EXAMPLE
PS C:\> Get-DbaCmsRegServerGroup -SqlInstance sql2017 -Group HR\Development| Move-DbaCmsRegServer -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) {
$InputObject += Get-DbaCmsRegServerGroup -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-DbaCmsRegServerGroup -SqlInstance $server -Id 1
} else {
$groupobject = Get-DbaCmsRegServerGroup -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())
Get-DbaCmsRegServerGroup -SqlInstance $server -Group $newname
$parentserver.ServerConnection.Disconnect()
} catch {
Stop-Function -Message "Failed to move $($regserver.Name) to $NewGroup on $($regserver.SqlInstance)" -ErrorRecord $_ -Continue
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Move-DbaRegisteredServerGroup
}
}
#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
The target SQL Server instance or instances. 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 can 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 can 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 can 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 can 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 can 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
Tags: Agent, Job, JobStep
Author: Sander Stad (@sqlstad), sqlstad.nl
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaAgentJob
.EXAMPLE
PS C:\> New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -Description 'Just another job'
Creates a job with the name "Job1" and a small description
.EXAMPLE
PS C:\> New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -Disabled
Creates the job but sets it to disabled
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> New-DbaAgentJob -SqlInstance sql1 -Job 'Job One' -Description 'Just another job' -Whatif
Doesn't create the job but shows what would happen.
.EXAMPLE
PS C:\> New-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job 'Job One'
Creates a job with the name "Job One" on multiple servers
.EXAMPLE
PS C:\> "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, ConfirmImpact = "Low")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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
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
The target SQL Server instance or instances. 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
Tags: Agent, Job, JobCategory
Author: Sander Stad (@sqlstad), sqlstad.nl
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaAgentJobCategory
.EXAMPLE
PS C:\> New-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 1'
Creates a new job category with the name 'Category 1'.
.EXAMPLE
PS C:\> 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, ConfirmImpact = "Low")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Parameter(Mandatory)]
[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
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
The target SQL Server instance or instances. 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 SubSystemServer
The subsystems AnalysisScripting, AnalysisCommand, AnalysisQuery ned the server property to be able to apply
.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 "GoToStep".
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaAgentJobStep
.EXAMPLE
PS C:\> New-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1
Create a step in "Job1" with the name Step1 with the default subsystem TransactSql.
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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, ConfirmImpact = "Low")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[object[]]$Job,
[int]$StepId,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$StepName,
[ValidateSet('ActiveScripting', 'AnalysisCommand', 'AnalysisQuery', 'CmdExec', 'Distribution', 'LogReader', 'Merge', 'PowerShell', 'QueueReader', 'Snapshot', 'Ssis', 'TransactSql')]
[string]$Subsystem = 'TransactSql',
[string]$SubsystemServer,
[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 = 0,
[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 -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 fail 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
}
if ($Subsystem -in 'AnalysisScripting', 'AnalysisCommand', 'AnalysisQuery') {
if (-not $SubsystemServer) {
Stop-Function -Message "Please enter the server value using -SubSystemServer for subsystem $Subsystem." -Target $Subsystem
return
}
}
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($instance in $sqlinstance) {
# Try connecting to the instance
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" -Level 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 -SqlCredential $SqlCredential
# 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) -and $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[$j].JobSteps | Where-Object {$_.ID -eq 1}).Name
Remove-DbaAgentJobStep -SqlInstance $instance -Job $currentjob -StepName $StepName -SqlCredential $SqlCredential
# 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 ($SubsystemServer) {
Write-Message -Message "Setting job step subsystem server to $SubsystemServer" -Level Verbose
$JobStep.Server = $SubsystemServer
}
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 target SQL Server instance or instances.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 ProxyCredential
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaAgentProxy
.EXAMPLE
PS C:\> New-DbaAgentProxy -SqlInstance sql2016 -Name STIG -ProxyCredential '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
PS C:\> New-DbaAgentProxy -SqlInstance localhost\sql2016 -Name STIG -ProxyCredential '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')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "", Justification = "For Parameter ProxyCredential")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(Mandatory)]
[string[]]$Name,
[parameter(Mandatory)]
[string[]]$ProxyCredential,
[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) {
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[$ProxyCredential]) {
Write-Message -Level Warning -Message "Credential '$ProxyCredential' does not exist on $instance"
continue
}
if ($Pscmdlet.ShouldProcess($instance, "Adding $proxyname with the $ProxyCredential credential")) {
# the new-object is stubborn and $true/$false has to be forced in
switch (Test-Bound $disabled) {
$false {
$proxy = New-Object Microsoft.SqlServer.Management.Smo.Agent.ProxyAccount -ArgumentList $jobServer, $ProxyName, $ProxyCredential, $true, $Description
}
$true {
$proxy = New-Object Microsoft.SqlServer.Management.Smo.Agent.ProxyAccount -ArgumentList $jobServer, $ProxyName, $ProxyCredential, $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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaAgentSchedule
.EXAMPLE
PS C:\> 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
PS C:\> 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, ConfirmImpact = "Low")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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
}
#Format dates and times
if ($StartDate) {
$StartDate = $StartDate.Insert(6, '-').Insert(4, '-')
}
if ($EndDate) {
$EndDate = $EndDate.Insert(6, '-').Insert(4, '-')
}
if ($StartTime) {
$StartTime = $StartTime.Insert(4, ':').Insert(2, ':')
}
if ($EndTime) {
$EndTime = $EndTime.Insert(4, ':').Insert(2, ':')
}
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($instance in $sqlinstance) {
# Try connecting to the instance
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) {
Write-Message -Message "Setting job schedule start date to $StartDate" -Level Verbose
$JobSchedule.ActiveStartDate = $StartDate
}
if ($EndDate) {
Write-Message -Message "Setting job schedule end date to $EndDate" -Level Verbose
$JobSchedule.ActiveEndDate = $EndDate
}
if ($StartTime) {
Write-Message -Message "Setting job schedule start time to $StartTime" -Level Verbose
$JobSchedule.ActiveStartTimeOfDay = $StartTime
}
if ($EndTime) {
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) {
Write-Message -Message "Setting job schedule start date to $StartDate" -Level Verbose
$JobSchedule.ActiveStartDate = $StartDate
}
if ($EndDate) {
Write-Message -Message "Setting job schedule end date to $EndDate" -Level Verbose
$JobSchedule.ActiveEndDate = $EndDate
}
if ($StartTime) {
Write-Message -Message "Setting job schedule start time to $StartTime" -Level Verbose
$JobSchedule.ActiveStartTimeOfDay = $StartTime
}
if ($EndTime) {
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
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaAvailabilityGroup {
<#
.SYNOPSIS
Automates the creation of availability groups.
.DESCRIPTION
Automates the creation of availability groups.
* Checks prerequisites
* Creates Availability Group and adds primary replica
* Grants cluster permissions if necessary
* Adds secondary replica if supplied
* Adds databases if supplied
* Performs backup/restore if seeding mode is manual
* Performs backup to NUL if seeding mode is automatic
* Adds listener to primary if supplied
* Joins secondaries to availability group
* Grants endpoint connect permissions to service accounts
* Grants CreateAnyDatabase permissions if seeding mode is automatic
* Returns Availability Group object from primary
NOTE: If a backup / restore is performed, the backups will be left intact on the network share.
Thanks for this, Thomas Stringer! https://blogs.technet.microsoft.com/heyscriptingguy/2013/04/29/set-up-an-alwayson-availability-group-with-powershell/
.PARAMETER Primary
The primary SQL Server instance. Server version must be SQL Server version 2012 or higher.
.PARAMETER PrimarySqlCredential
Login to the primary instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Secondary
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SecondarySqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Name
The name of the Availability Group.
.PARAMETER DtcSupport
Indicates whether the DtcSupport is enabled
.PARAMETER ClusterType
Cluster type of the Availability Group. Only supported in SQL Server 2017 and above.
Options include: External, Wsfc or None. None by default.
.PARAMETER AutomatedBackupPreference
Specifies how replicas in the primary role are treated in the evaluation to pick the desired replica to perform a backup.
.PARAMETER FailureConditionLevel
Specifies the different conditions that can trigger an automatic failover in Availability Group.
.PARAMETER HealthCheckTimeout
This setting used to specify the length of time, in milliseconds, that the SQL Server resource DLL should wait for information returned by the sp_server_diagnostics stored procedure before reporting the Always On Failover Cluster Instance (FCI) as unresponsive.
Changes that are made to the timeout settings are effective immediately and do not require a restart of the SQL Server resource.
Defaults to 30000 (30 seconds).
.PARAMETER Basic
Indicates whether the availability group is basic. Basic availability groups like pumpkin spice and uggs.
https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/basic-availability-groups-always-on-availability-groups
.PARAMETER DatabaseHealthTrigger
Indicates whether the availability group triggers the database health.
.PARAMETER Passthru
Don't create the availability group, just pass thru an object that can be further customized before creation.
.PARAMETER Database
The database or databases to add.
.PARAMETER SharedPath
The network share where the backups will be backed up and restored from.
Each SQL Server service account must have access to this share.
NOTE: If a backup / restore is performed, the backups will be left in tact on the network share.
.PARAMETER UseLastBackup
Use the last full backup of database.
.PARAMETER Force
Drop and recreate the database on remote servers using fresh backup.
.PARAMETER AvailabilityMode
Sets the availability mode of the availability group replica. Options are: AsynchronousCommit and SynchronousCommit. SynchronousCommit is default.
.PARAMETER FailoverMode
Sets the failover mode of the availability group replica. Options are Automatic, Manual and External. Automatic is default.
.PARAMETER BackupPriority
Sets the backup priority availability group replica. Default is 50.
.PARAMETER Endpoint
By default, this command will attempt to find a DatabaseMirror endpoint. If one does not exist, it will create it.
If an endpoint must be created, the name "hadr_endpoint" will be used. If an alternative is preferred, use Endpoint.
.PARAMETER ConnectionModeInPrimaryRole
Specifies the connection intent modes of an Availability Replica in primary role. AllowAllConnections by default.
.PARAMETER ConnectionModeInSecondaryRole
Specifies the connection modes of an Availability Replica in secondary role. AllowAllConnections by default.
.PARAMETER ReadonlyRoutingConnectionUrl
Sets the read only routing connection url for the availability replica.
.PARAMETER SeedingMode
Specifies how the secondary replica will be initially seeded.
Automatic enables direct seeding. This method will seed the secondary replica over the network. This method does not require you to backup and restore a copy of the primary database on the replica.
Manual requires you to create a backup of the database on the primary replica and manually restore that backup on the secondary replica.
.PARAMETER Certificate
Specifies that the endpoint is to authenticate the connection using the certificate specified by certificate_name to establish identity for authorization.
The far endpoint must have a certificate with the public key matching the private key of the specified certificate.
.PARAMETER IPAddress
Sets the IP address of the availability group listener.
.PARAMETER SubnetMask
Sets the subnet IP mask of the availability group listener.
.PARAMETER Port
Sets the number of the port used to communicate with the availability group.
.PARAMETER Dhcp
Indicates whether the object is DHCP.
.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: HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaAvailabilityGroup
.EXAMPLE
PS C:\> New-DbaAvailabilityGroup -Primary sql2016a -Name SharePoint
Creates a new availability group on sql2016a named SharePoint
.EXAMPLE
PS C:\> New-DbaAvailabilityGroup -Primary sql2016a -Name SharePoint -Secondary sql2016b
Creates a new availability group on sql2016b named SharePoint with a secondary replica, sql2016b
.EXAMPLE
PS C:\> New-DbaAvailabilityGroup -Primary sql2016std -Name BAG1 -Basic -Confirm:$false
Creates a basic availability group named BAG1 on sql2016std and does not confirm when setting up
.EXAMPLE
PS C:\> New-DbaAvailabilityGroup -Primary sql2016b -Name AG1 -ClusterType Wsfc -Dhcp -Database db1 -UseLastBackup
Creates an availability group on sql2016b with the name ag1. Uses the last backups available to add the database db1 to the AG.
.EXAMPLE
PS C:\> New-DbaAvailabilityGroup -Primary sql2017 -Name SharePoint -ClusterType None -FailoverMode Manual
Creates a new availability group on sql2017 named SharePoint with a cluster type of none and a failover mode of manual
.EXAMPLE
PS C:\> New-DbaAvailabilityGroup -Primary sql1 -Secondary sql2 -Name ag1 -Database pubs -ClusterType None -SeedingMode Automatic -FailoverMode Manual
Creates a new availability group with a primary replica on sql1 and a secondary on sql2. Automatically adds the database pubs.
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> $params = @{
>> Primary = "sql1"
>> PrimarySqlCredential = $cred
>> Secondary = "sql2"
>> SecondarySqlCredential = $cred
>> Name = "test-ag"
>> Database = "pubs"
>> ClusterType = "None"
>> SeedingMode = "Automatic"
>> FailoverMode = "Manual"
>> Confirm = $false
>> }
PS C:\> New-DbaAvailabilityGroup @params
This exact command was used to create an availability group on docker!
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[parameter(ValueFromPipeline)]
[DbaInstanceParameter]$Primary,
[PSCredential]$PrimarySqlCredential,
[DbaInstanceParameter[]]$Secondary,
[PSCredential]$SecondarySqlCredential,
# AG
[parameter(Mandatory)]
[string]$Name,
[switch]$DtcSupport,
[ValidateSet('External', 'Wsfc', 'None')]
[string]$ClusterType = 'External',
[ValidateSet('None', 'Primary', 'Secondary', 'SecondaryOnly')]
[string]$AutomatedBackupPreference = 'Secondary',
[ValidateSet('OnAnyQualifiedFailureCondition', 'OnCriticalServerErrors', 'OnModerateServerErrors', 'OnServerDown', 'OnServerUnresponsive')]
[string]$FailureConditionLevel = "OnServerDown",
[int]$HealthCheckTimeout = 30000,
[switch]$Basic,
[switch]$DatabaseHealthTrigger,
[switch]$Passthru,
# database
[string[]]$Database,
[Alias("NetworkShare")]
[string]$SharedPath,
[switch]$UseLastBackup,
[switch]$Force,
# replica
[ValidateSet('AsynchronousCommit', 'SynchronousCommit')]
[string]$AvailabilityMode = "SynchronousCommit",
[ValidateSet('Automatic', 'Manual', 'External')]
[string]$FailoverMode = "Automatic",
[int]$BackupPriority = 50,
[ValidateSet('AllowAllConnections', 'AllowReadWriteConnections')]
[string]$ConnectionModeInPrimaryRole = 'AllowAllConnections',
[ValidateSet('AllowAllConnections', 'AllowNoConnections', 'AllowReadIntentConnectionsOnly')]
[string]$ConnectionModeInSecondaryRole = 'AllowAllConnections',
[ValidateSet('Automatic', 'Manual')]
[string]$SeedingMode = 'Manual',
[string]$Endpoint,
[string]$ReadonlyRoutingConnectionUrl,
[string]$Certificate,
# network
[ipaddress[]]$IPAddress,
[ipaddress]$SubnetMask = "255.255.255.0",
[int]$Port = 1433,
[switch]$Dhcp,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter NetworkShare -CustomMessage "Using the parameter NetworkShare is deprecated. This parameter will be removed in version 1.0.0 or before. Use SharedPath instead."
}
process {
$stepCounter = $wait = 0
if ($Force -and $Secondary -and (-not $SharedPath -and -not $UseLastBackup) -and ($SeedingMode -ne 'Automatic')) {
Stop-Function -Message "SharedPath or UseLastBackup is required when Force is used"
return
}
try {
$server = Connect-SqlInstance -SqlInstance $Primary -SqlCredential $PrimarySqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Primary
return
}
if ($SeedingMode -eq 'Automatic' -and $server.VersionMajor -lt 13) {
Stop-Function -Message "Automatic seeding mode only supported in SQL Server 2016 and above" -Target $Primary
return
}
if ($Basic -and $server.VersionMajor -lt 13) {
Stop-Function -Message "Basic availability groups are only supported in SQL Server 2016 and above" -Target $Primary
return
}
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Checking perquisites"
# Don't reuse $server here, it fails
if (Get-DbaAvailabilityGroup -SqlInstance $Primary -SqlCredential $PrimarySqlCredential -AvailabilityGroup $Name) {
Stop-Function -Message "Availability group named $Name already exists on $Primary"
return
}
if ($Certificate) {
$cert = Get-DbaDbCertificate -SqlInstance $Primary -SqlCredential $PrimarySqlCredential -Certificate $Certificate
if (-not $cert) {
Stop-Function -Message "Certificate $Certificate does not exist on $Primary" -ErrorRecord $_ -Target $Primary
return
}
}
if (($SharedPath)) {
if (-not (Test-DbaPath -SqlInstance $Primary -SqlCredential $PrimarySqlCredential -Path $SharedPath)) {
Stop-Function -Continue -Message "Cannot access $SharedPath from $Primary"
return
}
}
if ($Database -and -not $UseLastBackup -and -not $SharedPath -and $Secondary -and $SeedingMode -ne 'Automatic') {
Stop-Function -Continue -Message "You must specify a SharedPath when adding databases to a manually seeded availability group"
return
}
if ($server.HostPlatform -eq "Linux") {
# New to SQL Server 2017 (14.x) is the introduction of a cluster type for AGs. For Linux, there are two valid values: External and None.
if ($ClusterType -notin "External", "None") {
Stop-Function -Continue -Message "Linux only supports ClusterType of External or None"
return
}
# Microsoft Distributed Transaction Coordinator (DTC) is not supported under Linux in SQL Server 2017
if ($DtcSupport) {
Stop-Function -Continue -Message "Microsoft Distributed Transaction Coordinator (DTC) is not supported under Linux"
return
}
}
if ($ClusterType -eq "None" -and $server.VersionMajor -lt 14) {
Stop-Function -Message "ClusterType of None only supported in SQL Server 2017 and above"
return
}
if ($Secondary) {
$secondaries = @()
foreach ($computer in $Secondary) {
try {
$secondaries += Connect-SqlInstance -SqlInstance $computer -SqlCredential $SecondarySqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Primary
return
}
}
if ($SeedingMode -eq "Automatic") {
$primarypath = Get-DbaDefaultPath -SqlInstance $server
foreach ($second in $secondaries) {
$secondarypath = Get-DbaDefaultPath -SqlInstance $second
if ($primarypath.Data -ne $secondarypath.Data) {
Write-Message -Level Warning -Message "Primary and secondary ($second) default data paths do not match. Trying anyway."
}
if ($primarypath.Log -ne $secondarypath.Log) {
Write-Message -Level Warning -Message "Primary and secondary ($second) default log paths do not match. Trying anyway."
}
}
}
}
# database checks
if ($Database) {
$dbs += Get-DbaDatabase -SqlInstance $Primary -SqlCredential $PrimarySqlCredential -Database $Database
}
foreach ($primarydb in $dbs) {
if ($primarydb.MirroringStatus -ne "None") {
Stop-Function -Message "Cannot setup mirroring on database ($dbname) due to its current mirroring state: $($primarydb.MirroringStatus)"
return
}
if ($primarydb.Status -ne "Normal") {
Stop-Function -Message "Cannot setup mirroring on database ($dbname) due to its current state: $($primarydb.Status)"
return
}
if ($primarydb.RecoveryModel -ne "Full") {
if ((Test-Bound -ParameterName UseLastBackup)) {
Stop-Function -Message "$dbName not set to full recovery. UseLastBackup cannot be used."
return
} else {
Set-DbaDbRecoveryModel -SqlInstance $Primary -SqlCredential $PrimarySqlCredential -Database $primarydb.Name -RecoveryModel Full
}
}
}
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Creating availability group named $Name on $Primary"
# Start work
if ($Pscmdlet.ShouldProcess($Primary, "Setting up availability group named $Name and adding primary replica")) {
try {
$ag = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityGroup -ArgumentList $server, $Name
$ag.AutomatedBackupPreference = [Microsoft.SqlServer.Management.Smo.AvailabilityGroupAutomatedBackupPreference]::$AutomatedBackupPreference
$ag.FailureConditionLevel = [Microsoft.SqlServer.Management.Smo.AvailabilityGroupFailureConditionLevel]::$FailureConditionLevel
$ag.HealthCheckTimeout = $HealthCheckTimeout
if ($server.VersionMajor -ge 13) {
$ag.BasicAvailabilityGroup = $Basic
$ag.DatabaseHealthTrigger = $DatabaseHealthTrigger
}
if ($server.VersionMajor -ge 14) {
$ag.ClusterType = $ClusterType
}
if ($PassThru) {
$defaults = 'LocalReplicaRole', 'Name as AvailabilityGroup', 'PrimaryReplicaServerName as PrimaryReplica', 'AutomatedBackupPreference', 'AvailabilityReplicas', 'AvailabilityDatabases', 'AvailabilityGroupListeners'
return (Select-DefaultView -InputObject $ag -Property $defaults)
}
$replicaparams = @{
InputObject = $ag
AvailabilityMode = $AvailabilityMode
FailoverMode = $FailoverMode
BackupPriority = $BackupPriority
ConnectionModeInPrimaryRole = $ConnectionModeInPrimaryRole
ConnectionModeInSecondaryRole = $ConnectionModeInSecondaryRole
Endpoint = $Endpoint
ReadonlyRoutingConnectionUrl = $ReadonlyRoutingConnectionUrl
Certificate = $Certificate
}
if ($server.VersionMajor -ge 13) {
$replicaparams += @{SeedingMode = $SeedingMode}
}
$null = Add-DbaAgReplica @replicaparams -EnableException -SqlInstance $server
} catch {
$msg = $_.Exception.InnerException.InnerException.Message
if (-not $msg) {
$msg = $_
}
Stop-Function -Message $msg -ErrorRecord $_ -Target $Primary
return
}
}
# Add cluster permissions
if ($ClusterType -eq 'Wsfc') {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Adding endpoint connect permissions"
foreach ($second in $secondaries) {
if ($Pscmdlet.ShouldProcess($Primary, "Adding cluster permissions for availability group named $Name")) {
Write-Message -Level Verbose -Message "WSFC Cluster requires granting [NT AUTHORITY\SYSTEM] a few things. Setting now."
$sql = "GRANT ALTER ANY AVAILABILITY GROUP TO [NT AUTHORITY\SYSTEM]
GRANT CONNECT SQL TO [NT AUTHORITY\SYSTEM]
GRANT VIEW SERVER STATE TO [NT AUTHORITY\SYSTEM]"
try {
$null = $server.Query($sql)
foreach ($second in $secondaries) {
$null = $second.Query($sql)
}
} catch {
Stop-Function -Message "Failure adding cluster service account permissions" -ErrorRecord $_
}
}
}
}
# Add replicas
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Adding secondary replicas"
foreach ($second in $secondaries) {
if ($Pscmdlet.ShouldProcess($second.Name, "Adding replica to availability group named $Name")) {
try {
# Add replicas
$null = Add-DbaAgReplica @replicaparams -EnableException -SqlInstance $second
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $second -Continue
}
}
}
try {
# something is up with .net create(), force a stop
Invoke-Create -Object $ag
} catch {
$msg = $_.Exception.InnerException.InnerException.Message
if (-not $msg) {
$msg = $_
}
Stop-Function -Message $msg -ErrorRecord $_ -Target $Primary
return
}
# Add listener
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Adding listener"
if ($IPAddress) {
if ($Pscmdlet.ShouldProcess($Primary, "Adding static IP listener for $Name to the Primary replica")) {
$null = Add-DbaAgListener -InputObject $ag -IPAddress $IPAddress[0] -SubnetMask $SubnetMask -Port $Port -Dhcp:$Dhcp
}
} elseif ($Dhcp) {
if ($Pscmdlet.ShouldProcess($Primary, "Adding DHCP listener for $Name to all replicas")) {
$null = Add-DbaAgListener -InputObject $ag -Port $Port -Dhcp:$Dhcp
foreach ($second in $secondaries) {
$secag = Get-DbaAvailabilityGroup -SqlInstance $second -AvailabilityGroup $Name
$null = Add-DbaAgListener -InputObject $secag -Port $Port -Dhcp:$Dhcp
}
}
}
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Joining availability group"
foreach ($second in $secondaries) {
if ($Pscmdlet.ShouldProcess("Joining $($second.Name) to $Name")) {
try {
# join replicas to ag
Join-DbaAvailabilityGroup -SqlInstance $second -InputObject $ag -EnableException
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $second -Continue
}
}
}
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Granting permissions on availability group, this may take a moment"
# Grant permissions, but first, get all necessary service accounts
$primaryserviceaccount = $server.ServiceAccount.Trim()
$saname = ([DbaInstanceParameter]($server.DomainInstanceName)).ComputerName
if ($primaryserviceaccount) {
if ($primaryserviceaccount.StartsWith("NT ")) {
$primaryserviceaccount = "$saname`$"
}
if ($primaryserviceaccount.StartsWith("$saname")) {
$primaryserviceaccount = "$saname`$"
}
if ($primaryserviceaccount.StartsWith(".")) {
$primaryserviceaccount = "$saname`$"
}
}
if (-not $primaryserviceaccount) {
$primaryserviceaccount = "$saname`$"
}
$serviceaccounts = @($primaryserviceaccount)
foreach ($second in $secondaries) {
# If service account is empty, add the computer account instead
$secondaryserviceaccount = $second.ServiceAccount.Trim()
$saname = ([DbaInstanceParameter]($second.DomainInstanceName)).ComputerName
if ($secondaryserviceaccount) {
if ($secondaryserviceaccount.StartsWith("NT ")) {
$secondaryserviceaccount = "$saname`$"
}
if ($secondaryserviceaccount.StartsWith("$saname")) {
$secondaryserviceaccount = "$saname`$"
}
if ($secondaryserviceaccount.StartsWith(".")) {
$secondaryserviceaccount = "$saname`$"
}
}
if (-not $secondaryserviceaccount) {
$secondaryserviceaccount = "$saname`$"
}
$serviceaccounts += $secondaryserviceaccount
}
$serviceaccounts = $serviceaccounts | Select-Object -Unique
if ($SeedingMode -eq 'Automatic') {
try {
if ($Pscmdlet.ShouldProcess($server.Name, "Seeding mode is automatic. Adding CreateAnyDatabase permissions to availability group.")) {
$null = $server.Query("ALTER AVAILABILITY GROUP [$Name] GRANT CREATE ANY DATABASE")
}
} catch {
# Log the exception but keep going
Stop-Function -Message "Failure" -ErrorRecord $_
}
}
# Add databases
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Adding databases"
Add-DbaAgDatabase -SqlInstance $Primary -AvailabilityGroup $Name -Database $Database -SeedingMode $SeedingMode -SharedPath $SharedPath
foreach ($second in $secondaries) {
if ($server.HostPlatform -ne "Linux" -and $second.HostPlatform -ne "Linux") {
if ($Pscmdlet.ShouldProcess($second.Name, "Granting Connect permissions to service accounts: $serviceaccounts")) {
$null = Grant-DbaAgPermission -SqlInstance $server, $second -Login $serviceaccounts -Type Endpoint -Permission Connect
}
}
if ($SeedingMode -eq 'Automatic') {
$done = $false
try {
if ($Pscmdlet.ShouldProcess($second.Name, "Seeding mode is automatic. Adding CreateAnyDatabase permissions to availability group.")) {
do {
$second.Refresh()
$second.AvailabilityGroups.Refresh()
if (Get-DbaAvailabilityGroup -SqlInstance $second -AvailabilityGroup $Name) {
$null = $second.Query("ALTER AVAILABILITY GROUP [$Name] GRANT CREATE ANY DATABASE")
$done = $true
} else {
$wait++
Start-Sleep -Seconds 1
}
} while ($wait -lt 20 -and $done -eq $false)
}
} catch {
# Log the exception but keep going
Stop-Function -Message "Failure" -ErrorRecord $_
}
}
}
# Get results
Get-DbaAvailabilityGroup -SqlInstance $Primary -SqlCredential $PrimarySqlCredential -AvailabilityGroup $Name
}
}
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 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: Alias
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaClientAlias
.EXAMPLE
PS C:\> New-DbaClientAlias -ServerName sqlcluster\sharepoint -Alias sp
Creates a new TCP alias on the local workstation called sp, which points sqlcluster\sharepoint
.EXAMPLE
PS C:\> 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
PS C:\> 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(SupportsShouldProcess)]
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"
#Variable marked as unused by PSScriptAnalyzer
#$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
}
<#
#Variable marked as unused by PSScriptAnalyzer
#Looks like it was once used for a Verbose Message
if ($basekey -like "*WOW64*") {
$architecture = "32-bit"
} else {
$architecture = "64-bit"
}
#>
<# DO NOT use Write-Message as this is inside of a script block #>
# 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 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: ComputerManagement, CIM
Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaCmConnection
.EXAMPLE
PS C:\> 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
PS C:\> 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(SupportsShouldProcess, DefaultParameterSetName = 'Credential')]
param (
[Parameter(ValueFromPipeline)]
[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-DbatoolsConfigValue -Name 'ComputerManagement.Cache.Disable.All' -Fallback $false
}
process {
foreach ($connectionObject in $ComputerName) {
if ($Pscmdlet.ShouldProcess($($connectionObject.connection.computername), "Creating connection object")) {
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 instance or instances. 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 SecurePassword
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> New-DbaComputerCertificate
Creates a computer certificate signed by the local domain CA for the local machine with the keylength of 1024.
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> New-DbaComputerCertificate -ComputerName Server1 -WhatIf
Shows what would happen if the command were run
.EXAMPLE
PS C:\> New-DbaComputerCertificate -SelfSigned
Creates a self-signed certificate
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[parameter(ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlInstance")]
[DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[string]$CaServer,
[string]$CaName,
[string]$ClusterInstanceName,
[Alias("Password")]
[securestring]$SecurePassword,
[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 }
# uses dos command locally
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, $SecurePassword)
}
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 {
Invoke-Command2 -ComputerName $computer -Credential $Credential -ArgumentList $certdata, $SecurePassword, $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-DbaConnectionString {
<#
.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 target SQL Server instance or instances.
.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.
.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: Connection, Connect, ConnectionString
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaConnectionString
.EXAMPLE
PS C:\> New-DbaConnectionString -SqlInstance sql2014
Creates a connection string that connects using Windows Authentication
.EXAMPLE
PS C:\> Connect-DbaInstance -SqlInstance sql2016 | New-DbaConnectionString
Builds a connected SMO object using Connect-DbaInstance then extracts and displays the connection string
.EXAMPLE
PS C:\> $wincred = Get-Credential ad\sqladmin
PS C:\> New-DbaConnectionString -SqlInstance sql2014 -Credential $wincred
Creates a connection string that connects using alternative Windows credentials
.EXAMPLE
PS C:\> $sqlcred = Get-Credential sqladmin
PS C:\> $server = New-DbaConnectionString -SqlInstance sql2014 -Credential $sqlcred
Login to sql2014 as SQL login sqladmin.
.EXAMPLE
PS C:\> $server = New-DbaConnectionString -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
PS C:\> $server = New-DbaConnectionString -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
PS C:\> $server = New-DbaConnectionString -SqlInstance sql2014 -NetworkProtocol TcpIp -MultiSubnetFailover
Creates a connection string with Windows Authentication that uses TCPIP and has MultiSubnetFailover enabled.
.EXAMPLE
PS C:\> $connstring = New-DbaConnectionString sql2016 -ApplicationIntent ReadOnly
Creates a connection string with ReadOnly ApplicationIntent.
#>
[CmdletBinding(SupportsShouldProcess)]
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 ($Pscmdlet.ShouldProcess($instance, "Making a new Connection String")) {
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]
#Variable marked as unused by PSScriptAnalyzer
#$authtype = "Windows Authentication with Credential"
$server.ConnectionContext.LoginSecure = $true
$server.ConnectionContext.ConnectAsUser = $true
$server.ConnectionContext.ConnectAsUserName = $username
$server.ConnectionContext.ConnectAsUserPassword = ($Credential).GetNetworkCredential().Password
} else {
#Variable marked as unused by PSScriptAnalyzer
#$authtype = "SQL Authentication"
$server.ConnectionContext.LoginSecure = $false
$server.ConnectionContext.set_Login($username)
$server.ConnectionContext.set_SecurePassword($Credential.Password)
}
}
($server.ConnectionContext.ConnectionString).Replace($guid, $SqlInstance)
}
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias New-DbaSqlConnectionString
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaConnectionStringBuilder {
<#
.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.
.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: SqlBuild, ConnectionString, Connection
Author: zippy1981 | Chrissy LeMaire (@cl)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaConnectionStringBuilder
.EXAMPLE
PS C:\> New-DbaConnectionStringBuilder
Returns an empty ConnectionStringBuilder
.EXAMPLE
PS C:\> "Data Source=localhost,1433;Initial Catalog=AlwaysEncryptedSample;UID=sa;PWD=alwaysB3Encrypt1ng;Application Name=Always Encrypted Sample MVC App;Column Encryption Setting=enabled" | New-DbaConnectionStringBuilder
Returns a connection string builder that can be used to connect to the local sql server instance on the default port.
#>
[CmdletBinding(SupportsShouldProcess)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "")]
param (
[Parameter(ValueFromPipeline)]
[string[]]$ConnectionString = "",
[string]$ApplicationName = "dbatools Powershell Module",
[string]$DataSource = $null,
[string]$InitialCatalog = $null,
[Nullable[bool]]$IntegratedSecurity = $null,
[string]$UserName = $null,
# No point in securestring here, the memory is never stored securely in memory.
[string]$Password = $null,
[Alias('MARS')]
[switch]$MultipleActiveResultSets,
[Alias('AlwaysEncrypted')]
[Data.SqlClient.SqlConnectionColumnEncryptionSetting]$ColumnEncryptionSetting =
[Data.SqlClient.SqlConnectionColumnEncryptionSetting]::Enabled,
[string]$WorkstationId = $env:COMPUTERNAME
)
process {
foreach ($cs in $ConnectionString) {
if ($Pscmdlet.ShouldProcess($cs, "Creating new connection string")) {
$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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias New-DbaSqlConnectionStringBuilder
}
}
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 SecurePassword
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> 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
PS C:\> New-DbaCredential -SqlInstance Server1 -Confirm:$false
Suppresses all prompts to install but prompts to securely enter your password and creates a credential on Server1.
.EXAMPLE
PS C:\> New-DbaCredential -SqlInstance Server1 -Name AzureBackupBlobStore -Identity '<Azure Storage Account Name>' -SecurePassword (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
PS C:\> New-DbaCredential -SqlInstance Server1 -Name 'https://<Azure Storage Account Name>.blob.core.windows.net/<Blob Store Container Name>' -Identity 'SHARED ACCESS SIGNATURE' -SecurePassword (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)] #, ConfirmImpact = "High"
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$Name = $Identity,
[parameter(Mandatory)]
[Alias("CredentialIdentity")]
[string[]]$Identity,
[Alias("Password")]
[Security.SecureString]$SecurePassword,
[ValidateSet('CryptographicProvider', 'None')]
[string]$MappedClassType = "None",
[string]$ProviderName,
[switch]$Force,
[switch]$EnableException
)
begin {
$mappedclass = switch ($MappedClassType) {
"CryptographicProvider" { 1 }
"None" { 0 }
}
}
process {
if (!$SecurePassword) {
Read-Host -AsSecureString -Prompt "Enter the credential password"
}
foreach ($instance in $SqlInstance) {
try {
$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, $SecurePassword)
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-DbaDacOption {
<#
.SYNOPSIS
Creates a new Microsoft.SqlServer.Dac.DacExtractOptions/DacExportOptions object depending on the chosen Type
.DESCRIPTION
Creates a new Microsoft.SqlServer.Dac.DacExtractOptions/DacExportOptions object that can be used during DacPackage extract. Basically saves you the time from remembering the SMO assembly name ;)
See:
https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.dac.dacexportoptions.aspx
https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.dac.dacextractoptions.aspx
for more information
.PARAMETER Type
Selecting the type of the export: Dacpac (default) or Bacpac.
.PARAMETER Action
Choosing an intended action: Publish or Export.
.PARAMETER PublishXml
Specifies the publish profile which will include options and sqlCmdVariables.
.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, Database, Dacpac
Author: Kirill Kravtsov (@nvarscar), nvarscar.wordpress.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaDacOption
.EXAMPLE
PS C:\> $options = New-DbaDacOption -Type Dacpac -Action Export
PS C:\> $options.ExtractAllTableData = $true
PS C:\> $options.CommandTimeout = 0
PS C:\> Export-DbaDacPackage -SqlInstance sql2016 -Database DB1 -Options $options
Uses DacOption object to set the CommandTimeout to 0 then extracts the dacpac for SharePoint_Config on sql2016 to C:\temp\SharePoint_Config.dacpac including all table data.
.EXAMPLE
PS C:\> $options = New-DbaDacOption -Type Dacpac -Action Publish
PS C:\> $options.DeployOptions.DropObjectsNotInSource = $true
PS C:\> Publish-DbaDacPackage -SqlInstance sql2016 -Database DB1 -Options $options -Path c:\temp\db.dacpac
Uses DacOption object to set Deployment Options and publish the db.dacpac dacpac file as DB1 on sql2016
#>
[CmdletBinding(SupportsShouldProcess)]
Param (
[ValidateSet('Dacpac', 'Bacpac')]
[string]$Type = 'Dacpac',
[Parameter(Mandatory)]
[ValidateSet('Publish', 'Export')]
[string]$Action,
[string]$PublishXml,
[switch]$EnableException
)
if ($PScmdlet.ShouldProcess("$type", "Creating New DacOptions of $action")) {
if (-not $script:core) {
$dacfxPath = "$script:PSModuleRoot\bin\smo\Microsoft.SqlServer.Dac.dll"
if ((Test-Path $dacfxPath) -eq $false) {
Stop-Function -Message 'Dac Fx library not found.' -EnableException $EnableException
return
} 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.' -ErrorRecord $_
return
}
}
}
# Pick proper option object depending on type and action
if ($Action -eq 'Export') {
if ($Type -eq 'Dacpac') {
New-Object -TypeName Microsoft.SqlServer.Dac.DacExtractOptions
} elseif ($Type -eq 'Bacpac') {
New-Object -TypeName Microsoft.SqlServer.Dac.DacExportOptions
}
} elseif ($Action -eq 'Publish') {
if ($Type -eq 'Dacpac') {
$output = New-Object -TypeName Microsoft.SqlServer.Dac.PublishOptions
if ($PublishXml) {
try {
$dacProfile = [Microsoft.SqlServer.Dac.DacProfile]::Load($PublishXml)
$output.DeployOptions = $dacProfile.DeployOptions
} catch {
Stop-Function -Message "Could not load profile." -ErrorRecord $_
return
}
} else {
$output.DeployOptions = New-Object -TypeName Microsoft.SqlServer.Dac.DacDeployOptions
}
$output.GenerateDeploymentScript = $false
$output
} elseif ($Type -eq 'Bacpac') {
New-Object -TypeName Microsoft.SqlServer.Dac.DacImportOptions
}
}
}
}
function New-DbaDacProfile {
<#
.SYNOPSIS
Creates a new Publish Profile.
.DESCRIPTION
The New-DbaDacProfile 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
The target SQL Server instance or instances. 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 PublishOptions
Optional hashtable to set publish options. Key/value pairs in the hashtable get converted to strings of "<key>value</key>".
.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: Dacpac
Author: Richie lee (@richiebzzzt)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaDacProfile
.EXAMPLE
PS C:\> New-DbaDacProfile -SqlInstance sql2017 -SqlCredential ad\sqldba -Database WorldWideImporters -Path C:\temp
In this example, a prompt will appear for alternative credentials, then 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
PS C:\> New-DbaDacProfile -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(SupportsShouldProcess)]
param (
[parameter(ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstance[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[Parameter(Mandatory)]
[string[]]$Database,
[string]$Path = "$home\Documents",
[string[]]$ConnectionString,
[hashtable]$PublishOptions,
[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 Convert-HashtableToXMLString($PublishOptions) {
$return = @()
if ($PublishOptions) {
$PublishOptions.GetEnumerator() | ForEach-Object {
$key = $PSItem.Key.ToString()
$value = $PSItem.Value.ToString()
$return += "<$key>$value</$key>"
}
}
$return | Out-String
}
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>
{2}
</PropertyGroup>
</Project>" -f $db[0], $connstring, $(Convert-HashtableToXMLString($PublishOptions))
}
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 {
$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) {
if ($Pscmdlet.ShouldProcess($db, "Creating new DAC Profile")) {
$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
}
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias New-DbaPublishProfile
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function New-DbaDatabase {
<#
.SYNOPSIS
Creates a new database
.DESCRIPTION
This command creates a new database.
It allows creation with multiple files, and sets all growth settings to be fixed size rather than percentage growth.
.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 Name
The name of the new database or databases to be created.
.PARAMETER DataFilePath
The location that data files will be placed, otherwise the default SQL Server data path will be used.
.PARAMETER LogFilePath
The location the log file will be placed, otherwise the default SQL Server log path will be used.
.PARAMETER Collation
The database collation, if not supplied the default server collation will be used.
.PARAMETER Recoverymodel
The recovery model for the database, if not supplied the recovery model from the model database will be used.
.PARAMETER Owner
The login that will be used as the database owner.
.PARAMETER PrimaryFilesize
The size in MB for the Primary file. If this is less than the primary file size for the model database, then the model size will be used instead.
.PARAMETER PrimaryFileGrowth
The size in MB that the Primary file will autogrow by.
.PARAMETER PrimaryFileMaxSize
The maximum permitted size in MB for the Primary File. If this is less the primary file size for the model database, then the model size will be used instead.
.PARAMETER LogSize
The size in MB that the Transaction log will be created.
.PARAMETER LogGrowth
The amount in MB that the log file will be set to autogrow by.
.PARAMETER SecondaryFileCount
The number of files to create in the Secondary filegroup for the database.
.PARAMETER SecondaryFilesize
The size in MB of the files to be added to the Secondary filegroup. Each file added will be created with this size setting.
.PARAMETER SecondaryFileMaxSize
The maximum permitted size in MB for the Secondary data files to grow to. Each file added will be created with this max size setting.
.PARAMETER SecondaryFileGrowth
The amount in MB that the Secondary files will be set to autogrow by. Use 0 for no growth allowed. Each file added will be created with this growth setting.
.PARAMETER DefaultFileGroup
Sets the default file group. Either primary or secondary.
.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: Matthew Darwin (@evoDBA, naturalselectiondba.wordpress.com) | Chrissy LeMaire (@cl)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaDatabase
.EXAMPLE
New-DbaDatabase -SqlInstance sql1
Creates a randomly named database (random-N) on instance sql1
.EXAMPLE
New-DbaDatabase -SqlInstance sql1 -Name dbatools, dbachecks
Creates a database named dbatools and a database named dbachecks on sql1
.EXAMPLE
New-DbaDatabase -SqlInstance sql1, sql2, sql3 -Name multidb, multidb2 -SecondaryFilesize 20 -SecondaryFileGrowth 20 -LogSize 20 -LogGrowth 20
Creates two databases, multidb and multidb2, on 3 instances (sql1, sql2 and sql3) and sets the secondary data file size to 20MB, the file growth to 20MB and the log growth to 20MB for each
.EXAMPLE
New-DbaDatabase -SqlInstance sql1 -Name nondefault -DataFilePath M:\Data -LogFilePath 'L:\Logs with spaces' -SecondaryFileCount 2
Creates a database named nondefault and places data files in in the M:\data directory and log files in "L:\Logs with spaces".
Creates a secondary group with 2 files in the Secondary filegroup.
#>
[Cmdletbinding(SupportsShouldProcess, ConfirmImpact = "Low")]
param
(
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Name,
[string]$Collation,
[string]$Recoverymodel,
[string]$Owner,
[string]$DataFilePath,
[string]$LogFilePath,
[int32]$PrimaryFilesize,
[int32]$PrimaryFileGrowth,
[int32]$PrimaryFileMaxSize,
[int32]$LogSize,
[int32]$LogGrowth,
[int32]$SecondaryFilesize,
[int32]$SecondaryFileGrowth,
[int32]$SecondaryFileMaxSize,
[int32]$SecondaryFileCount,
[ValidateSet('Primary', 'Secondary')]
[string]$DefaultFileGroup,
[switch]$EnableException
)
begin {
# do some checks to see if the advanced config settings will be invoked
if (Test-Bound -ParameterName DataFilePath, LogFilePath, DefaultFileGroup) {
$advancedconfig = $true
}
if (Test-Bound -ParameterName PrimaryFilesize, PrimaryFileGrowth, PrimaryFileMaxSize) {
$advancedconfig = $true
}
if (Test-Bound -ParameterName LogSize, LogGrowth) {
$advancedconfig = $true
}
if (Test-Bound -ParameterName SecondaryFilesize, SecondaryFileMaxSize, SecondaryFileGrowth, SecondaryFileCount) {
$advancedconfig = $true
}
if ($advancedconfig) {
Write-Message -Message "Advanced data file configuration will be invoked" -Level Verbose
}
}
process {
if (Test-FunctionInterrupt) {
return
}
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if ($advancedconfig -and $server.VersionMajor -eq 8) {
Stop-Function -Message "Advanced configuration options are not available to SQL Server 2000. Aborting creation of database on $instance" -Target $instance -Continue
}
if (-not (Test-Bound -ParameterName Name)) {
$Name = "random-$(Get-Random)"
}
if (-not (Test-Bound -ParameterName DataFilePath)) {
$DataFilePath = (Get-DbaDefaultPath -SqlInstance $server).Data
}
if (-not (Test-Bound -ParameterName LogFilePath)) {
$LogFilePath = (Get-DbaDefaultPath -SqlInstance $server).Log
}
if (-not (Test-DbaPath -SqlInstance $server -Path $LogFilePath)) {
try {
Write-Message -Message "Creating directory $LogFilePath" -Level Verbose
$null = New-DbaDirectory -SqlInstance $server -Path $LogFilePath -EnableException
} catch {
Stop-Function -Message "Error creating log file directory $LogFilePath" -Target $instance -Continue
}
}
if (-not (Test-DbaPath -SqlInstance $server -Path $DataFilePath)) {
try {
Write-Message -Message "Creating directory $DataFilePath" -Level Verbose
$null = New-DbaDirectory -SqlInstance $server -Path $DataFilePath -EnableException
} catch {
Stop-Function -Message "Error creating secondary file directory $DataFilePath on $instance" -Target $instance -Continue
}
}
Write-Message -Message "Set local data path to $DataFilePath and local log path to $LogFilePath" -Level Verbose
foreach ($dbname in $Name) {
if ($server.Databases[$dbname].Name) {
Stop-Function -Message "Database $dbname already exists on $instance" -Target $instance -Continue
}
try {
Write-Message -Message "Creating smo object for new database $dbname" -Level Verbose
$newdb = New-Object Microsoft.SqlServer.Management.Smo.Database($server, $dbname)
} catch {
Stop-Function -Message "Error creating database object for $dbname on server $server" -ErrorRecord $_ -Target $instance -Continue
}
if ($Collation) {
Write-Message -Message "Setting collation to $Collation" -Level Verbose
$newdb.Collation = $Collation
}
if ($Recoverymodel) {
Write-Message -Message "Setting recovery model to $Recoverymodel" -Level Verbose
$newdb.Recoverymodel = $Recoverymodel
}
if ($advancedconfig) {
try {
Write-Message -Message "Creating PRIMARY filegroup" -Level Verbose
$primaryfg = New-Object Microsoft.SqlServer.Management.Smo.Filegroup($newdb, "PRIMARY")
$newdb.Filegroups.Add($primaryfg)
} catch {
Stop-Function -Message "Error creating Primary filegroup object" -ErrorRecord $_ -Target $instance -Continue
}
#add the primary file
try {
$primaryfilename = $dbname + "_PRIMARY"
Write-Message -Message "Creating file name $primaryfilename in filegroup PRIMARY" -Level Verbose
#check the size of the modeldev file; if larger than our $PrimaryFilesize setting use that instead
if ($server.Databases["model"].FileGroups["PRIMARY"].Files["modeldev"].Size -gt ($PrimaryFilesize * 1024)) {
Write-Message -Message "model database modeldev larger than our the PrimaryFilesize so using modeldev size for Primary file" -Level Verbose
$PrimaryFilesize = ($server.Databases["model"].FileGroups["PRIMARY"].Files["modeldev"].Size / 1024)
if ($PrimaryFilesize -gt $PrimaryFileMaxSize) {
Write-Message -Message "Resetting Primary File Max size to be the new Primary File Size setting" -Level Verbose
$PrimaryFileMaxSize = $PrimaryFilesize
}
}
#create the filegroup object
$primaryfile = New-Object Microsoft.SqlServer.Management.Smo.DataFile($primaryfg, $primaryfilename)
$primaryfile.FileName = $DataFilePath + "\" + $primaryfilename + ".mdf"
$primaryfile.IsPrimaryFile = $true
if (Test-Bound -ParameterName PrimaryFilesize) {
$primaryfile.Size = ($PrimaryFilesize * 1024)
}
if (Test-Bound -ParameterName PrimaryFileGrowth) {
$primaryfile.Growth = ($PrimaryFileGrowth * 1024)
$primaryfile.GrowthType = "KB"
}
if (Test-Bound -ParameterName PrimaryFileMaxSize) {
$primaryfile.MaxSize = ($PrimaryFileMaxSize * 1024)
}
#add the file to the filegroup
$primaryfg.Files.Add($primaryfile)
} catch {
Stop-Function -Message "Error adding file to Primary filegroup" -ErrorRecord $_ -Target $instance -Continue
}
try {
$logname = $dbname + "_Log"
Write-Message -Message "Creating log $logname" -Level Verbose
#check the size of the modellog file; if larger than our $LogSize setting use that instead
if ($server.Databases["model"].LogFiles["modellog"].Size -gt ($LogSize * 1024)) {
Write-Message -Message "model database modellog larger than our the LogSize so using modellog size for Log file size" -Level Verbose
$LogSize = ($server.Databases["model"].LogFiles["modellog"].Size / 1024)
}
$tlog = New-Object Microsoft.SqlServer.Management.Smo.LogFile($newdb, $logname)
$tlog.FileName = $LogFilePath + "\" + $logname + ".ldf"
if (Test-Bound -ParameterName LogSize) {
$tlog.Size = ($LogSize * 1024)
}
if (Test-Bound -ParameterName LogGrowth) {
$tlog.Growth = ($LogGrowth * 1024)
$tlog.GrowthType = "KB"
}
#add the log to the db
$newdb.LogFiles.Add($tlog)
} catch {
Stop-Function -Message "Error adding log file to database." -ErrorRecord $_ -Target $instance -Continue
}
if (Test-Bound -ParameterName SecondaryFileMaxSize, SecondaryFileGrowth, SecondaryFilesize) {
#add the Secondary data file group
try {
$secondaryfilegroupname = $dbname + "_MainData"
Write-Message -Message "Creating Secondary filegroup $secondaryfilegroupname" -Level Verbose
$secondaryfg = New-Object Microsoft.SqlServer.Management.Smo.Filegroup($newdb, $secondaryfilegroupname)
$newdb.Filegroups.Add($secondaryfg)
} catch {
Stop-Function -Message "Error creating Secondary filegroup" -ErrorRecord $_ -Target $instance -Continue
}
# add the required number of files to the filegroup in a loop
$secondaryfgcount = $bail = 0
# open a loop while the filecounter is less than the required number of files
do {
$secondaryfgcount++
try {
$secondaryfilename = "$($secondaryfilegroupname)_$($secondaryfgcount)"
Write-Message -Message "Creating file name $secondaryfilename in filegroup $secondaryfilegroupname" -Level Verbose
$secondaryfile = New-Object Microsoft.SQLServer.Management.Smo.Datafile($secondaryfg, $secondaryfilename)
$secondaryfile.FileName = $DataFilePath + "\" + $secondaryfilename + ".ndf"
if (Test-Bound -ParameterName SecondaryFilesize) {
$secondaryfile.Size = ($SecondaryFilesize * 1024)
}
if (Test-Bound -ParameterName SecondaryFileGrowth) {
$secondaryfile.Growth = ($SecondaryFileGrowth * 1024)
$secondaryfile.GrowthType = "KB"
}
if (Test-Bound -ParameterName SecondaryFileMaxSize) {
$secondaryfile.MaxSize = ($SecondaryFileMaxSize * 1024)
}
$secondaryfg.Files.Add($secondaryfile)
} catch {
$bail = $true
Stop-Function -Message "Error adding file $secondaryfg to $secondaryfilegroupname" -ErrorRecord $_ -Target $instance
return
}
} while ($secondaryfgcount -le $SecondaryFileCount -or $bail)
}
}
Write-Message -Message "Creating Database $dbname" -Level Verbose
if ($PSCmdlet.ShouldProcess($instance, "Creating the database $dbname on instance $instance")) {
try {
$newdb.Create()
} catch {
Stop-Function -Message "Error creating Database $dbname on server $instance" -ErrorRecord $_ -Target $instance -Continue
}
if ($Owner) {
Write-Message -Message "Setting database owner to $Owner" -Level Verbose
try {
$newdb.SetOwner($Owner)
} catch {
Stop-Function -Message "Error setting Database Owner to $Owner" -ErrorRecord $_ -Target $instance -Continue
}
}
if ($DefaultFileGroup -eq "Secondary") {
Write-Message -Message "Setting default filegroup to $secondaryfilegroupname" -Level Verbose
try {
$newdb.SetDefaultFileGroup($secondaryfilegroupname)
} catch {
Stop-Function -Message "Error setting default filegorup to $secondaryfilegroupname" -ErrorRecord $_ -Target $instance -Continue
}
}
Get-DbaDatabase -SqlInstance $server -Database $dbname
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 target SQL Server instance or instances.
.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 SecurePassword
Optional password - if no password is supplied, the password will be protected by the master key
.PARAMETER InputObject
Enables piping from Get-DbaDatabase
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> 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
PS C:\> 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, ConfirmImpact = "Low")]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Name,
[string[]]$Database = "master",
[string[]]$Subject,
[datetime]$StartDate = (Get-Date),
[datetime]$ExpirationDate = $StartDate.AddYears(5),
[switch]$ActiveForServiceBrokerDialog,
[Alias("Password")]
[Security.SecureString]$SecurePassword,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias New-DbaDatabaseCertificate
}
process {
if ($SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database
}
foreach ($db in $InputObject) {
if ((Test-Bound -Not -ParameterName Name)) {
Write-Message -Level Verbose -Message "Name of certificate not specified, setting it to '$db'"
$Name = $db.Name
}
if ((Test-Bound -Not -ParameterName Subject)) {
Write-Message -Level Verbose -Message "Subject not specified, setting it to '$Name Database Certificate'"
$subject = "$Name Database Certificate"
}
foreach ($cert in $Name) {
if ($null -ne $db.Certificates[$cert]) {
Stop-Function -Message "Certificate '$cert' already exists in $($db.Name) on $($db.Parent.Name)" -Target $db -Continue
}
if ($Pscmdlet.ShouldProcess($db.Parent.Name, "Creating certificate for database '$($db.Name)'")) {
# something is up with .net, force a stop
$eap = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
try {
$smocert = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Certificate $db, $cert
$smocert.StartDate = $StartDate
$smocert.Subject = $Subject
$smocert.ExpirationDate = $ExpirationDate
$smocert.ActiveForServiceBrokerDialog = $ActiveForServiceBrokerDialog
if ($SecurePassword) {
$smocert.Create(([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($SecurePassword))))
} else {
$smocert.Create()
}
Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name ComputerName -value $db.Parent.ComputerName
Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name InstanceName -value $db.Parent.ServiceName
Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name SqlInstance -value $db.Parent.DomainInstanceName
Add-Member -Force -InputObject $smocert -MemberType NoteProperty -Name Database -value $db.Name
Select-DefaultView -InputObject $smocert -Property ComputerName, InstanceName, SqlInstance, Database, Name, Subject, StartDate, ActiveForServiceBrokerDialog, ExpirationDate, Issuer, LastBackupDate, Owner, PrivateKeyEncryptionType, Serial
} catch {
$ErrorActionPreference = $eap
Stop-Function -Message "Failed to create certificate in $($db.Name) on $($db.Parent.Name)" -Target $smocert -ErrorRecord $_ -Continue
}
$ErrorActionPreference = $eap
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaDbMasterKey {
<#
.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 target SQL Server instance or instances.
.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.
.PARAMETER Credential
Enables easy creation of a secure password.
.PARAMETER Database
The database where the master key will be created. Defaults to master.
.PARAMETER SecurePassword
Secure string used to create the key.
.PARAMETER InputObject
Database object piped in from Get-DbaDatabase.
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> New-DbaDbMasterKey -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
PS C:\> New-DbaDbMasterKey -SqlInstance Server1 -Credential usernamedoesntmatter
You will be prompted by a credential interface to securely enter your password, then a master key will be created in the master database on server1 if it does not exist.
.EXAMPLE
PS C:\> New-DbaDbMasterKey -SqlInstance Server1 -Database db1 -Confirm:$false
Suppresses all prompts to install but prompts in th console to securely enter your password and creates a master key in the 'db1' database
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[string[]]$Database = "master",
[Alias("Password")]
[Security.SecureString]$SecurePassword,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
begin {
if ($Credential) {
$SecurePassword = $Credential.Password
} else {
if (-not $SecurePassword) {
$SecurePassword = Read-Host "Password" -AsSecureString
}
}
}
process {
if ($SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -Database $Database -ExcludeDatabase $ExcludeDatabase
}
foreach ($db in $InputObject) {
if ($null -ne $db.MasterKey) {
Stop-Function -Message "Master key already exists in the $db database on $($db.Parent.Name)" -Target $db -Continue
}
if ($Pscmdlet.ShouldProcess($db.Parent.Name, "Creating master key for database '$($db.Name)'")) {
try {
$masterkey = New-Object Microsoft.SqlServer.Management.Smo.MasterKey $db
$masterkey.Create(([System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($SecurePassword))))
Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name ComputerName -value $db.Parent.ComputerName
Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name InstanceName -value $db.Parent.ServiceName
Add-Member -Force -InputObject $masterkey -MemberType NoteProperty -Name SqlInstance -value $db.Parent.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
} catch {
Stop-Function -Message "Failed to create master key in $db on $instance. Exception: $($_.Exception.InnerException)" -Target $masterkey -ErrorRecord $_ -Continue
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias New-DbaDatabaseMasterKey
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaDbSnapshot {
<#
.SYNOPSIS
Creates database snapshots
.DESCRIPTION
Creates database snapshots without hassles
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.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: Simone Bizzotto (@niphold)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaDbSnapshot
.EXAMPLE
PS C:\> 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
PS C:\> New-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR -Name HR_snap
Creates snapshot named "HR_snap" for HR
.EXAMPLE
PS C:\> New-DbaDbSnapshot -SqlInstance sqlserver2014a -Database HR -NameSuffix 'fool_{0}_snap'
Creates snapshot named "fool_HR_snap" for HR
.EXAMPLE
PS C:\> 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
PS C:\> 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) {
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-DbaPath -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) {
#Variable marked as unused by PSScriptAnalyzer
#$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 or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaDbUser
.EXAMPLE
PS C:\> New-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Login user1
Creates a new sql user with login named user1 in the specified database.
.EXAMPLE
PS C:\> New-DbaDbUser -SqlInstance sqlserver2014 -Database DB1 -Username user1
Creates a new sql user without login named user1 in the specified database.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaDbUser -SqlInstance sqlserver1 -Database DB1 | New-DbaDbUser -SqlInstance sqlserver2 -Database DB1
Copies users from sqlserver1.DB1 to sqlserver2.DB1. Does not copy permissions!
#>
[CmdletBinding(SupportsShouldProcess, 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 {
$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 ($null -eq $smoLogin) {
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-DbaDbUser -SqlInstance $instance -SqlCredential $sqlcredential -Database $db.Name | Where-Object name -eq $smoUser.Name
}
}
}
}
function New-DbaDirectory {
<#
.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 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: Path, Directory, Folder
Author: Stuart Moore
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: Admin access to server (not SQL Services),
Remoting must be enabled and accessible if $SqlInstance is not local
.LINK
https://dbatools.io/New-DbaDirectory
.EXAMPLE
PS C:\> New-DbaDirectory -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
PS C:\> $credential = Get-Credential
PS C:\> New-DbaDirectory -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(SupportsShouldProcess)]
param (
[Parameter(Mandatory)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Parameter(Mandatory)]
[string]$Path,
[PSCredential]$SqlCredential,
[switch]$EnableException
)
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias New-DbaSqlDirectory
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$Path = $Path.Replace("'", "''")
$exists = Test-DbaPath -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
if ($Pscmdlet.ShouldProcess($path, "Creating a new path on $($server.name)")) {
try {
$null = $server.Query($sql)
$Created = $true
} catch {
$Created = $false
Stop-Function -Message "Failure" -ErrorRecord $_
}
[pscustomobject]@{
Server = $SqlInstance
Path = $Path
Created = $Created
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function New-DbaEndpoint {
<#
.SYNOPSIS
Creates endpoints on a SQL Server instance.
.DESCRIPTION
Creates endpoints on a SQL Server instance.
.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 Name
The name of the endpoint. If a name is not specified, one will be auto-generated.
.PARAMETER Type
The type of endpoint. Defaults to DatabaseMirroring. Options: DatabaseMirroring, ServiceBroker, Soap, TSql
.PARAMETER Protocol
The type of protocol. Defaults to tcp. Options: Tcp, NamedPipes, Http, Via, SharedMemory
.PARAMETER Role
The type of role. Defaults to All. Options: All, None, Partner, Witness
.PARAMETER Port
Port for TCP. If one is not provided, it will be autogenerated.
.PARAMETER SslPort
Port for SSL
.PARAMETER Certificate
Database certificate used for authentication.
.PARAMETER EndpointEncryption
Used to specify the state of encryption on the endpoint. Defaults to required.
Disabled
Required
Supported
.PARAMETER EncryptionAlgorithm
Specifies an encryption algorithm used on an endpoint. Defaults to Aes.
Options are:
AesRC4
Aes
None
RC4
RC4Aes
.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.
.PARAMETER Owner
Owner of the endpoint. Defaults to sa.
.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: Endpoint
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaEndpoint
.EXAMPLE
PS C:\> New-DbaEndpoint -SqlInstance localhost
Creates all Endpoint(s) on the local default SQL Server instance
.EXAMPLE
PS C:\> New-DbaEndpoint -SqlInstance localhost, sql2016
Returns all Endpoint(s) for the local and sql2016 SQL Server instances
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string]$Name,
[ValidateSet('DatabaseMirroring', 'ServiceBroker', 'Soap', 'TSql')]
[string]$Type = 'DatabaseMirroring',
[ValidateSet('Tcp', 'NamedPipes', 'Http', 'Via', 'SharedMemory')]
[string]$Protocol = 'Tcp',
[ValidateSet('All', 'None', 'Partner', 'Witness')]
[string]$Role = 'All',
[ValidateSet('Disabled', 'Required', 'Supported')]
[string]$EndpointEncryption = 'Required',
[ValidateSet('Aes', 'AesRC4', 'None', 'RC4', 'RC4Aes')]
[string]$EncryptionAlgorithm = 'Aes',
[string]$Certificate,
[int]$Port,
[int]$SslPort,
[string]$Owner,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName Name -Not)) {
$name = "endpoint-" + [DateTime]::Now.ToString('s').Replace(":", "-")
}
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 (Test-Bound -ParameterName Owner)) {
$Owner = Get-SaLoginName -SqlInstance $server
}
if ($Certificate) {
$cert = Get-DbaDbCertificate -SqlInstance $server -Certificate $Certificate
if (-not $cert) {
Stop-Function -Message "Certificate $Certificate does not exist on $instance" -ErrorRecord $_ -Target $Certificate -Continue
}
}
# Thanks to https://github.com/mmessano/PowerShell/blob/master/SQL-ConfigureDatabaseMirroring.ps1
if ($Port) {
$tcpPort = $port
} else {
$thisport = (Get-DbaEndPoint -SqlInstance $server).Protocol.Tcp
$measure = $thisport | Measure-Object ListenerPort -Maximum
if ($thisport.ListenerPort -eq 0) {
$tcpPort = 5022
} elseif ($measure.Maximum) {
$maxPort = $measure.Maximum
#choose a random port that is greater than the current max port
$tcpPort = $maxPort + (New-Object Random).Next(1, 500)
} else {
$maxPort = 5000
#choose a random port that is greater than the current max port
$tcpPort = $maxPort + (New-Object Random).Next(1, 500)
}
}
if ($Pscmdlet.ShouldProcess($server.Name, "Creating endpoint $Name of type $Type using protocol $Protocol and if TCP then using Port $tcpPort")) {
try {
$endpoint = New-Object Microsoft.SqlServer.Management.Smo.EndPoint $server, $Name
$endpoint.ProtocolType = [Microsoft.SqlServer.Management.Smo.ProtocolType]::$Protocol
$endpoint.EndpointType = [Microsoft.SqlServer.Management.Smo.EndpointType]::$Type
$endpoint.Owner = $Owner
if ($Protocol -eq "TCP") {
$endpoint.Protocol.Tcp.ListenerPort = $tcpPort
$endpoint.Payload.DatabaseMirroring.ServerMirroringRole = [Microsoft.SqlServer.Management.Smo.ServerMirroringRole]::$Role
if (Test-Bound -ParameterName SslPort) {
$endpoint.Protocol.Tcp.SslPort = $SslPort
}
$endpoint.Payload.DatabaseMirroring.EndpointEncryption = [Microsoft.SqlServer.Management.Smo.EndpointEncryption]::$EndpointEncryption
$endpoint.Payload.DatabaseMirroring.EndpointEncryptionAlgorithm = [Microsoft.SqlServer.Management.Smo.EndpointEncryptionAlgorithm]::$EncryptionAlgorithm
}
if ($Certificate) {
$outscript = $endpoint.Script()
$outscript = $outscript.Replace("ROLE = ALL,", "ROLE = ALL, AUTHENTICATION = CERTIFICATE $cert,")
$server.Query($outscript)
} else {
$null = $endpoint.Create()
}
$server.Endpoints.Refresh()
Get-DbaEndpoint -SqlInstance $server -Endpoint $name
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
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 SecurePassword
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, Security
Author: Kirill Kravtsov (@nvarscar)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaLogin
.EXAMPLE
PS C:\> 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
PS C:\> $securePassword = Read-Host "Input password" -AsSecureString
PS C:\> 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
PS C:\> 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
PS C:\> 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.
.EXAMPLE
PS C:\> New-DbaLogin -SqlInstance sql1 -Login domain\user
Creates a new Windows Authentication backed login on sql1. The login will be part of the public server role.
#>
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Password", ConfirmImpact = "Low")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "", Justification = "For Parameters Password and MapToCredential")]
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)]
[parameter(ParameterSetName = "Password")]
[parameter(ParameterSetName = "PasswordHash")]
[parameter(ParameterSetName = "MapToCertificate")]
[parameter(ParameterSetName = "MapToAsymmetricKey")]
[object[]]$InputObject,
[Alias("Rename")]
[hashtable]$LoginRenameHashtable,
[parameter(ParameterSetName = "Password", Position = 3)]
[Alias("Password")]
[Security.SecureString]$SecurePassword,
[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
}
foreach ($instance in $SqlInstance) {
try {
$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 !($SecurePassword -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 !($SecurePassword -or $HashedPassword)) {
$SecurePassword = 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 ($SecurePassword) {
$currentHashedPassword = Get-PasswordHash $SecurePassword $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-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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaScriptingOption
.EXAMPLE
PS C:\> $options = New-DbaScriptingOption
PS C:\> $options.ScriptDrops = $false
PS C:\> $options.WithDependencies = $true
PS C:\> Get-DbaAgentJob -SqlInstance sql2016 | Export-DbaScript -ScriptingOptionObject $options
Exports Agent Jobs with the Scripting Options ScriptDrops set to $false and WithDependencies set to true
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
param()
New-Object Microsoft.SqlServer.Management.Smo.ScriptingOptions
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 instance or instances.
.PARAMETER SqlCredential
Allows you to login to SQL Server using alternative credentials.
.PARAMETER SecurePassword
Secure string used to create the key.
.PARAMETER Credential
Enables easy creation of a secure password.
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> New-DbaServiceMasterKey -SqlInstance Server1
You will be prompted to securely enter your Service Key password, then a master key will be created in the master database on server1 if it does not exist.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[Alias("Password")]
[Security.SecureString]$SecurePassword,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
if ($PSCmdlet.ShouldProcess("$instance", "Creating New MasterKey")) {
New-DbaDbMasterKey -SqlInstance $instance -Database master -SecurePassword $SecurePassword -Credential $Credential
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 SecurePassword
Required password that will be used for the security key in SSISDB.
.PARAMETER Credential
Use a credential object instead of a securepassword
.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/
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaSsisCatalog
.EXAMPLE
PS C:\> $SecurePassword = Read-Host -AsSecureString -Prompt "Enter password"
PS C:\> New-DbaSsisCatalog -SqlInstance DEV01 -SecurePassword $SecurePassword
Creates the SSIS Catalog on server DEV01 with the specified password.
.EXAMPLE
PS C:\> New-DbaSsisCatalog -SqlInstance sql2016 -Credential usernamedoesntmatter
Creates the SSIS Catalog on server DEV01 with the specified password in the credential prompt. As the example username suggets the username does not matter.
This is simply an easier way to get a secure password.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[Alias("Password")]
[Security.SecureString]$SecurePassword,
[string]$SsisCatalog = "SSISDB",
[switch]$EnableException
)
begin {
if (-not $SecurePassword -and -not $Credential) {
Stop-Function -Message "You must specify either -SecurePassword or -Credential"
return
}
if (-not $SecurePassword -and $Credential) {
$SecurePassword = $Credential.Password
}
}
process {
if (Test-FunctionInterrupt) {
return
}
foreach ($instance in $SqlInstance) {
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-DbaService -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 -Name IsSqlClrEnabled
if (-not $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.Count -gt 0) {
Stop-Function -Message "SSIS Catalog already exists" -Continue -Target $ssis.Catalogs
} 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($SecurePassword)))))
} catch {
Stop-Function -Message "Failed to create SSIS Catalog: $_" -Target $_ -Continue
}
try {
$ssisdb.Create()
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
SsisCatalog = $SsisCatalog
Created = $true
}
} catch {
$msg = $_.Exception.InnerException.InnerException.Message
if (-not $msg) {
$msg = $_
}
Stop-Function -Message "$msg" -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 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: Debug
Author: Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbatoolsSupportPackage
.EXAMPLE
PS C:\> New-DbatoolsSupportPackage
Creates a large support pack in order to help us troubleshoot stuff.
#>
[CmdletBinding(SupportsShouldProcess)]
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 {
# here to avoid an empty catch
$null = 1
}
}
#endregion Helper functions
}
process {
if ($Pscmdlet.ShouldProcess("Creating a Support Package for diagnosing Dbatools")) {
$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"
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. 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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaXESession
.EXAMPLE
PS C:\> $session = New-DbaXESession -SqlInstance sql2017 -Name XeSession_Test
PS C:\> $event = $session.AddEvent("sqlserver.file_written")
PS C:\> $event.AddAction("package0.callstack")
PS C:\> $session.Create()
Returns a new XE Session object from sql2017 then adds an event, an action then creates it.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(Mandatory)]
[string]$Name,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if ($Pscmdlet.ShouldProcess($instance, "Creating new XESession")) {
$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)
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 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
Author: Chrissy LeMaire (@cl) | SmartTarget by Gianluca Sartori (@spaghettidba)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaXESmartCsvWriter
.EXAMPLE
PS C:\> $columns = "cpu_time", "duration", "physical_reads", "logical_reads", "writes", "row_count"
PS C:\> $response = New-DbaXESmartCsvWriter -OutputFile c:\temp\workload.csv -OutputColumn $columns -OverWrite -Event "sql_batch_completed"
PS C:\> Start-DbaXESmartTarget -SqlInstance localhost\sql2017 -Session "Profiler Standard" -Responder $response
Writes Extended Events to the file "C:\temp\workload.csv".
#>
[CmdletBinding(SupportsShouldProcess)]
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 }
if ($Pscmdlet.ShouldProcess("Creating new XESmartCsvWriter Object")) {
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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 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
Author: Chrissy LeMaire (@cl) | SmartTarget by Gianluca Sartori (@spaghettidba)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaXESmartEmail
.EXAMPLE
PS C:\> $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"
>> }
>>
PS C:\> $emailresponse = New-DbaXESmartEmail @params
PS C:\> Start-DbaXESmartTarget -SqlInstance sql2017 -Session querytracker -Responder $emailresponse
Sends an email each time a querytracker event is captured.
#>
[CmdletBinding(SupportsShouldProcess)]
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 }
if ($Pscmdlet.ShouldProcess("Creating new XESmartEmail Object")) {
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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. 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 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
Author: Chrissy LeMaire (@cl) | SmartTarget by Gianluca Sartori (@spaghettidba)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaXESmartQueryExec
.EXAMPLE
PS C:\> $response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query "update table set whatever = 1"
PS C:\> 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(SupportsShouldProcess)]
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 {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if ($Pscmdlet.ShouldProcess($instance, "Creating new XESmartQueryExec")) {
$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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. 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 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
Author: Chrissy LeMaire (@cl) | SmartTarget by Gianluca Sartori (@spaghettidba)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/New-DbaXESmartReplay
.EXAMPLE
PS C:\> $response = New-DbaXESmartReplay -SqlInstance sql2017 -Database planning
PS C:\> 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
PS C:\> $response = New-DbaXESmartReplay -SqlInstance sql2017 -Database planning
PS C:\> 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(SupportsShouldProcess)]
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 {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if ($Pscmdlet.ShouldProcess($instance, "Creating new XESmartReply")) {
Write-Message -Message "Making a New XE SmartReplay for $Event against $instance running on $($server.name)" -Level Verbose
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
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. 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 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
Author: Chrissy LeMaire (@cl) | SmartTarget by Gianluca Sartori (@spaghettidba)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
SmartTarget: by Gianluca Sartori (@spaghettidba)
.LINK
https://dbatools.io/New-DbaXESmartTableWriter
.EXAMPLE
PS C:\> $columns = "cpu_time", "duration", "physical_reads", "logical_reads", "writes", "row_count", "batch_text"
PS C:\> $response = New-DbaXESmartTableWriter -SqlInstance sql2017 -Database dbadb -Table deadlocktracker -OutputColumn $columns -Filter "duration > 10000"
PS C:\> Start-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response
Writes Extended Events to the deadlocktracker table in dbadb on sql2017.
#>
[CmdletBinding(SupportsShouldProcess)]
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 {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 11
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
if ($Pscmdlet.ShouldProcess($instance, "Creating new XESmartTableWriter")) {
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-DbaDacPackage {
<#
.SYNOPSIS
The Publish-DbaDacPackage 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 (Microsoft.Data.Tools.MSBuild) and this uses that nuget package.
.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 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 Type
Selecting the type of the export: Dacpac (default) or Bacpac.
.PARAMETER DacOption
Export options for a corresponding export type. Can be created by New-DbaDacOption -Type Dacpac | Bacpac
.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 (@richiebzzzt)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Publish-DbaDacPackage
.EXAMPLE
PS C:\> $options = New-DbaDacOption -Type Dacpac -Action Publish
PS C:\> $options.DeployOptions.DropObjectsNotInSource = $true
PS C:\> Publish-DbaDacPackage -SqlInstance sql2016 -Database DB1 -DacOption $options -Path c:\temp\db.dacpac
Uses DacOption object to set Deployment Options and updates DB1 database on sql2016 from the db.dacpac dacpac file, dropping objects that are missing from source
.EXAMPLE
PS C:\> Publish-DbaDacPackage -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
PS C:\> New-DbaDacProfile -SqlInstance sql2016 -Database db2 -Path C:\temp
PS C:\> Export-DbaDacPackage -SqlInstance sql2016 -Database db2 | Publish-DbaDacPackage -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
PS C:\> $loc = "C:\Users\bob\source\repos\Microsoft.Data.Tools.Msbuild\lib\net46\Microsoft.SqlServer.Dac.dll"
PS C:\> Publish-DbaDacPackage -SqlInstance "local" -Database WideWorldImporters -Path C:\temp\WideWorldImporters.dacpac -PublishXml C:\temp\WideWorldImporters.publish.xml -DacFxPath $loc
.EXAMPLE
PS C:\> Publish-DbaDacPackage -SqlInstance sql2017 -Database WideWorldImporters -Path C:\temp\sql2016-WideWorldImporters.dacpac -PublishXml C:\temp\sql2016-WideWorldImporters-publish.xml -GenerateDeploymentScript -ScriptOnly
Does not deploy the changes, but will generate the deployment script that would be executed against WideWorldImporters.
.EXAMPLE
PS C:\> Publish-DbaDacPackage -SqlInstance sql2017 -Database WideWorldImporters -Path C:\temp\sql2016-WideWorldImporters.dacpac -PublishXml C:\temp\sql2016-WideWorldImporters-publish.xml -GenerateDeploymentReport -ScriptOnly
Does not deploy the changes, but will generate the deployment report that would be executed against WideWorldImporters.
#>
[CmdletBinding(DefaultParameterSetName = 'Obj')]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstance[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[string]$Path,
[Parameter(Mandatory, ParameterSetName = 'Xml')]
[string]$PublishXml,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[string[]]$Database,
[string[]]$ConnectionString,
[parameter(ParameterSetName = 'Xml')]
[switch]$GenerateDeploymentScript,
[parameter(ParameterSetName = 'Xml')]
[switch]$GenerateDeploymentReport,
[Switch]$ScriptOnly,
[ValidateSet('Dacpac', 'Bacpac')]
[string]$Type = 'Dacpac',
[string]$OutputPath = "$home\Documents",
[switch]$IncludeSqlCmdVars,
[Parameter(ParameterSetName = 'Obj')]
[Alias("Option")]
[object]$DacOption,
[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."
return
}
if ($Type -eq 'Dacpac') {
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'
}
} elseif ($Type -eq 'Bacpac') {
if ($ScriptOnly -or $GenerateDeploymentReport -or $GenerateDeploymentScript) {
Stop-Function -Message "ScriptOnly, GenerateDeploymentScript, and GenerateDeploymentReport cannot be used in a Bacpac scenario." -ErrorRecord $_
return
}
$defaultcolumns = 'ComputerName', 'InstanceName', 'SqlInstance', 'Database', 'Bacpac', 'Result', 'DeployOptions'
}
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"
return
}
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') -and (-not $script:core)) {
$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
return
} 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 $_
}
}
}
#Check Option object types - should have a specific type
if ($Type -eq 'Dacpac') {
if ($DacOption -and $DacOption -isnot [Microsoft.SqlServer.Dac.PublishOptions]) {
Stop-Function -Message "Microsoft.SqlServer.Dac.PublishOptions object type is expected - got $($DacOption.GetType())."
return
}
} elseif ($Type -eq 'Bacpac') {
if ($DacOption -and $DacOption -isnot [Microsoft.SqlServer.Dac.DacImportOptions]) {
Stop-Function -Message "Microsoft.SqlServer.Dac.DacImportOptions object type is expected - got $($DacOption.GetType())."
return
}
}
}
process {
if (Test-FunctionInterrupt) { return }
if (-not (Test-Path -Path $Path)) {
Stop-Function -Message "$Path not found!"
return
}
if ($PsCmdlet.ParameterSetName -eq 'Xml') {
if (-not (Test-Path -Path $PublishXml)) {
Stop-Function -Message "$PublishXml not found!"
return
}
}
foreach ($instance in $sqlinstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
} catch {
Stop-Function -Message "Failure." -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$ConnectionString += $server.ConnectionContext.ConnectionString.Replace('"', "'")
}
#Use proper class to load the object
if ($Type -eq 'Dacpac') {
try {
$dacPackage = [Microsoft.SqlServer.Dac.DacPackage]::Load($Path)
} catch {
Stop-Function -Message "Could not load Dacpac." -ErrorRecord $_
return
}
} elseif ($Type -eq 'Bacpac') {
try {
$bacPackage = [Microsoft.SqlServer.Dac.BacPackage]::Load($Path)
} catch {
Stop-Function -Message "Could not load Bacpac." -ErrorRecord $_
return
}
}
#Load XML profile when used
if ($PsCmdlet.ParameterSetName -eq 'Xml') {
try {
$options = New-DbaDacOption -Type $Type -Action Publish -PublishXml $PublishXml -EnableException
} catch {
Stop-Function -Message "Could not load profile." -ErrorRecord $_
return
}
}
#Create/re-use deployment options object
elseif ($PsCmdlet.ParameterSetName -eq 'Obj') {
if (!$DacOption) {
$options = New-DbaDacOption -Type $Type -Action Publish
} else {
$options = $DacOption
}
}
#Replace variables if defined
if ($IncludeSqlCmdVars) {
Get-SqlCmdVars -SqlCommandVariableValues $options.DeployOptions.SqlCommandVariableValues
}
foreach ($connstring in $ConnectionString) {
$cleaninstance = Get-ServerName $connstring
$instance = $cleaninstance.ToString().Replace('--', '\')
foreach ($dbname in $database) {
#Set deployment properties when specified
if (Test-Bound -ParameterName GenerateDeploymentScript) {
$options.GenerateDeploymentScript = $GenerateDeploymentScript
}
if (Test-Bound -ParameterName GenerateDeploymentReport) {
$options.GenerateDeploymentReport = $GenerateDeploymentReport
}
#Set output file paths when needed
$timeStamp = (Get-Date).ToString("yyMMdd_HHmmss_f")
if ($GenerateDeploymentScript) {
$options.DatabaseScriptPath = Join-Path $OutputPath "$cleaninstance-$dbname`_DeployScript_$timeStamp.sql"
$options.MasterDbScriptPath = Join-Path $OutputPath "$cleaninstance-$dbname`_Master.DeployScript_$timeStamp.sql"
}
if ($connstring -notmatch 'Database=') {
$connstring = "$connstring;Database=$dbname"
}
#Create services object
try {
$dacServices = New-Object Microsoft.SqlServer.Dac.DacServices $connstring
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $server -Continue
}
try {
$null = $output = Register-ObjectEvent -InputObject $dacServices -EventName "Message" -SourceIdentifier "msg" -Action { $EventArgs.Message.Message }
#Perform proper action depending on the Type
if ($Type -eq 'Dacpac') {
if ($ScriptOnly) {
if (!$options.GenerateDeploymentScript) {
Stop-Function -Message "GenerateDeploymentScript option should be specified when running with -ScriptOnly" -EnableException $true
}
if (!$options.DatabaseScriptPath) {
Stop-Function -Message "DatabaseScriptPath option should be specified when running with -ScriptOnly" -EnableException $true
}
Write-Message -Level Verbose -Message "Generating script."
$result = $dacServices.Script($dacPackage, $dbname, $options)
} else {
Write-Message -Level Verbose -Message "Executing Dacpac publish."
$result = $dacServices.Publish($dacPackage, $dbname, $options)
}
} elseif ($Type -eq 'Bacpac') {
Write-Message -Level Verbose -Message "Executing Bacpac import."
$dacServices.ImportBacpac($bacPackage, $dbname, $options, $null)
}
} catch [Microsoft.SqlServer.Dac.DacServicesException] {
Stop-Function -Message "Deployment failed" -ErrorRecord $_ -Continue
} finally {
Unregister-Event -SourceIdentifier "msg"
if ($options.GenerateDeploymentReport) {
$deploymentReport = Join-Path $OutputPath "$cleaninstance-$dbname`_Result.DeploymentReport_$timeStamp.xml"
$result.DeploymentReport | Out-File $deploymentReport
Write-Message -Level Verbose -Message "Deployment Report - $deploymentReport."
}
if ($options.GenerateDeploymentScript) {
Write-Message -Level Verbose -Message "Database change script - $($options.DatabaseScriptPath)."
if ((Test-Path $options.MasterDbScriptPath)) {
Write-Message -Level Verbose -Message "Master database change script - $($result.MasterDbScript)."
}
}
$resultoutput = ($output.output -join "`r`n" | Out-String).Trim()
if ($resultoutput -match "Failed" -and ($options.GenerateDeploymentReport -or $options.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
if ($Type -eq 'Dacpac') {
$output = [pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.InstanceName
SqlInstance = $server.FullName
Database = $dbname
Result = $resultoutput
Dacpac = $Path
PublishXml = $PublishXml
ConnectionString = $connstring
DatabaseScriptPath = $options.DatabaseScriptPath
MasterDbScriptPath = $options.MasterDbScriptPath
DeploymentReport = $DeploymentReport
DeployOptions = $options.DeployOptions | Select-Object -Property * -ExcludeProperty "SqlCommandVariableValues"
SqlCmdVariableValues = $options.DeployOptions.SqlCommandVariableValues.Keys
}
} elseif ($Type -eq 'Bacpac') {
$output = [pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.InstanceName
SqlInstance = $server.FullName
Database = $dbname
Result = $resultoutput
Bacpac = $Path
ConnectionString = $connstring
DeployOptions = $options
}
}
$output | Select-DefaultView -Property $defaultcolumns
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Publish-DbaDacpac
}
}
function Read-DbaAuditFile {
<#
.SYNOPSIS
Read Audit details from *.sqlaudit files.
.DESCRIPTION
Read Audit details from *.sqlaudit files.
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Read-DbaAuditFile
.EXAMPLE
PS C:\> Read-DbaAuditFile -Path C:\temp\logins.sqlaudit
Returns events from C:\temp\logins.sqlaudit.
.EXAMPLE
PS C:\> Get-ChildItem C:\temp\audit\*.sqlaudit | Read-DbaAuditFile
Returns events from all .sqlaudit files in C:\temp\audit.
.EXAMPLE
PS C:\> 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
#Variable marked as unused by PSScriptAnalyzer
#$manualadd = $true
} elseif ($file -is [System.IO.FileInfo]) {
$currentfile = $file.FullName
#Variable marked as unused by PSScriptAnalyzer
#$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 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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Read-DbaBackupHeader
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> $backupinfo = Read-DbaBackupHeader -SqlInstance . -Path C:\temp\myfile.bak
PS C:\> $backupinfo.FileList
Displays detailed information about each of the datafiles contained in the backupset.
.EXAMPLE
PS C:\> Read-DbaBackupHeader -SqlInstance . -Path C:\temp\myfile.bak -FileList
Also returns detailed information about each of the datafiles contained in the backupset.
.EXAMPLE
PS C:\> "C:\temp\myfile.bak", "\backupserver\backups\myotherfile.bak" | Read-DbaBackupHeader -SqlInstance sql2016 | Where-Object { $_.BackupSize.Megabyte -gt 100 }
Reads the two files and returns only backups larger than 100 MB
.EXAMPLE
PS C:\> 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
PS C:\> 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', Justification = "For Parameter AzureCredential")]
[CmdletBinding()]
param (
[parameter(Mandatory)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstance]$SqlInstance,
[PsCredential]$SqlCredential,
[parameter(Mandatory, ValueFromPipeline)]
[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])
$null = $dataTable.Columns.Add("SqlVersion")
$null = $dataTable.Columns.Add("BackupPath")
foreach ($row in $dataTable) {
$row.BackupPath = $Path
$backupsize = $row.BackupSize
$null = $dataTable.Columns.Remove("BackupSize")
$null = $dataTable.Columns.Add("BackupSize", [dbasize])
if ($backupsize -isnot [dbnull]) {
$row.BackupSize = [dbasize]$backupsize
}
$cbackupsize = $row.CompressedBackupSize
$null = $dataTable.Columns.Remove("CompressedBackupSize")
$null = $dataTable.Columns.Add("CompressedBackupSize", [dbasize])
if ($cbackupsize -isnot [dbnull]) {
$row.CompressedBackupSize = [dbasize]$cbackupsize
}
$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-DbaPath -SqlInstance $server -Path $pathGroup
#Setup initial session state
$InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$defaultrunspace = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace
#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, BackupSize, CompressedBackupSize, DatabaseCreationDate, UserName, ServerName, SqlVersion, BackupPath
} elseif ($FileList) {
$dataTable.filelist
} else {
$dataTable
}
$thread.thread.Dispose()
}
}
Start-Sleep -Milliseconds 500
}
#Close the runspace pool
$runspacePool.Close()
[System.Management.Automation.Runspaces.Runspace]::DefaultRunspace = $defaultrunspace
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 or instances.
.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 ErrorId
Search for results only with specific Errors. Filters 'Error in ($ErrorId)' 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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[]]$ErrorId,
[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 $ErrorId) {
$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 ($ErrorId) {
$where = $ErrorId -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-DbaPath -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
The target SQL Server instance or instances
.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 RowLimit
Will limit the number of rows returned from the transaction log
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> $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
PS C:\> $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 recommendation of not returning more that 0.5GB of log
#>
[CmdletBinding(DefaultParameterSetName = "Default")]
param (
[parameter(Position = 0, Mandatory)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(Mandatory)]
[object]$Database,
[Switch]$IgnoreLimit,
[int]$RowLimit = 0,
[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 ('Normal' -notin ($server.databases[$Database].Status -split ',')) {
Stop-Function -Message "$Database is not in a normal State, command will not run."
return
}
if ($RowLimit -gt 0) {
Write-Message -Message "Limiting results to $RowLimit rows" -Level Verbose
$RowLimitSql = " TOP $RowLimit "
$IgnoreLimit = $true
} else {
$RowLimitSql = ""
}
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 $RowLimitSql * from fn_dblog(NULL,NULL)"
Write-Message -Level Debug -Message $sql
Write-Message -Level Verbose -Message "Starting Log retrieval"
$server.Query($sql, $Database)
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 *.xem 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Read-DbaXEFile
.EXAMPLE
PS C:\> Read-DbaXEFile -Path C:\temp\deadocks.xel
Returns events from C:\temp\deadocks.xel.
.EXAMPLE
PS C:\> Get-ChildItem C:\temp\xe\*.xel | Read-DbaXEFile
Returns events from all .xel files in C:\temp\xe.
.EXAMPLE
PS C:\> 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
#Variable marked as unused by PSScriptAnalyzer
#$manualadd = $true
} elseif ($file -is [System.IO.FileInfo]) {
$currentfile = $file.FullName
#Variable marked as unused by PSScriptAnalyzer
#$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 $enum) {
$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
}
$enum.Dispose()
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAgDatabase {
<#
.SYNOPSIS
Removes a database from an availability group on a SQL Server instance.
.DESCRIPTION
Removes a database from an availability group on a SQL Server instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Database
The database or databases to remove.
.PARAMETER AvailabilityGroup
Only remove databases from specific availability groups.
.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 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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaAgDatabase
.EXAMPLE
PS C:\> Remove-DbaAgDatabase -SqlInstance sqlserver2012 -AvailabilityGroup ag1, ag2 -Confirm:$false
Removes the ag1 and ag2 availability groups on sqlserver2012. Does not prompt for confirmation.
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sqlserver2012 -AvailabilityGroup availabilitygroup1 | Remove-DbaAgDatabase
Removes the availability groups returned from the Get-DbaAvailabilityGroup function. Prompts for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[string[]]$AvailabilityGroup,
# needs to accept db or agdb so generic object
[parameter(ValueFromPipeline)]
[object[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance)) {
if ((Test-Bound -Not -ParameterName Database)) {
Stop-Function -Message "You must specify one or more databases and one or more Availability Groups when using the SqlInstance parameter."
return
}
}
if ($InputObject) {
if ($InputObject[0].GetType().Name -eq 'Database') {
$Database += $InputObject.Name
}
}
if ($SqlInstance) {
$InputObject += Get-DbaAgDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database
}
foreach ($db in $InputObject) {
if ($Pscmdlet.ShouldProcess($db.Parent.Parent.Name, "Removing availability group database $db")) {
try {
$ag = $db.Parent.Name
$db.Parent.AvailabilityDatabases[$db.Name].Drop()
[pscustomobject]@{
ComputerName = $db.ComputerName
InstanceName = $db.InstanceName
SqlInstance = $db.SqlInstance
AvailabilityGroup = $ag
Database = $db.Name
Status = "Removed"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAgentJob {
<#
.SYNOPSIS
Remove-DbaAgentJob removes a job.
.DESCRIPTION
Remove-DbaAgentJob removes a job in the SQL Server Agent.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 InputObject
Accepts piped input from Get-DbaAgentJob
.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
Author: Sander Stad (@sqlstad, sqlstad.nl)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaAgentJob
.EXAMPLE
PS C:\> Remove-DbaAgentJob -SqlInstance sql1 -Job Job1
Removes the job from the instance with the name Job1
.EXAMPLE
PS C:\> GetDbaAgentJob -SqlInstance sql1 -Job Job1 | Remove-DbaAgentJob -KeepHistory
Removes teh job but keeps the history
.EXAMPLE
PS C:\> Remove-DbaAgentJob -SqlInstance sql1 -Job Job1 -KeepUnusedSchedule
Removes the job but keeps the unused schedules
.EXAMPLE
PS C:\> Remove-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job Job1
Removes the job from multiple servers
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$Job,
[switch]$KeepHistory,
[switch]$KeepUnusedSchedule,
[DbaMode]$Mode = (Get-DbatoolsConfigValue -FullName 'message.mode.default' -Fallback "Strict"),
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Agent.Job[]]$InputObject,
[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
}
foreach ($j in $Job) {
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
}
}
}
$InputObject += ($Server.JobServer.Jobs | Where-Object Name -eq $j)
}
}
foreach ($currentJob in $InputObject) {
$j = $currentJob.Name
$server = $currentJob.Parent.Parent
if ($PSCmdlet.ShouldProcess($instance, "Removing the job $j from $server")) {
try {
$dropHistory = $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.Replace("'", "''"), $dropHistory, $dropSchedule)
$server.Databases['msdb'].ExecuteNonQuery($dropJobQuery)
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Name = $currentJob.Name
Status = 'Dropped'
}
} catch {
Write-Message -Level Verbose -Message "Could not drop job $job on $server"
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Name = $currentJob.Name
Status = "Failed. $(Get-ErrorMessage -Record $_)"
}
}
}
}
}
}
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
The target SQL Server instance or instances. 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
Tags: Agent, Job, JobCategory
Author: Sander Stad (@sqlstad, sqlstad.nl)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaAgentJobCategory
.EXAMPLE
PS C:\> Remove-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 1'
Remove the job category Category 1 from the instance.
.EXAMPLE
PS C:\> Remove-DbaAgentJobCategory -SqlInstance sql1 -Category Category1, Category2, Category3
Remove multiple job categories from the instance.
.EXAMPLE
PS C:\> Remove-DbaAgentJobCategory -SqlInstance sql1, sql2, sql3 -Category Category1, Category2, Category3
Remove multiple job categories from the multiple instances.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[ValidateNotNullOrEmpty()]
[string[]]$Category,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $sqlinstance) {
# Try connecting to the instance
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
The target SQL Server instance or instances. 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
Tags: Agent, Job, JobStep
Author: Sander Stad (@sqlstad), sqlstad.nl
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaAgentJobStep
.EXAMPLE
PS C:\> Remove-DbaAgentJobStep -SqlInstance sql1 -Job Job1 -StepName Step1
Remove 'Step1' from job 'Job1' on sql1.
.EXAMPLE
PS C:\> Remove-DbaAgentJobStep -SqlInstance sql1 -Job Job1, Job2, Job3 -StepName Step1
Remove the job step from multiple jobs.
.EXAMPLE
PS C:\> Remove-DbaAgentJobStep -SqlInstance sql1, sql2, sql3 -Job Job1 -StepName Step1
Remove the job step from the job on multiple servers.
.EXAMPLE
PS C:\> sql1, sql2, sql3 | Remove-DbaAgentJobStep -Job Job1 -StepName Step1
Remove the job step from the job on multiple servers using pipeline.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[object[]]$Job,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$StepName,
[DbaMode]$Mode = (Get-DbatoolsConfigValue -Name 'message.mode.default' -Fallback "Strict"),
[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
}
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 job in the SQL Server Agent.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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
Tags: Agent, Job, Schedule
Author: Sander Stad (@sqlstad), sqlstad.nl
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaAgentJobSchedule
.EXAMPLE
PS C:\> Remove-DbaAgentSchedule -SqlInstance sql1 -Schedule weekly
Remove the schedule weekly
.EXAMPLE
PS C:\> 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
PS C:\> Remove-DbaAgentSchedule -SqlInstance sql1 -Schedule daily, weekly
Remove multiple schedule
.EXAMPLE
PS C:\> 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, ConfirmImpact = "Low")]
param (
[parameter(Mandatory, ValueFromPipeline, ParameterSetName = "instance")]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[System.Management.Automation.PSCredential]$SqlCredential,
[Parameter(Mandatory, 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
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$InputObject += $server.JobServer.SharedSchedules
if ($Schedule) {
$InputObject = $InputObject | Where-Object Name -in $Schedule
}
} # foreach object instance
foreach ($currentschedule in $InputObject) {
$server = $currentschedule.Parent.Parent
if (-not $server) {
$server = $currentschedule.Parent
}
$server.JobServer.SharedSchedules.Refresh()
$schedulename = $currentschedule.Name
$jobCount = $server.JobServer.SharedSchedules[$currentschedule].JobCount
# Check if the schedule is shared among other jobs
if ($jobCount -ge 1 -and -not $Force) {
Stop-Function -Message "The schedule $schedulename 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 $currentschedule on $instance")) {
# Loop through each of the schedules and drop them
Write-Message -Message "Removing schedule $schedulename 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 $currentschedule.Name) }
Write-Message -Message "Schedule $schedulename 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 "Failure" -Target $instance -ErrorRecord $_ -Continue
}
}
}
}
}
}
Write-Message -Message "Removing schedules that are not being used by other jobs." -Level Verbose
$server.JobServer.SharedSchedules.Refresh()
# Get the schedules
$smoSchedules = $server.JobServer.SharedSchedules | Where-Object { ($_.Name -eq $currentschedule.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
}
}
}
}
}
end {
Write-Message -Message "Finished removing jobs schedule(s)." -Level Verbose
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAgListener {
<#
.SYNOPSIS
Removes a listener from an availability group on a SQL Server instance.
.DESCRIPTION
Removes a listener from an availability group on a SQL Server instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Listener
The listener or listeners to remove.
.PARAMETER AvailabilityGroup
Only remove listeners from specific availability groups.
.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 InputObject
Enables piping from Get-DbaListener
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaAgListener
.EXAMPLE
PS C:\> Remove-DbaAgListener -SqlInstance sqlserver2012 -AvailabilityGroup ag1, ag2 -Confirm:$false
Removes the ag1 and ag2 availability groups on sqlserver2012. Does not prompt for confirmation.
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sqlserver2012 -AvailabilityGroup availabilitygroup1 | Remove-DbaAgListener
Removes the listeners returned from the Get-DbaAvailabilityGroup function. Prompts for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Listener,
[string[]]$AvailabilityGroup,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroupListener[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance)) {
if ((Test-Bound -Not -ParameterName Listener)) {
Stop-Function -Message "You must specify one or more listeners and one or more Availability Groups when using the SqlInstance parameter."
return
}
}
if ($SqlInstance) {
$InputObject += Get-DbaAgListener -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Listener $Listener
}
foreach ($aglistener in $InputObject) {
if ($Pscmdlet.ShouldProcess($aglistener.Parent.Parent.Name, "Removing availability group listener $aglistener")) {
try {
$ag = $aglistener.Parent.Name
$aglistener.Parent.AvailabilityGroupListeners[$aglistener.Name].Drop()
[pscustomobject]@{
ComputerName = $aglistener.ComputerName
InstanceName = $aglistener.InstanceName
SqlInstance = $aglistener.SqlInstance
AvailabilityGroup = $ag
Listener = $aglistener.Name
Status = "Removed"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAgReplica {
<#
.SYNOPSIS
Removes availability group replicas from availability groups.
.DESCRIPTION
Removes availability group replicas from availability groups.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER AvailabilityGroup
The specific availability group to query.
.PARAMETER Replica
The replica to remove.
.PARAMETER InputObject
Enables piped input from Get-DbaAgReplica.
.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: AG, HA, AvailabilityGroup, Replica
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaAgReplica
.EXAMPLE
PS C:\> Remove-DbaAgReplica -SqlInstance sql2017a -AvailabilityGroup SharePoint -Replica sp1
Removes the sp1 replica from the SharePoint ag on sql2017a. Prompts for confirmation.
.EXAMPLE
PS C:\> Remove-DbaAgReplica -SqlInstance sql2017a | Select-Object *
Returns full object properties on all availability group replicas found on sql2017a
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[string[]]$Replica,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityReplica[]]$InputObject,
[switch]$EnableException
)
process {
if ($SqlInstance -and -not $Replica) {
Stop-Function -Message "You must specify a replica when using the SqlInstance parameter."
return
}
if ($SqlInstance) {
$InputObject += Get-DbaAgReplica -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Replica $Replica -AvailabilityGroup $AvailabilityGroup
}
foreach ($agreplica in $InputObject) {
if ($Pscmdlet.ShouldProcess($agreplica.Parent.Parent.Name, "Removing availability group replica $agreplica")) {
try {
$agreplica.Parent.AvailabilityGroupReplicas[$agreplica.Name].Drop()
[pscustomobject]@{
ComputerName = $agreplica.ComputerName
InstanceName = $agreplica.InstanceName
SqlInstance = $agreplica.SqlInstance
AvailabilityGroup = $agreplica.Parent.Name
Replica = $agreplica.Name
Status = "Removed"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaAvailabilityGroup {
<#
.SYNOPSIS
Removes availability groups on a SQL Server instance.
.DESCRIPTION
Removes availability groups on a SQL Server instance.
If possible, remove the availability group only while connected to the server instance that hosts the primary replica.
When the availability group is dropped from the primary replica, changes are allowed in the former primary databases (without high availability protection).
Deleting an availability group from a secondary replica leaves the primary replica in the RESTORING state, and changes are not allowed on the databases.
Avoid dropping an availability group when the Windows Server Failover Clustering (WSFC) cluster has no quorum.
If you must drop an availability group while the cluster lacks quorum, the metadata availability group that is stored in the cluster is not removed.
After the cluster regains quorum, you will need to drop the availability group again to remove it from the WSFC cluster.
For more information: https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-availability-group-transact-sql
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER AvailabilityGroup
Only remove specific availability groups.
.PARAMETER AllAvailabilityGroups
Remove all availability groups on an instance.
.PARAMETER InputObject
Enables piping from Get-DbaAvailabilityGroup.
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaAvailabilityGroup
.EXAMPLE
PS C:\> Remove-DbaAvailabilityGroup -SqlInstance sqlserver2012 -AllAvailabilityGroups
Removes all availability groups on the sqlserver2014 instance. Prompts for confirmation.
.EXAMPLE
PS C:\> Remove-DbaAvailabilityGroup -SqlInstance sqlserver2012 -AvailabilityGroup ag1, ag2 -Confirm:$false
Removes the ag1 and ag2 availability groups on sqlserver2012. Does not prompt for confirmation.
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sqlserver2012 -AvailabilityGroup availabilitygroup1 | Remove-DbaAvailabilityGroup
Removes the availability groups returned from the Get-DbaAvailabilityGroup function. Prompts for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[switch]$AllAvailabilityGroups,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName AvailabilityGroup, AllAvailabilityGroups)) {
Stop-Function -Message "You must specify AllAvailabilityGroups groups or AvailabilityGroups when using the SqlInstance parameter."
return
}
if ($SqlInstance) {
$InputObject += Get-DbaAvailabilityGroup -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup
}
foreach ($ag in $InputObject) {
if ($Pscmdlet.ShouldProcess($ag.Parent.Name, "Removing availability group $ag")) {
# avoid enumeration issues
try {
$ag.Parent.Query("DROP AVAILABILITY GROUP $ag")
[pscustomobject]@{
ComputerName = $ag.ComputerName
InstanceName = $ag.InstanceName
SqlInstance = $ag.SqlInstance
AvailabilityGroup = $ag.Name
Status = "Removed"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaBackup {
<#
.SYNOPSIS
Removes SQL Server backups from disk.
.DESCRIPTION
Removes SQL Server backups from disk.
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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaBackup
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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)]
param (
[parameter(Mandatory, HelpMessage = "Full path to the root level backup folder (ex. 'C:\SQL\Backups'")]
[Alias("BackupFolder")]
[string]$Path,
[parameter(Mandatory, HelpMessage = "Backup File extension to remove (ex. bak, trn, dif)")]
[string]$BackupFileExtension ,
[parameter(Mandatory, HelpMessage = "Backup retention period. (ex. 24h, 7d, 4w, 6m)")]
[string]$RetentionPeriod ,
[switch]$CheckArchiveBit = $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 alias for the specified server by altering HKLM:\SOFTWARE\Microsoft\MSSQLServer\Client - mimics cliconfg.exe.
.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 or array of aliases to be deleted
.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: Alias
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaClientAlias
.EXAMPLE
PS C:\> Remove-DbaClientAlias -ComputerName workstationx -Alias sqlps
Removes the sqlps SQL client alias on workstationx
.EXAMPLE
PS C:\> Get-DbaClientAlias | Remove-DbaClientAlias
Removes all SQL Server client aliases on the local computer
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(ValueFromPipelineByPropertyName = $true)]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[parameter(Mandatory, ValueFromPipelineByPropertyName = $true)]
[Alias('AliasName')]
[string[]]$Alias,
[Alias('Silent')]
[switch]$EnableException
)
begin {
$scriptblock = {
$Alias = $args
$basekeys = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\MSSQLServer", "HKLM:\SOFTWARE\Microsoft\MSSQLServer"
foreach ($basekey in $basekeys) {
$fullKey = "$basekey\Client\ConnectTo"
if ((Test-Path $fullKey) -eq $false) {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Warning "Registry key ($fullKey) does not exist. Quitting."
continue
}
if ($basekey -like "*WOW64*") {
$architecture = "32-bit"
} else {
$architecture = "64-bit"
}
$all = Get-Item -Path $fullKey
foreach ($entry in $all) {
$e = $entry.ToString().Replace('HKEY_LOCAL_MACHINE', 'HKLM:\')
foreach ($a in $Alias) {
if ($entry.Property -contains $a) {
Remove-ItemProperty -Path $e -Name $a
[PSCustomObject]@{
ComputerName = $computer
Architecture = $architecture
Alias = $a
Status = "Removed"
}
}
}
}
}
}
}
process {
foreach ($computer in $ComputerName) {
$null = Test-ElevationRequirement -ComputerName $computer -Continue
if ($PSCmdlet.ShouldProcess("$($Alias -join ', ') on $computer", "Remove aliases")) {
try {
Invoke-Command2 -ComputerName $computer -Credential $Credential -ScriptBlock $scriptblock -ErrorAction Stop -Verbose:$false -ArgumentList $Alias
} 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 target computer. Accepts both text as well as the output of Get-DbaCmConnection.
.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: ComputerManagement, CIM
Author: Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaCmConnection
.EXAMPLE
PS C:\> Remove-DbaCmConnection -ComputerName sql2014
Removes the cached connection to the server sql2014 from the cache.
.EXAMPLE
PS C:\> Get-DbaCmConnection | Remove-DbaCmConnection
Clears the entire connection cache.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[Parameter(ValueFromPipeline, Mandatory)]
[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 ($Pscmdlet.ShouldProcess($($connectionObject.Connection.ComputerName), "Removing Connection")) {
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"
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaCmsRegServer {
<#
.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
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 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-DbaCmsRegServer 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
Tags: RegisteredServer, CMS
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaCmsRegServer
.EXAMPLE
PS C:\> Remove-DbaCmsRegServer -SqlInstance sql2012 -Group HR, Accounting
Removes all servers from the HR and Accounting groups on sql2012
.EXAMPLE
PS C:\> Remove-DbaCmsRegServer -SqlInstance sql2012 -Group HR\Development
Removes all servers from the HR and sub-group Development from the CMS on sql2012.
.EXAMPLE
PS C:\> Remove-DbaCmsRegServer -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-DbaCmsRegServer -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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Remove-DbaRegisteredServer
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaCmsRegServerGroup {
<#
.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
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 Name
Specifies one or more groups to include from SQL Server Central Management Server.
.PARAMETER InputObject
Allows results from Get-DbaCmsRegServerGroup 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
Tags: RegisteredServer, CMS
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaCmsRegServerGroup
.EXAMPLE
PS C:\> Remove-DbaCmsRegServerGroup -SqlInstance sql2012 -Group HR, Accounting
Removes the HR and Accounting groups on sql2012
.EXAMPLE
PS C:\> Remove-DbaCmsRegServerGroup -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-DbaCmsRegServerGroup -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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Remove-DbaRegisteredServerGroup
}
}
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.
.NOTES
Tags: Certificate
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaComputerCertificate
.EXAMPLE
PS C:\> Remove-DbaComputerCertificate -ComputerName Server1 -Thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94
Removes certificate with thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 in the LocalMachine store on Server1
.EXAMPLE
PS C:\> Get-DbaComputerCertificate | Where-Object Thumbprint -eq E0A071E387396723C45E92D42B2D497C6A182340 | Remove-DbaComputerCertificate
Removes certificate using the pipeline
.EXAMPLE
PS C:\> Remove-DbaComputerCertificate -ComputerName Server1 -Thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 -Store User -Folder My
Removes certificate with thumbprint C2BBE81A94FEE7A26FFF86C2DFDAF6BFD28C6C94 in the User\My (Personal) store on Server1
#>
[CmdletBinding(SupportsShouldProcess, 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
)
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose "Searching Cert:\$Store\$Folder for thumbprint: $thumbprint"
function Get-CoreCertStore {
[CmdletBinding()]
param (
[ValidateSet("CurrentUser", "LocalMachine")]
[string]$Store,
[ValidateSet("AddressBook", "AuthRoot, CertificateAuthority", "Disallowed", "My", "Root", "TrustedPeople", "TrustedPublisher")]
[string]$Folder,
[ValidateSet("ReadOnly", "ReadWrite")]
[string]$Flag = "ReadOnly"
)
$storename = [System.Security.Cryptography.X509Certificates.StoreLocation]::$Store
$foldername = [System.Security.Cryptography.X509Certificates.StoreName]::$Folder
$flags = [System.Security.Cryptography.X509Certificates.OpenFlags]::$Flag
$certstore = [System.Security.Cryptography.X509Certificates.X509Store]::New($foldername, $storename)
$certstore.Open($flags)
$certstore
}
function Get-CoreCertificate {
[CmdletBinding()]
param (
[ValidateSet("CurrentUser", "LocalMachine")]
[string]$Store,
[ValidateSet("AddressBook", "AuthRoot, CertificateAuthority", "Disallowed", "My", "Root", "TrustedPeople", "TrustedPublisher")]
[string]$Folder,
[ValidateSet("ReadOnly", "ReadWrite")]
[string]$Flag = "ReadOnly",
[string[]]$Thumbprint,
[System.Security.Cryptography.X509Certificates.X509Store[]]$InputObject
)
if (-not $InputObject) {
$InputObject += Get-CoreCertStore -Store $Store -Folder $Folder -Flag $Flag
}
$certs = ($InputObject).Certificates
if ($Thumbprint) {
$certs = $certs | Where-Object Thumbprint -in $Thumbprint
}
$certs
}
if ($Thumbprint) {
try {
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose "Searching Cert:\$Store\$Folder"
$cert = Get-CoreCertificate -Store $Store -Folder $Folder -Thumbprint $Thumbprint
} catch {
# don't care - there's a weird issue with remoting where an exception gets thrown for no apparent reason
# here to avoid an empty catch
$null = 1
}
}
if ($cert) {
$certstore = Get-CoreCertStore -Store $Store -Folder $Folder -Flag ReadWrite
$certstore.Remove($cert)
$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 target SQL Server instance or instances.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaDatabase
.EXAMPLE
PS C:\> Remove-DbaDatabase -SqlInstance sql2016 -Database containeddb
Prompts then removes the database containeddb on SQL Server sql2016
.EXAMPLE
PS C:\> Remove-DbaDatabase -SqlInstance sql2016 -Database containeddb, mydb
Prompts then removes the databases containeddb and mydb on SQL Server sql2016
.EXAMPLE
PS C:\> Remove-DbaDatabase -SqlInstance sql2016 -Database containeddb -Confirm:$false
Does not prompt and swiftly removes containeddb on SQL Server sql2016
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance server\instance -ExcludeSystem | Remove-DbaDatabase
Removes all the user databases from server\instance
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance server\instance -ExcludeSystem | 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,
[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 {
$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")) {
$dbname = $db.Name
$db.Parent.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-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 target SQL Server instance or instances. 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 Destination 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaDatabaseSafely
.EXAMPLE
PS C:\> 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
PS C:\> $Database = 'DemoNCIndex','RemoveTestDatabase'
PS C:\> 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
PS C:\> Remove-DbaDatabaseSafely -SqlInstance 'Fade2Black' -Destination 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
PS C:\> Remove-DbaDatabaseSafely -SqlInstance IronMaiden -Database $Database -Destination TheWildHearts -BackupFolder Z:\Backups -NoDbccCheckDb -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
PS C:\> Remove-DbaDatabaseSafely -SqlInstance IronMaiden -Database $Database -Destination TheWildHearts -BackupFolder Z:\Backups
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, DefaultParameterSetName = "Default")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter]$SqlInstance,
[Alias("Credential")]
[PSCredential]
$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[DbaInstanceParameter]$Destination = $sqlinstance,
[PSCredential]
$DestinationCredential,
[Alias("NoCheck")]
[switch]$NoDbccCheckDb,
[parameter(Mandatory)]
[string]$BackupFolder,
[string]$CategoryName = 'Rationalisation',
[string]$JobOwner,
[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-DbaPath -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)]
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)]
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)]
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)]
[Alias('ServerInstance', 'SqlInstance', 'SqlServer')]
[object]$server,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$dbname,
[Parameter(Mandatory)]
[string]$backupfile,
[string]$filetype = 'Database',
[Parameter(Mandatory)]
[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-DbaDbBackupRestoreHistory {
<#
.SYNOPSIS
Reduces the size of the backup and restore history tables by deleting old entries for backup sets.
.DESCRIPTION
Reduces the size of the backup and restore history tables by deleting the entries for backup sets.
Can be used at server level, in this case a retention period -KeepDays can be set (default is 30 days).
Can also be used at database level, in this case the complete history for the database(s) is deleted.
The backup and restore history tables reside in the msdb database.
To periodically remove old data from backup and restore history tables it is recommended to schedule the agent job sp_delete_backuphistory from the
SQL Server Maintenance Solution created by Ola Hallengren (https://ola.hallengren.com).
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 KeepDays
The number of days of history to keep. Defaults to 30 days.
.PARAMETER Database
The database(s) to process. If unspecified, all databases will be processed.
.PARAMETER InputObject
Enables piped input from Get-DbaDatabase
.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
Author: IJeb Reitsma
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaDbBackupRestoreHistory
.EXAMPLE
PS C:\> Remove-DbaDbBackupRestoreHistory -SqlInstance sql2016
Prompts for confirmation then deletes backup and restore history on SQL Server sql2016 older than 30 days (default period)
PS C:\> Remove-DbaDbBackupRestoreHistory -SqlInstance sql2016 -KeepDays 100 -Confirm:$false
Remove backup and restore history on SQL Server sql2016 older than 100 days. Does not prompt for confirmation.
PS C:\> Remove-DbaDbBackupRestoreHistory -SqlInstance sql2016 -Database db1
Prompts for confirmation then deletes all backup and restore history for database db1 on SQL Server sql2016
PS C:\> Get-DbaDatabase -SqlInstance sql2016 | Remove-DbaDbBackupRestoreHistory -WhatIf
Remove complete backup and restore history for all databases on SQL Server sql2016
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[int]$KeepDays = 30,
[string[]]$Database,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
begin {
$odt = (Get-Date).AddDays(-$KeepDays)
}
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 $Database) {
try {
if ($Pscmdlet.ShouldProcess($server, "Remove backup/restore history before $($odt) for all databases")) {
# While this method is named DeleteBackupHistory, it also removes restore history
$server.DeleteBackupHistory($odt)
$server.Refresh()
}
} catch {
Stop-Function -Message "Could not remove backup/restore history on $server" -Continue
}
} else {
$InputObject += $server.Databases | Where-Object { $_.Name -in $Database }
}
}
foreach ($db in $InputObject) {
try {
$servername = $db.Parent.Name
if ($Pscmdlet.ShouldProcess("$db on $servername", "Remove complete backup/restore history")) {
# While this method is named DeleteBackupHistory, it also removes restore history
$db.DropBackupHistory()
$db.Refresh()
}
} catch {
Stop-Function -Message "Could not remove backup/restore history for database $db on $servername" -Continue
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Remove-DbaDbCertificate -SqlInstance Server1
The certificate in the master database on server1 will be removed if it exists.
.EXAMPLE
PS C:\> 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, ConfirmImpact = "High")]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[string[]]$Certificate,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Certificate[]]$InputObject,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Remove-DbaDatabaseCertificate
}
process {
if ($SqlInstance) {
$InputObject += Get-DbaDbCertificate -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Certificate $Certificate -Database $Database
}
foreach ($cert in $InputObject) {
$db = $cert.Parent
$server = $db.Parent
if ($Pscmdlet.ShouldProcess($server.Name, "Dropping the certificate named $cert for database $db")) {
try {
# erroractionprefs are not invoked for .net methods suddenly (??), so use Invoke-DbaQuery
# Avoids modifying the collection
Invoke-DbaQuery -SqlInstance $server -Database $db.Name -Query "DROP CERTIFICATE $cert" -EnableException
Write-Message -Level Verbose -Message "Successfully removed certificate named $cert from the $db database on $server"
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Database = $db.Name
Certificate = $cert.Name
Status = "Success"
}
} catch {
Stop-Function -Message "Failed to drop certificate named $($cert.Name) from $($db.Name) on $($server.Name)." -Target $smocert -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaDbMasterKey {
<#
.SYNOPSIS
Deletes specified database master key
.DESCRIPTION
Deletes specified database master key
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.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 InputObject
Enables pipeline input 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.
.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, Masterkey
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaMasterKey
.EXAMPLE
PS C:\> Remove-DbaDbMasterKey -SqlInstance sql2017, sql2016 -Database pubs
The master key in the pubs database on sql2017 and sql2016 will be removed if it exists.
.EXAMPLE
PS C:\> Remove-DbaDbMasterKey -SqlInstance sql2017 -Database db1 -Confirm:$false
Suppresses all prompts to remove the master key in the 'db1' database and drops the key.
.EXAMPLE
PS C:\> Get-DbaDbMasterKey -SqlInstance sql2017 -Database db1 | Remove-DbaDbMasterKey -Confirm:$false
Suppresses all prompts to remove the master key in the 'db1' database and drops the key.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess, ConfirmImpact = "High")]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[System.Management.Automation.PSCredential]$SqlCredential,
[string[]]$Database,
[string[]]$ExcludeDatabase,
[switch]$All,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.MasterKey[]]$InputObject,
[switch]$EnableException
)
process {
if ($SqlInstance) {
if (-not $Database -and -not $ExcludeDatabase -and -not $All) {
Stop-Function -Message "You must specify Database, ExcludeDatabase or All when using SqlInstance"
return
}
# all does not need to be addressed in the code because it gets all the dbs if $databases is empty
$databases = Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase
if ($databases) {
foreach ($key in $databases.MasterKey) {
$InputObject += $key
}
}
}
foreach ($masterkey in $InputObject) {
$server = $masterkey.Parent.Parent
$db = $masterkey.Parent
if ($Pscmdlet.ShouldProcess($server.Name, "Removing master key on $($db.Name)")) {
# avoid enumeration issues
try {
$masterkey.Parent.Query("DROP MASTER KEY")
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Database = $db.Name
Status = "Master key removed"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaDbMirror {
<#
.SYNOPSIS
Removes database mirrors.
.DESCRIPTION
Removes database mirrors. Does not set databases in recovery to recovered.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 target database.
.PARAMETER Partner
The partner fqdn.
.PARAMETER Witness
The witness fqdn.
.PARAMETER InputObject
Allows piping from Get-DbaDatabase.
.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: Mirror, HA
Author: Chrissy LeMaire (@cl), netnerds.net
dbatools PowerShell module (https://dbatools.io, [email protected])
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaDbMirror
.EXAMPLE
PS C:\> Set-DbaDbMirror -SqlInstance localhost
Returns all Endpoint(s) on the local default SQL Server instance
.EXAMPLE
PS C:\> Set-DbaDbMirror -SqlInstance localhost, sql2016
Returns all Endpoint(s) for the local and sql2016 SQL Server instances
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName Database)) {
Stop-Function -Message "Database is required when SqlInstance is specified"
return
}
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $instance -SqlCredential $SqlCredential -Database $Database
}
foreach ($db in $InputObject) {
if ($Pscmdlet.ShouldProcess($db.Parent.Name, "Turning off mirror for $db")) {
# use t-sql cuz $db.Alter() doesnt always work against restoring dbs
try {
try {
$db.ChangeMirroringState([Microsoft.SqlServer.Management.Smo.MirroringOption]::Off)
$db.Alter()
} catch {
try {
$db.Parent.Query("ALTER DATABASE $db SET PARTNER OFF")
} catch {
Stop-Function -Message "Failure on $($db.Parent) for $db" -ErrorRecord $_ -Continue
}
}
[pscustomobject]@{
ComputerName = $db.ComputerName
InstanceName = $db.InstanceName
SqlInstance = $db.SqlInstance
Database = $db.Name
Status = "Removed"
}
} catch {
Stop-Function -Message "Failure on $($db.Parent.Name)" -ErrorRecord $_
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaDbMirrorMonitor {
<#
.SYNOPSIS
Stops and deletes the mirroring monitor job for all the databases on the server instance.
.DESCRIPTION
Stops and deletes the mirroring monitor job for all the databases on the server instance.
Basically executes sp_dbmmonitordropmonitoring.
.PARAMETER SqlInstance
The target SQL Server instance
.PARAMETER SqlCredential
Login to the target instance using alternate Windows or SQL Login Authentication. 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: Mirror, HA, Monitor
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaDbMirrorMonitor
.EXAMPLE
PS C:\> Remove-DbaDbMirrorMonitor -SqlInstance sql2008, sql2012
Stops and deletes the mirroring monitor job for all the databases on sql2008 and sql2012.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[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
}
if ($Pscmdlet.ShouldProcess($instance, "Removing mirror monitoring")) {
try {
$server.Query("msdb.dbo.sp_dbmmonitordropmonitoring")
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
MonitorStatus = "Removed"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaDbSnapshot {
<#
.SYNOPSIS
Removes database snapshots
.DESCRIPTION
Removes (drops) database snapshots from the server
.PARAMETER SqlInstance
The target SQL Server instance or instances
.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: Simone Bizzotto (@niphold)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaDbSnapshot
.EXAMPLE
PS C:\> Remove-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snap_20161201, HR_snap_20161101
Removes database snapshots named HR_snap_20161201 and HR_snap_20161101
.EXAMPLE
PS C:\> Remove-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting
Removes all database snapshots having HR and Accounting as base dbs
.EXAMPLE
PS C:\> Get-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting | Remove-DbaDbSnapshot
Removes all database snapshots having HR and Accounting as base dbs
.EXAMPLE
PS C:\> Remove-DbaDbSnapshot -SqlInstance sql2014 -Snapshot HR_snapshot, Accounting_snapshot
Removes HR_snapshot and Accounting_snapshot
.EXAMPLE
PS C:\> Get-DbaDbSnapshot -SqlInstance sql2016 | Where SnapshotOf -like '*dumpsterfire*' | Remove-DbaDbSnapshot
Removes all snapshots associated with databases that have dumpsterfire in the name
.EXAMPLE
PS C:\> Get-DbaDbSnapshot -SqlInstance sql2016 | Out-GridView -Passthru | Remove-DbaDbSnapshot
Allows the selection of snapshots on sql2016 to remove
.EXAMPLE
PS C:\> Remove-DbaDbSnapshot -SqlInstance sql2014 -AllSnapshots
Removes all database snapshots from sql2014
.EXAMPLE
PS C:\> 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) {
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 target SQL Server instance or instances.
.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-DbaDbUser.
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaDbUser
.EXAMPLE
PS C:\> Remove-DbaDbUser -SqlInstance sqlserver2014 -User user1
Drops user1 from all databases it exists in on server 'sqlserver2014'.
.EXAMPLE
PS C:\> Remove-DbaDbUser -SqlInstance sqlserver2014 -Database database1 -User user1
Drops user1 from the database1 database on server 'sqlserver2014'.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaDbUser sqlserver2014 | Where-Object Name -In "user1" | Remove-DbaDbUser
Drops user1 from all databases it exists in on server 'sqlserver2014'.
#>
[CmdletBinding(DefaultParameterSetName = 'User', SupportsShouldProcess)]
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(SupportsShouldProcess)]
param ([Microsoft.SqlServer.Management.Smo.User[]]$users)
foreach ($user in $users) {
$db = $user.Parent
$server = $db.Parent
$ownedObjects = $false
$alterSchemas = @()
$dropSchemas = @()
Write-Message -Level Verbose -Message "Removing User $user from Database $db on target $server"
if ($Pscmdlet.ShouldProcess($user, "Removing user from Database $db")) {
# 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
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 {
$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-DbaDbUser -SqlInstance $server -Database $db.Name
$users = $users | Where-Object Name -In $User
Remove-DbUser $users
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaEndpoint {
<#
.SYNOPSIS
Removes endpoints from a SQL Server instance.
.DESCRIPTION
Removes endpoints from a SQL Server instance.
.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 Endpoint
Only remove specific endpoints.
.PARAMETER AllEndpoints
Remove all endpoints on an instance.
.PARAMETER InputObject
Enables piping from Get-Endpoint.
.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: Endpoint
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaEndpoint
.EXAMPLE
PS C:\> Remove-DbaEndpoint -SqlInstance sqlserver2012 -AllEndpoints
Removes all endpoints on the sqlserver2014 instance. Prompts for confirmation.
.EXAMPLE
PS C:\> Remove-DbaEndpoint -SqlInstance sqlserver2012 -Endpoint endpoint1,endpoint2 -Confirm:$false
Removes the endpoint1 and endpoint2 endpoints. Does not prompt for confirmation.
.EXAMPLE
PS C:\> Get-DbaEndpoint -SqlInstance sqlserver2012 -Endpoint endpoint1 | Remove-DbaEndpoint
Removes the endpoints returned from the Get-DbaEndpoint function. Prompts for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$EndPoint,
[switch]$AllEndpoints,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Endpoint[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName Endpoint, AllEndpoints)) {
Stop-Function -Message "You must specify AllEndpoints or Endpoint when using the SqlInstance parameter."
return
}
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaEndpoint -SqlInstance $instance -SqlCredential $SqlCredential -EndPoint $Endpoint
}
foreach ($ep in $InputObject) {
if ($Pscmdlet.ShouldProcess($ep.Parent.name, "Removing endpoint $ep")) {
try {
# avoid enumeration issues
$ep.Parent.Query("DROP ENDPOINT $ep")
[pscustomobject]@{
ComputerName = $ep.ComputerName
InstanceName = $ep.InstanceName
SqlInstance = $ep.SqlInstance
Endpoint = $ep.Name
Status = "Removed"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
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 target SQL Server instance or instances.
.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, Login
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaLogin
.EXAMPLE
PS C:\> Remove-DbaLogin -SqlInstance sql2016 -Login mylogin
Prompts then removes the Login mylogin on SQL Server sql2016
.EXAMPLE
PS C:\> Remove-DbaLogin -SqlInstance sql2016 -Login mylogin, yourlogin
Prompts then removes the Logins mylogin and yourlogin on SQL Server sql2016
.EXAMPLE
PS C:\> Remove-DbaLogin -SqlInstance sql2016 -Login mylogin -Confirm:$false
Does not prompt and swiftly removes mylogin on SQL Server sql2016
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance server\instance -Login yourlogin | Remove-DbaLogin
Removes mylogin on SQL Server server\instance
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = "Default")]
param (
[parameter(Mandatory, ParameterSetName = "instance")]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[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 {
$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 instance or instances. 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.
.NOTES
Tags: Certificate
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaNetworkCertificate
.EXAMPLE
PS C:\> Remove-DbaNetworkCertificate
Removes the Network Certificate for the default instance (MSSQLSERVER) on localhost
.EXAMPLE
PS C:\> Remove-DbaNetworkCertificate -SqlInstance sql1\SQL2008R2SP2
Removes the Network Certificate for the SQL2008R2SP2 instance on sql1
.EXAMPLE
PS C:\> Remove-DbaNetworkCertificate -SqlInstance localhost\SQL2008R2SP2 -WhatIf
Shows what would happen if the command were run
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low", DefaultParameterSetName = 'Default')]
param (
[Parameter(ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "ComputerName")]
[DbaInstanceParameter[]]$SqlInstance = $env:COMPUTERNAME,
[PSCredential]$Credential,
[switch]$EnableException
)
process {
# Registry access
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
}
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\$($regroot)\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 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 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) | Simone Bizzotto (@niphlod)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaOrphanUser
.EXAMPLE
PS C:\> Remove-DbaOrphanUser -SqlInstance sql2005
Finds and drops all orphan users without matching Logins in all databases present on server 'sql2005'.
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> Remove-DbaOrphanUser -SqlInstance sqlserver2014a -User OrphanUser
Removes user OrphanUser from all databases only if there is no matching login.
.EXAMPLE
PS C:\> 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)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]
$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[parameter(ValueFromPipeline)]
[object[]]$User,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($Instance in $SqlInstance) {
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 -ge 1) {
#the third validation will remove from list sql users without login or mapped to certificate. 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 -in @([Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin, [Microsoft.SqlServer.Management.Smo.LoginType]::Certificate)) -eq $false) }
} else {
#the fourth validation will remove from list sql users without login or mapped to certificate. 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 -in @([Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin, [Microsoft.SqlServer.Management.Smo.LoginType]::Certificate)) -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 a 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
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 the target computer 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaPfDataCollectorCounter
.EXAMPLE
PS C:\> 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
PS C:\> 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
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 the target computer 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaPfDataCollectorSet
.EXAMPLE
PS C:\> Remove-DbaPfDataCollectorSet
Prompts for confirmation then removes all ready Collectors on localhost.
.EXAMPLE
PS C:\> Remove-DbaPfDataCollectorSet -ComputerName sql2017 -Confirm:$false
Attempts to remove all ready Collectors on localhost and does not prompt to confirm.
.EXAMPLE
PS C:\> Remove-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential ad\sqldba -CollectorSet 'System Correlation'
Prompts for confirmation then removes the 'System Correlation' Collector on sql2017 and sql2016 using alternative credentials.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Remove-DbaPfDataCollectorSet
Removes the 'System Correlation' Collector.
.EXAMPLE
PS C:\> 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 {
<# DO NOT use Write-Message as this is inside of a script block #>
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")) {
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#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.
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaSpn
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Remove-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -Credential ad\sqldba
Connects to Active Directory and removes a provided SPN to the given account. Uses alternative account to connect to AD.
.EXAMPLE
PS C:\> 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
PS C:\> Test-DbaSpn -ComputerName sql2005 | Where { $_.isSet -eq $true } | Remove-DbaSpn
Removes all set SPNs for sql2005 and the relative delegations
#>
[cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = "Default")]
param (
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[Alias("RequiredSPN")]
[string]$SPN,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[Alias("InstanceServiceAccount", "AccountName")]
[string]$ServiceAccount,
[Parameter(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
}
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 or instances.
.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 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: Security, Trace
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Remove-DbaTrace -SqlInstance sql2008
Stops and removes all traces on sql2008
.EXAMPLE
PS C:\> Remove-DbaTrace -SqlInstance sql2008 -Id 1
Stops and removes all trace with ID 1 on sql2008
.EXAMPLE
PS C:\> Get-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Remove-DbaTrace
Stops and removes selected traces on sql2008
#>
[CmdletBinding(SupportsShouldProcess)]
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"
if ($Pscmdlet.ShouldProcess($traceid, "Removing the trace flag")) {
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
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaXESession {
<#
.SYNOPSIS
Removes Extended Events sessions.
.DESCRIPTION
This script removes Extended Events sessions on a SQL Server instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Remove-DbaXESession
.EXAMPLE
PS C:\> Remove-DbaXESession -SqlInstance sql2012 -AllSessions
Removes all Extended Event Session on the sqlserver2014 instance.
.EXAMPLE
PS C:\> Remove-DbaXESession -SqlInstance sql2012 -Session xesession1,xesession2
Removes the xesession1 and xesession2 Extended Event sessions.
.EXAMPLE
PS C:\> Get-DbaXESession -SqlInstance sql2017 | Remove-DbaXESession -Confirm:$false
Removes all sessions from sql2017, bypassing prompts.
.EXAMPLE
PS C:\> Get-DbaXESession -SqlInstance sql2012 -Session xesession1 | Remove-DbaXESession
Removes the sessions returned from the Get-DbaXESession function.
#>
[CmdletBinding(DefaultParameterSetName = 'Session', SupportsShouldProcess, ConfirmImpact = 'High')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Internal functions are ignored")]
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(SupportsShouldProcess)]
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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Remove-DbaXESmartTarget {
<#
.SYNOPSIS
Removes XESmartTarget PowerShell jobs.
.DESCRIPTION
Removes XESmartTarget PowerShell jobs.
.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
Author: Chrissy LeMaire (@cl) | SmartTarget by Gianluca Sartori (@spaghettidba)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
https://github.com/spaghettidba/XESmartTarget/wiki
.LINK
https://dbatools.io/Remove-DbaXESmartTarget
.EXAMPLE
PS C:\> Get-DbaXESmartTarget | Remove-DbaXESmartTarget
Removes all XESmartTarget jobs.
.EXAMPLE
PS C:\> 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: Simone Bizzotto (@niphold)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Rename-DbaDatabase
.EXAMPLE
PS C:\> Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName HR2 -Preview | select *
Shows the detailed result set you'll get renaming the HR database to HR2 without doing anything
.EXAMPLE
PS C:\> Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName HR2
Renames the HR database to HR2
.EXAMPLE
PS C:\> 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
PS C:\> Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>"
Renames the HR database to dbatools_HR
.EXAMPLE
PS C:\> 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
PS C:\> Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -FileGroupName "dbatools_<FGN>"
Renames every FileGroup within HR to "dbatools_[the original FileGroup name]"
.EXAMPLE
PS C:\> 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]"
.EXAMPLE
PS C:\> Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -FileGroupName "dbatools_<DBN>_<FGN>"
PS C:\> Rename-DbaDatabase -SqlInstance sqlserver2014a -Database HR -DatabaseName "dbatools_<DBN>"
Renames the HR database to "dbatools_HR", then renames every FileGroup within to "dbatools_HR_[the original FileGroup name]"
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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)]
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) {
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-DbaDbState -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-DbaDbState -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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Rename-DbaLogin
.EXAMPLE
PS C:\>Rename-DbaLogin -SqlInstance localhost -Login DbaToolsUser -NewLogin captain
SQL Login Example
.EXAMPLE
PS C:\>Rename-DbaLogin -SqlInstance localhost -Login domain\oldname -NewLogin domain\newname
Change the windowsuser login name.
.EXAMPLE
PS C:\>Rename-DbaLogin -SqlInstance localhost -Login dbatoolsuser -NewLogin captain -WhatIf
WhatIf Example
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess)]
param (
[parameter(Mandatory)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(Mandatory)]
[string]$Login,
[parameter(Mandatory)]
[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
PreviousLogin = $Login
NewLogin = $NewLogin
Status = "Successful"
}
} catch {
$dbenums = $null
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Database = $null
PreviousLogin = $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
PreviousUser = $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
PreviousUser = $NewLogin
NewUser = $oldname
Status = "Failure to rename. Rolled back change."
}
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $NewLogin
}
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Repair-DbaDbMirror {
<#
.SYNOPSIS
Attempts to repair a suspended or paused mirroring database.
.DESCRIPTION
Attempts to repair a suspended mirroring database.
Restarts the endpoints then sets the partner to resume. See this article for more info:
http://www.sqlservercentral.com/blogs/vivekssqlnotes/2016/09/03/how-to-resume-suspended-database-mirroring-in-sql-server-/
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 target database.
.PARAMETER InputObject
Allows piping from Get-DbaDatabase.
.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: Mirror, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Repair-DbaDbMirror
.EXAMPLE
PS C:\> Repair-DbaDbMirror -SqlInstance sql2017 -Database pubs
Attempts to repair the mirrored but suspended pubs database on sql2017.
Restarts the endpoints then sets the partner to resume. Prompts for confirmation.
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2017 -Database pubs | Repair-DbaDbMirror -Confirm:$false
Attempts to repair the mirrored but suspended pubs database on sql2017.
Restarts the endpoints then sets the partner to resume. Does not prompt for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName Database)) {
Stop-Function -Message "Database is required when SqlInstance is specified"
return
}
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $instance -SqlCredential $SqlCredential -Database $Database
}
foreach ($db in $InputObject) {
try {
Get-DbaEndpoint -SqlInstance $db.Parent | Where-Object EndpointType -eq DatabaseMirroring | Stop-DbaEndPoint
Get-DbaEndpoint -SqlInstance $db.Parent | Where-Object EndpointType -eq DatabaseMirroring | Start-DbaEndPoint
$db | Set-DbaDbMirror -State Resume
if ($Pscmdlet.ShouldProcess("console", "displaying output")) {
$db
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
}
}
}
}
#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 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 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.
.NOTES
Tags: Orphan
Author: Claudio Silva (@ClaudioESSilva) | Simone Bizzotto (@niphlod)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Repair-DbaOrphanUser
.EXAMPLE
PS C:\> Repair-DbaOrphanUser -SqlInstance sql2005
Finds and repairs all orphan users of all databases present on server 'sql2005'
.EXAMPLE
PS C:\> 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
PS C:\> Repair-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1, db2
Finds and repairs all orphan users in both db1 and db2 databases.
.EXAMPLE
PS C:\> Repair-DbaOrphanUser -SqlInstance sqlserver2014a -Database db1 -Users OrphanUser
Finds and repairs user 'OrphanUser' in 'db1' database.
.EXAMPLE
PS C:\> Repair-DbaOrphanUser -SqlInstance sqlserver2014a -Users OrphanUser
Finds and repairs user 'OrphanUser' on all databases
.EXAMPLE
PS C:\> 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.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[parameter(ValueFromPipeline)]
[object[]]$Users,
[switch]$RemoveNotExisting,
[switch]$Force,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$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 -in @([Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin, [Microsoft.SqlServer.Management.Smo.LoginType]::Certificate)) -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 -in @([Microsoft.SqlServer.Management.Smo.LoginType]::SqlLogin, [Microsoft.SqlServer.Management.Smo.LoginType]::Certificate)) -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 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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Repair-DbaServerName
.EXAMPLE
PS C:\> Repair-DbaServerName -SqlInstance sql2014
Checks to see if the server name is updatable and changes the name with a number of prompts.
.EXAMPLE
PS C:\> 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
PS C:\> 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, ConfirmImpact = "High")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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) {
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 target SQL Server instance or instances. SQL Server must be 2005 and above, and can be a clustered or stand-alone instance.
.PARAMETER SqlCredential
Instead of using Login and SecurePassword, you can just pass in a credential object.
.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.
.NOTES
Tags: WSMan
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: Admin access to server (not SQL Services),
Remoting must be enabled and accessible if $instance is not local
.LINK
https://dbatools.io/Reset-DbaAdmin
.EXAMPLE
PS C:\> Reset-DbaAdmin -SqlInstance sqlcluster -SqlCredential sqladmin
Prompts for password, then resets the "sqladmin" account password on sqlcluster.
.EXAMPLE
PS C:\> Reset-DbaAdmin -SqlInstance sqlserver\sqlexpress -Login ad\administrator -Confirm:$false
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.
Does not prompt for a password since it is not a SQL login. Does not prompt for confirmation since -Confirm is set to $false.
.EXAMPLE
PS C:\> 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.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWMICmdlet", "", Justification = "Using Get-WmiObject for client backwards compatibilty")]
param (
[Parameter(Mandatory)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter]$SqlInstance,
[pscredential]$SqlCredential,
[string]$Login = "sa",
[SecureString]$SecurePassword,
[switch]$Force,
[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)]
[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)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter]$instance,
[string]$sql,
[switch]$EnableException
)
try {
$connstring = "Data Source=$instance;Integrated Security=True;Connect Timeout=20;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()
$true
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
}
}
#endregion Utility functions
if ($Force) {
$ConfirmPreference = "none"
}
if ($SqlCredential) {
$Login = $SqlCredential.UserName
$SecurePassword = $SqlCredential.Password
}
}
process {
foreach ($instance in $sqlinstance) {
$stepcounter = 0
$baseaddress = $instance.ComputerName
# Get hostname
if ($baseaddress.IsLocalHost) {
$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) {
Stop-Function -Continue -Message "Remote PowerShell access not enabled on on $instance 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 {
Stop-Function -Continue -ErrorRecord $_ -Message "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 {
Stop-Function -Continue -ErrorRecord $_ -Message "Could not resolve SqlServer IP or NetBIOS name"
}
Write-Message -Level Verbose -Message "Resolving NetBIOS name."
try {
# this is required otherwise, the ip is returned
$hostname = (Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName $ipaddr -ErrorAction Stop).PSComputerName
if ($null -eq $hostname) {
$hostname = (nbtstat -A $ipaddr | Where-Object {
$_ -match '\<00\> UNIQUE'
} | ForEach-Object {
$_.SubString(4, 14)
}).Trim()
}
} catch {
Stop-Function -Continue -ErrorRecord $_ -Message "Could not access remote WMI object. Check permissions and firewall."
}
}
# Setup remote session if server is not local
if (-not $instance.IsLocalHost) {
try {
$session = New-PSSession -ComputerName $hostname -ErrorAction Stop
} catch {
Stop-Function -Continue -ErrorRecord $_ -Message "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)
#Variable $sid marked as unused by PSScriptAnalyzer replace with $null to catch output
$null = $account.Translate([System.Security.Principal.SecurityIdentifier])
} else {
Invoke-Command -ErrorAction Stop -Session $session -ArgumentList $login -ScriptBlock {
$account = New-Object System.Security.Principal.NTAccount($args)
#Variable $sid marked as unused by PSScriptAnalyzer replace with $null to catch output
$null = $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 (-not $windowslogin -and -not $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 ($SecurePassword) {
$Password = $SecurePassword
}
# Get instance and service display name, then get services
$instancename = $null
$instancename = $instance.InstanceName
if (-not $instancename) {
$instancename = "MSSQLSERVER"
}
$displayName = "SQL Server ($instancename)"
try {
if ($hostname -eq $env:COMPUTERNAME) {
$instanceservices = Get-Service -ErrorAction Stop | Where-Object {
$_.DisplayName -like "*($instancename)*" -and $_.Status -eq "Running"
}
$sqlservice = Get-Service -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($instancename)"
} else {
$instanceservices = Get-Service -ComputerName $ipaddr -ErrorAction Stop | Where-Object {
$_.DisplayName -like "*($instancename)*" -and $_.Status -eq "Running"
}
$sqlservice = Get-Service -ComputerName $ipaddr -ErrorAction Stop | Where-Object DisplayName -eq "SQL Server ($instancename)"
}
} 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 $instance
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 $instance
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 $instance -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 ($instancename)"
}
}
if ($pscmdlet.ShouldProcess($baseaddress, "Stop $instance to restart in single-user mode")) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Stopping $instance to restart in single-user mode"
# 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 $instance
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 $instance
return
}
}
}
# /mReset-DbaAdmin Starts an instance of SQL Server in single-user mode and only allows this script to connect.
if ($pscmdlet.ShouldProcess($baseaddress, "Starting $instance in single-user mode")) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Starting $instance in single-user mode"
try {
if ($instance.IsLocalHost) {
$netstart = net start ""$displayname"" /mReset-DbaAdmin 2>&1
if ("$netstart" -notmatch "success") {
Stop-Function -Message "Restart failure" -Continue
}
} 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
}
}
if ($pscmdlet.ShouldProcess($baseaddress, "Testing $instance to ensure it's back up")) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Testing $instance to ensure it's back up"
try {
$null = Invoke-ResetSqlCmd -instance $instance -Sql "SELECT 1" -EnableException
} catch {
try {
Start-Sleep 3
$null = Invoke-ResetSqlCmd -instance $instance -Sql "SELECT 1" -EnableException
} 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.
if ($pscmdlet.ShouldProcess($instance, "Adding login $login if it doesn't exist")) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Adding login $login if it doesn't exist"
if ($windowslogin) {
$sql = "IF NOT EXISTS (SELECT name FROM master.sys.server_principals WHERE name = '$login')
BEGIN CREATE LOGIN [$login] FROM WINDOWS END"
if (-not (Invoke-ResetSqlCmd -instance $instance -Sql $sql)) {
Write-Message -Level Warning -Message "Couldn't create Windows 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 (-not (Invoke-ResetSqlCmd -instance $instance -Sql $sql)) {
Write-Message -Level Warning -Message "Couldn't create SQL login."
}
}
}
# If $login is a SQL Login, Mixed mode authentication is required.
if ($windowslogin -ne $true) {
if ($pscmdlet.ShouldProcess($instance, "Enabling mixed mode authentication for $login and ensuring account is unlocked")) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Enabling mixed mode authentication for $login and ensuring account is unlocked"
$sql = "EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode', REG_DWORD, 2"
if (-not (Invoke-ResetSqlCmd -instance $instance -Sql $sql)) {
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 (-not (Invoke-ResetSqlCmd -instance $instance -Sql $sql)) {
Write-Message -Level Warning -Message "Couldn't unlock account."
}
}
}
if ($pscmdlet.ShouldProcess($instance, "Enabling $login")) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Ensuring login is enabled"
$sql = "ALTER LOGIN [$login] ENABLE"
if (-not (Invoke-ResetSqlCmd -instance $instance -Sql $sql)) {
Write-Message -Level Warning -Message "Couldn't enable login."
}
}
if ($login -ne "sa") {
if ($pscmdlet.ShouldProcess($instance, "Ensuring $login exists within sysadmin role")) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Ensuring $login exists within sysadmin role"
$sql = "EXEC sp_addsrvrolemember '$login', 'sysadmin'"
if (-not (Invoke-ResetSqlCmd -instance $instance -Sql $sql)) {
Write-Message -Level Warning -Message "Couldn't add to sysadmin role."
}
}
}
if ($pscmdlet.ShouldProcess($instance, "Finished with login tasks. Restarting")) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Finished with login tasks. Restarting."
try {
Stop-Service -InputObject $sqlservice -Force -ErrorAction Stop
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 Stop
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
}
}
if ($pscmdlet.ShouldProcess($instance, "Logging in to get account information")) {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Logging in to get account information"
if ($securePassword) {
$cred = New-Object System.Management.Automation.PSCredential ($Login, $securePassword)
Get-DbaLogin -SqlInstance $instance -SqlCredential $cred -Login $Login
} elseif ($SqlCredential) {
Get-DbaLogin -SqlInstance $instance -SqlCredential $SqlCredential -Login $Login
} else {
try {
Get-DbaLogin -SqlInstance $instance -SqlCredential $SqlCredential -Login $Login -EnableException
} catch {
Stop-Function -Message "Password not supplied, tried logging in with Integrated authentication and it failed. Either way, $Login should work now on $instance." -Continue
}
}
}
}
}
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 target SQL Server instance or instances.
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) | Simone Bizzotto (@niphold)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Resolve-DbaNetworkName
.EXAMPLE
PS C:\> Resolve-DbaNetworkName -ComputerName ServerA
Returns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, DNSDomain, Domain, DNSHostEntry, FQDN, DNSHostEntry for ServerA
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaCmsRegServer -SqlInstance sql2014 | Resolve-DbaNetworkName
Returns a custom object displaying InputName, ComputerName, IPAddress, DNSHostName, Domain, FQDN for all SQL Servers returned by Get-DbaCmsRegServer
#>
[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 {
if (-not (Test-Windows -NoWarn)) {
Write-Message -Level Verbose -Message "Non-Windows client detected. Turbo (DNS resolution only) set to $true"
$Turbo = $true
}
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 -ErrorRecord $_
}
}
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 {
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
if ($ipaddress) {
$Computer = "$Computer.$env:USERDNSDOMAIN"
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"
Resolve-DbaNetworkName -ComputerName $Computer -Turbo
continue
}
}
} 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 -ComputerName $RemoteComputer -Credential $Credential -EnableException
$DNSSuffix = Invoke-Command2 -ComputerName $RemoteComputer -ScriptBlock $ScBlock -Credential $Credential -ErrorAction Stop
} else {
$conn = Get-DbaCmObject -ClassName win32_ComputerSystem -ComputerName $RemoteComputer -EnableException
$DNSSuffix = Invoke-Command2 -ComputerName $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" -ErrorRecord $_ -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" -ErrorRecord $_
}
$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-DbaService {
<#
.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 target SQL Server instance or 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-DbaService
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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, Instance, Restart
Author: Kirill Kravtsov (@nvarscar)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires Local Admin rights on destination computer(s).
.LINK
https://dbatools.io/Restart-DbaService
.EXAMPLE
PS C:\> Restart-DbaService -ComputerName sqlserver2014a
Restarts the SQL Server related services on computer sqlserver2014a.
.EXAMPLE
PS C:\> 'sql1','sql2','sql3'| Get-DbaService | Restart-DbaService
Gets the SQL Server related services on computers sql1, sql2 and sql3 and restarts them.
.EXAMPLE
PS C:\> Restart-DbaService -ComputerName sql1,sql2 -Instance MSSQLSERVER
Restarts the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.
.EXAMPLE
PS C:\> Restart-DbaService -ComputerName $MyServers -Type SSRS
Restarts the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.
.EXAMPLE
PS C:\> Restart-DbaService -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)]
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, Mandatory, ParameterSetName = "Service")]
[Alias("ServiceCollection")]
[object[]]$InputObject,
[int]$Timeout = 60,
[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-DbaService @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-DbaService
$serviceParams = @{
ComputerName = $service.ComputerName
InstanceName = $service.InstanceName
Type = 'Agent'
}
if ($Credential) { $serviceParams.Credential = $Credential }
if ($EnableException) { $serviceParams.Silent = $EnableException }
$processArray += @(Get-DbaService @serviceParams)
}
}
if ($processArray) {
if ($PSCmdlet.ShouldProcess("$ProcessArray", "Restarting Service")) {
$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."
}
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Restart-DbaSqlService
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Restore-DbaBackupFromDirectory {
<#
.SYNOPSIS
Please use `Get-ChildItem | Restore-DbaDatabase` instead. This command is no longer supported.
.DESCRIPTION
Please use `Get-ChildItem | Restore-DbaDatabase` instead. This command is no longer supported.
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Restore-SqlBackupFromDirectory
.EXAMPLE
PS C:\> 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)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter]$SqlInstance,
[parameter(Mandatory)]
[string]$Path,
[switch]$NoRecovery,
[Alias("ReuseFolderStructure")]
[switch]$ReuseSourceFolderStructure,
[PSCredential]$SqlCredential,
[switch]$Force
)
Write-Message -Level Warning -Message "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 backup files
.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 target SQL Server instance or instances.
.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 file structures 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 switch 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 using Storage Access Keys
If a backup path beginning http is passed in and this parameter is not specified then if a credential with a name matching the URL
.PARAMETER ReplaceDbNameInFile
If switch set and occurrence 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
.NOTES
Tags: DisasterRecovery, Backup, Restore
Author: Stuart Moore (@napalmgram), stuart-moore.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Restore-DbaDatabase
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> $RestoreTime = Get-Date('11:19 23/12/2016')
PS C:\> 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
PS C:\> $result = Restore-DbaDatabase -SqlInstance server1\instance1 -Path \\server2\backups -DestinationDataDirectory c:\restores -OutputScriptOnly
PS C:\> $result | 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
PS C:\> 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
PS C:\> 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
PS C:\> Restore-DbaDatabase -SqlInstance server1\instance1 -Path http://demo.blob.core.windows.net/backups/dbbackup.bak
Will attempt to restore the backups from http://demo.blob.core.windows.net/backups/dbbackup.bak if a SAS credential with the name http://demo.blob.core.windows.net/backups exists on server1\instance1
.EXAMPLE
PS C:\> $File = Get-ChildItem c:\backups, \\server1\backups -recurse
PS C:\> $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
PS C:\> $files = Get-ChildItem C:\dbatools\db1
PS C:\> $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
>>
PS C:\> #It's in standby so we can peek at it
PS C:\> Invoke-DbaQuery -SQLInstance server\instance1 -Query "select top 1 * from Restored.dbo.steps order by dt desc"
PS C:\> #Not quite there so let's roll on a bit:
PS C:\> $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
>>
PS C:\> Invoke-DbaQuery -SQLInstance server\instance1 -Query "select top 1 * from restored.dbo.steps order by dt desc"
PS C:\> 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
PS C:\> Restore-DbaDatabase -SqlInstance server\instance1 -Path c:\backups -DatabaseName example1 -NoRecovery
PS C:\> Restore-DbaDatabase -SqlInstance server\instance1 -Recover -DatabaseName example1
In this example we restore example1 database with no recovery, and then the second call is to set the database to recovery.
.EXAMPLE
PS C:\> Get-DbaBackupHistory - SqlInstance server\instance1 -Database ProdFinance -Last | Restore-DbaDatabase -PageRestore
PS C:\> $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
.EXAMPLE
PS C:\> $BackupHistory = Get-DbaBackupInformation -SqlInstance sql2005 -Path \\backups\sql2000\ProdDb
PS C:\> $BackupHistory | Restore-DbaDatabase -SqlInstance sql2000 -TrustDbBackupHistory
Due to SQL Server 2000 not returning all the backup headers we cannot restore directly. As this is an issues with the SQL engine all we can offer is the following workaround
This will use a SQL Server instance > 2000 to read the headers, and then pass them in to Restore-DbaDatabase as a BackupHistory object.
.EXAMPLE
PS C:\> Restore-DbaDatabase -SqlInstance server1\instance1 -Path "C:\Temp\devops_prod_full.bak" -DatabaseName "DevOps_DEV" -ReplaceDbNameInFile
PS C:\> Rename-DbaDatabase -SqlInstance server1\instance1 -Database "DevOps_DEV" -LogicalName "<DBN>_<FT>"
This will restore the database from the "C:\Temp\devops_prod_full.bak" file, with the new name "DevOps_DEV" and store the different physical files with the new name. It will use the system default configured data and log locations.
After the restore the logical names of the database files will be renamed with the "DevOps_DEV_ROWS" for MDF/NDF and "DevOps_DEV_LOG" for LDF
.EXAMPLE
PS C:\> $FileStructure = @{
>> 'database_data' = 'C:\Data\database_data.mdf'
>> 'database_log' = 'C:\Log\database_log.ldf'
>> }
>>
PS C:\> Restore-DbaDatabase -SqlInstance server1 -Path \\ServerName\ShareName\File -DatabaseName database -FileMapping $FileStructure
Restores 'database' to 'server1' and moves the files to new locations. The format for the $FileStructure HashTable is the file logical name as the Key, and the new location as the Value.
#>
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Restore")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "AzureCredential", Justification = "For Parameter AzureCredential")]
param (
[parameter(Mandatory)][Alias("ServerInstance", "SqlServer")][DbaInstanceParameter]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(Mandatory, ValueFromPipeline, ParameterSetName = "Restore")][parameter(Mandatory, ValueFromPipeline, ParameterSetName = "RestorePage")][object[]]$Path,
[parameter(ValueFromPipeline)][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")][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, ParameterSetName = "RestorePage")][object]$PageRestore,
[parameter(Mandatory, ParameterSetName = "RestorePage")][string]$PageRestoreTailFolder,
[int]$StatementTimeout = 0
)
begin {
Write-Message -Level InternalComment -Message "Starting"
Write-Message -Level Debug -Message "Parameters bound: $($PSBoundParameters.Keys -join ", ")"
#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-DbaPath -Path $StandbyDirectory -SqlInstance $RestoreInstance)) {
Stop-Function -Message "$SqlServer 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) {
Write-Message -Message "Called with continue, so assume we have an existing db in norecovery"
$ContinuePoints = Get-RestoreContinuableDatabase -SqlInstance $RestoreInstance
$LastRestoreType = Get-DbaDbRestoreHistory -SqlInstance $RestoreInstance -Last
}
if (!($PSBoundParameters.ContainsKey("DataBasename"))) {
$PipeDatabaseName = $true
}
}
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
if ($UseDestinationDefaultDirectories) {
$DefaultPath = (Get-DbaDefaultPath -SqlInstance $RestoreInstance)
$DestinationDataDirectory = $DefaultPath.Data
$DestinationLogDirectory = $DefaultPath.Log
}
$BackupHistory = @()
}
process {
if (Test-FunctionInterrupt) {
return
}
if ($RestoreInstance.VersionMajor -eq 8 -and $true -ne $TrustDbBackupHistory) {
foreach ($file in $Path) {
$bh = Get-DbaBackupInformation -SqlInstance $RestoreInstance -Path $file
$bound = $PSBoundParameters
$bound['TrustDbBackupHistory'] = $true
$bound['Path'] = $bh
Restore-Dbadatabase @bound
}
break
}
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 ("BackupSetGUID" -notin $f.PSobject.Properties.Name) {
$f = $f | Select-Object *, @{
Name = "BackupSetGUID"; Expression = {
$_.BackupSetID
}
}
}
if ($f.BackupPath -like 'http*') {
if ('' -ne $AzureCredential) {
Write-Message -Message "At least one Azure backup passed in with a credential, assume correct" -Level Verbose
Write-Message -Message "Storage Account Identity access means striped backups cannot be restore"
} else {
$f.BackupPath -match 'https://.*/.*/'
if (Get-DbaCredential -SqlInstance $RestoreInstance -name $matches[0].trim('/') ) {
Write-Message -Message "We have a SAS credential to use with $($f.BackupPath)" -Level Verbose
} else {
Stop-Function -Message "A URL to a backup has been passed in, but no credential can be found to access it"
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 ';')"
if ($BackupHistory.GetType().ToString() -eq 'Sqlcollaborative.Dbatools.Database.BackupHistory') {
$BackupHistory = @($BackupHistory)
}
$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-DbaPath -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-Message -Level Verbose -Message "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
}
$pathSep = Get-DbaPathSep -Server $RestoreInstance
$null = $BackupHistory | Format-DbaBackupInformation -DataFileDirectory $DestinationDataDirectory -LogFileDirectory $DestinationLogDirectory -DestinationFileStreamDirectory $DestinationFileStreamDirectory -DatabaseFileSuffix $DestinationFileSuffix -DatabaseFilePrefix $DestinationFilePrefix -DatabaseNamePrefix $RestoredDatabaseNamePrefix -ReplaceDatabaseName $DatabaseName -Continue:$Continue -ReplaceDbNameInFile:$ReplaceDbNameInFile -FileMapping $FileMapping -PathSep $pathSep
if (Test-Bound -ParameterName FormatBackupInformation) {
Set-Variable -Name $FormatBackupInformation -Value $BackupHistory -Scope Global
}
if ($StopAfterFormatBackupInformation) {
return
}
$FilteredBackupHistory = $BackupHistory | Select-DbaBackupInformation -RestoreTime $RestoreTime -IgnoreLogs:$IgnoreLogBackups -ContinuePoints $ContinuePoints -LastRestoreType $LastRestoreType -DatabaseName $DatabaseName
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
}
try {
Write-Message -Level Verbose -Message "VerifyOnly = $VerifyOnly"
$null = $FilteredBackupHistory | Test-DbaBackupInformation -SqlInstance $RestoreInstance -WithReplace:$WithReplace -Continue:$Continue -VerifyOnly:$VerifyOnly -EnableException:$true -OutputScriptOnly:$OutputScriptOnly
} 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
}
}
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 ($RestoreInstance.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 -DatabaseName $DatabaseName -OutputScriptOnly:$OutputScriptOnly
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Restore-DbaDbCertificate {
<#
.SYNOPSIS
Imports certificates from .cer files using SMO.
.DESCRIPTION
Imports certificates from.cer files using SMO.
.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 Path
The Path the contains the certificate and private key files. The path can be a directory or a specific certificate.
.PARAMETER SecurePassword
Secure string used to decrypt the private key.
.PARAMETER EncryptionPassword
If specified this will be used to encrypt 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), jesspomfret.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Restore-DbaDbCertificate
.EXAMPLE
PS C:\> Restore-DbaDbCertificate -SqlInstance Server1 -Path \\Server1\Certificates -SecurePassword (ConvertTo-SecureString -Force -AsPlainText GoodPass1234!!)
Restores all the certificates in the specified path, password is used to both decrypt and encrypt the private key.
.EXAMPLE
PS C:\> Restore-DbaDbCertificate -SqlInstance Server1 -Path \\Server1\Certificates\DatabaseTDE.cer -SecurePassword (ConvertTo-SecureString -force -AsPlainText GoodPass1234!!)
Restores the DatabaseTDE certificate to Server1 and uses the MasterKey to encrypt the private key.
#>
[CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess, ConfirmImpact = "High")]
param (
[Parameter(Mandatory)]
[DbaInstanceParameter]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(Mandatory, ValueFromPipeline)]
[Alias("FullName")]
[object[]]$Path,
[Security.SecureString]$EncryptionPassword,
[string]$Database = "master",
[Alias("Password")]
[Security.SecureString]$SecurePassword = (Read-Host "Password" -AsSecureString),
[switch]$EnableException
)
process {
try {
$server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $sqlcredential
} catch {
Stop-Function -Message "Failed to connect to: $SqlInstance" -Target $SqlInstance -ErrorRecord $_
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-DbaPath -SqlInstance $server -Path $fullname)) {
Stop-Function -Message "$SqlInstance cannot access $fullname" -Continue -Target $fullname
}
$directory = Split-Path $fullname
$filename = Split-Path $fullname -Leaf
$certname = [io.path]::GetFileNameWithoutExtension($filename)
if ($Pscmdlet.ShouldProcess("$certname 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 = $true
if ($EncryptionPassword) {
$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)))
} else {
$smocert.Create($fullcertname, $fromfile, $privatekey, [System.Runtime.InteropServices.marshal]::PtrToStringAuto([System.Runtime.InteropServices.marshal]::SecureStringToBSTR($password)))
}
$cert = $smocert
} catch {
Write-Message -Level Warning -Message $_ -ErrorRecord $_ -Target $instance
}
}
Get-DbaDbCertificate -SqlInstance $server -Database $Database -Certificate $cert.Name
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Retore-DbaDatabaseCertificate
}
}
#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 target SQL Server instance or instances
.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 snapshot, 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: Simone Bizzotto (@niphold)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Restore-DbaDbSnapshot
.EXAMPLE
PS C:\> Restore-DbaDbSnapshot -SqlInstance sql2014 -Database HR, Accounting
Restores HR and Accounting databases using the latest snapshot available
.EXAMPLE
PS C:\> Restore-DbaDbSnapshot -SqlInstance sql2014 -Database HR -Force
Restores HR database from latest snapshot and kills any active connections in the database on sql2014.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaDbSnapshot -SqlInstance sql2016 | Out-GridView -PassThru | Restore-DbaDbSnapshot
Allows the selection of snapshots on sql2016 to restore
.EXAMPLE
PS C:\> 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) {
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
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Resume-DbaAgDbDataMovement {
<#
.SYNOPSIS
Resumes data movement for an availability group database on a SQL Server instance.
.DESCRIPTION
Resumes data movement for an availability group database on a SQL Server instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Database
The database or databases to resume movement upon.
.PARAMETER AvailabilityGroup
The availability group where the database movement will be resumeed.
.PARAMETER InputObject
Enables piping from Get-DbaAgDatabase
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Resume-DbaAgDbDataMovement
.EXAMPLE
PS C:\> Resume-DbaAgDbDataMovement -SqlInstance sql2017a -AvailabilityGroup ag1 -Database db1, db2
Resumes data movement on db1 and db2 to ag1 on sql2017a. Prompts for confirmation.
.EXAMPLE
PS C:\> Get-DbaAgDatabase -SqlInstance sql2017a, sql2019 | Out-GridView -Passthru | Resume-DbaAgDbDataMovement -Confirm:$false
Resumes data movement on the selected availability group databases. Does not prompt for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string]$AvailabilityGroup,
[string[]]$Database,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityDatabase[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance)) {
if ((Test-Bound -Not -ParameterName Database) -and (Test-Bound -Not -ParameterName AvailabilityGroup)) {
Stop-Function -Message "You must specify one or more databases and one Availability Groups when using the SqlInstance parameter."
return
}
}
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaAgDatabase -SqlInstance $instance -SqlCredential $SqlCredential -Database $Database
}
foreach ($agdb in $InputObject) {
if ($Pscmdlet.ShouldProcess($ag.Parent.Name, "Seting availability group $db to $($db.Parent.Name)")) {
try {
$null = $agdb.ResumeDataMovement()
$agdb
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Revoke-DbaAgPermission {
<#
.SYNOPSIS
Revokes endpoint and availability group permissions to a login.
.DESCRIPTION
Revokes endpoint and availability group permissions to a login.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Login
The login or logins to modify.
.PARAMETER AvailabilityGroup
Only modify specific availability groups.
.PARAMETER Type
Specify type: Endpoint or AvailabilityGroup. Endpoint will modify the DatabaseMirror endpoint type.
.PARAMETER Permission
Revokes one or more permissions:
Alter
Connect
Control
CreateAnyDatabase
CreateSequence
Delete
Execute
Impersonate
Insert
Receive
References
Select
Send
TakeOwnership
Update
ViewChangeTracking
ViewDefinition
Connect is default.
.PARAMETER InputObject
Enables piping from Get-DbaLogin.
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Revoke-DbaAgPermission
.EXAMPLE
PS C:\> Revoke-DbaAgPermission -SqlInstance sql2017a -Type AvailabilityGroup -AvailabilityGroup SharePoint -Login ad\spservice -Permission CreateAnyDatabase
Removes CreateAnyDatabase permissions from ad\spservice on the SharePoint availability group on sql2017a. Does not prompt for confirmation.
.EXAMPLE
PS C:\> Revoke-DbaAgPermission -SqlInstance sql2017a -Type AvailabilityGroup -AvailabilityGroup ag1, ag2 -Login ad\spservice -Permission CreateAnyDatabase -Confirm
Removes CreateAnyDatabase permissions from ad\spservice on the ag1 and ag2 availability groups on sql2017a. Prompts for confirmation.
.EXAMPLE
PS C:\> Get-DbaLogin -SqlInstance sql2017a | Out-GridView -Passthru | Revoke-DbaAgPermission -Type EndPoint
Revokes the selected logins Connect permissions on the DatabaseMirroring endpoint for sql2017a.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Login,
[string[]]$AvailabilityGroup,
[parameter(Mandatory)]
[ValidateSet('Endpoint', 'AvailabilityGroup')]
[string[]]$Type,
[ValidateSet('Alter', 'Connect', 'Control', 'CreateAnyDatabase', 'CreateSequence', 'Delete', 'Execute', 'Impersonate', 'Insert', 'Receive', 'References', 'Select', 'Send', 'TakeOwnership', 'Update', 'ViewChangeTracking', 'ViewDefinition')]
[string[]]$Permission = "Connect",
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Login[]]$InputObject,
[switch]$EnableException
)
process {
if ($SqlInstance -and -not $Login -and -not $AvailabilityGroup) {
Stop-Function -Message "You must specify one or more logins when using the SqlInstance parameter."
return
}
if ($Type -contains "AvailabilityGroup" -and -not $AvailabilityGroup) {
Stop-Function -Message "You must specify at least one availability group when using the AvailabilityGroup type."
return
}
foreach ($instance in $SqlInstance) {
if ($perm -contains "CreateAnyDatabase") {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $sqlcredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
foreach ($ag in $AvailabilityGroup) {
try {
$server.Query("ALTER AVAILABILITY GROUP $ag GRANT CREATE ANY DATABASE")
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance
return
}
}
} elseif ($Login) {
$InputObject += Get-DbaLogin -SqlInstance $instance -SqlCredential $SqlCredential -Login $Login
foreach ($account in $Login) {
if ($account -notin $InputObject.Name) {
try {
$InputObject += New-DbaLogin -SqlInstance $server -Login $account -EnableException
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $instance
return
}
}
}
}
}
foreach ($account in $InputObject) {
$server = $account.Parent
if ($Type -contains "Endpoint") {
$server.Endpoints.Refresh()
$endpoint = $server.Endpoints | Where-Object EndpointType -eq DatabaseMirroring
if (-not $endpoint) {
Stop-Function -Message "DatabaseMirroring endpoint does not exist on $server" -Target $server -Continue
}
foreach ($perm in $Permission) {
if ($Pscmdlet.ShouldProcess($server.Name, "Revokeing $perm on $endpoint")) {
if ($perm -in 'CreateAnyDatabase') {
Stop-Function -Message "$perm not supported by endpoints" -Continue
}
try {
$bigperms = New-Object Microsoft.SqlServer.Management.Smo.ObjectPermissionSet([Microsoft.SqlServer.Management.Smo.ObjectPermission]::$perm)
$endpoint.Revoke($bigperms, $account.Name)
[pscustomobject]@{
ComputerName = $account.ComputerName
InstanceName = $account.InstanceName
SqlInstance = $account.SqlInstance
Name = $account.Name
Permission = $perm
Type = "Revoke"
Status = "Success"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $ag -Continue
}
}
}
}
if ($Type -contains "AvailabilityGroup") {
$ags = Get-DbaAvailabilityGroup -SqlInstance $account.Parent -AvailabilityGroup $AvailabilityGroup
foreach ($ag in $ags) {
foreach ($perm in $Permission) {
if ($perm -notin 'Alter', 'Control', 'TakeOwnership', 'ViewDefinition') {
Stop-Function -Message "$perm not supported by availability groups" -Continue
}
if ($Pscmdlet.ShouldProcess($server.Name, "Revokeing $perm on $ags")) {
try {
$bigperms = New-Object Microsoft.SqlServer.Management.Smo.ObjectPermissionSet([Microsoft.SqlServer.Management.Smo.ObjectPermission]::$perm)
$ag.Revoke($bigperms, $account.Name)
[pscustomobject]@{
ComputerName = $account.ComputerName
InstanceName = $account.InstanceName
SqlInstance = $account.SqlInstance
Name = $account.Name
Permission = $perm
Type = "Revoke"
Status = "Success"
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $ag -Continue
}
}
}
}
}
}
}
}
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
Tags: Diagnostic, DMV, Troubleshooting
Author: Andre Kamman (@AndreKamman), http://clouddba.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> 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 {
try {
$data = (Invoke-TlsWebRequest -uri $uri -ErrorAction Stop)
} catch {
(New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
$data = (Invoke-TlsWebRequest -uri $uri -ErrorAction Stop)
}
return $data
} catch {
Stop-Function -Message "Invoke-TlsWebRequest 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-TlsWebRequest -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 LastRestoreType
The Output of Get-DbaDbRestoreHistory -last
This is used to check the last type of backup to a database to see if a differential backup can be restored
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Select-DbaBackupInformation
.EXAMPLE
PS C:\> $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
PS C:\> $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
PS C:\> $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
PS C:\> $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
PS C:\> $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
PS C:\> $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
PS C:\> $Backups = Get-DbaBackupInformation -SqlInstance Server1 -Path \\server1\backups$
PS C:\> $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, ValueFromPipeline)]
[object]$BackupHistory,
[DateTime]$RestoreTime = (get-date).addmonths(1),
[switch]$IgnoreLogs,
[switch]$IgnoreDiffs,
[string[]]$DatabaseName,
[string[]]$ServerName,
[object]$ContinuePoints,
[object]$LastRestoreType,
[switch]$EnableException
)
begin {
$InternalHistory = @()
$IgnoreFull = $false
if ((Test-Bound -ParameterName ContinuePoints) -and $null -ne $ContinuePoints) {
Write-Message -Message "ContinuePoints provided so setting up for a continue" -Level Verbose
$IgnoreFull = $true
$Continue = $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
if ($continue -and $Databases.count -gt 1 -and $DatabaseName.count -gt 1) {
Stop-Function -Message "Cannot perform continuing restores on multiple databases with renames, exiting"
return
}
ForEach ($Database in $Databases) {
#Cope with restores renaming the db
# $database = the name of database in the backups being scanned
# $databasefilter = the name of the database the backups are being restore to/against
if ($null -ne $DatabaseName) {
$databasefilter = $DatabaseName
} else {
$databasefilter = $database
}
if ($true -eq $Continue) {
#Test if Database is in a continuing state and the LSN to continue from:
if ($Databasefilter -in ($ContinuePoints | Select-Object -Property Database).Database) {
Write-Message -Message "$Database in ContinuePoints, will attmept to continue" -Level verbose
$IgnoreFull = $True
#Check what the last backup restored was
if (($LastRestoreType | Where-Object {$_.Database -eq $Databasefilter}).RestoreType -eq 'log') {
#log Backup last restored, so diffs cannot be used
$IgnoreDiffs = $true
} else {
#Last restore was a diff or full, so can restore diffs or logs
$IgnoreDiffs = $false
}
} else {
Write-Message -Message "$Database not in ContinuePoints, will attmept normal restore" -Level Warning
}
}
$dbhistory = @()
$DatabaseHistory = $internalhistory | Where-Object {$_.Database -eq $Database}
#For a standard restore, work out the full backup
if ($false -eq $IgnoreFull) {
$Full = $DatabaseHistory | Where-Object {$_.Type -in ('Full', 'Database') -and $_.Start -le $RestoreTime} | Sort-Object -Property LastLsn -Descending | Select-Object -First 1
if ($full.Fullname) {
$full.Fullname = ($DatabaseHistory | Where-Object { $_.Type -in ('Full', 'Database') -and $_.BackupSetID -eq $Full.BackupSetID }).Fullname
} else {
Stop-Function -Message "Fullname property not found. This could mean that a full backup could not be found or the command must be re-run with the -Continue switch."
return
}
$dbHistory += $full
} elseif ($true -eq $IgnoreFull -and $false -eq $IgnoreDiffs) {
#Fake the Full backup
Write-Message -Message "Continuing, so setting a fake full backup from the existing database"
$Full = [PsCustomObject]@{
CheckpointLSN = ($ContinuePoints | Where-Object {$_.Database -eq $DatabaseFilter}).differential_base_lsn
}
}
if ($false -eq $IgnoreDiffs) {
Write-Message -Message "processing diffs" -Level Verbose
$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) {
if ($Diff.FullName) {
$Diff.FullName = ($DatabaseHistory | Where-Object { $_.Type -in ('Differential', 'Database Differential') -and $_.BackupSetID -eq $diff.BackupSetID }).Fullname
} else {
Stop-Function -Message "Fullname property not found. This could mean that a full backup could not be found or the command must be re-run with the -Continue switch."
return
}
$dbhistory += $Diff
}
}
#Sort out the LSN for the log restores
if ($null -ne ($dbHistory | Sort-Object -Property LastLsn -Descending | select-object -First 1).lastLsn) {
#We have history so use this
[bigint]$LogBaseLsn = ($dbHistory | Sort-Object -Property LastLsn -Descending | select-object -First 1).lastLsn.ToString()
$FirstRecoveryForkID = $Full.FirstRecoveryForkID
Write-Message -Level Verbose -Message "Found LogBaseLsn: $LogBaseLsn and FirstRecoveryForkID: $FirstRecoveryForkID"
} else {
Write-Message -Message "No full or diff, so attempting to pull from Continue informmation" -Level Verbose
try {
[bigint]$LogBaseLsn = ($ContinuePoints | Where-Object {$_.Database -eq $DatabaseFilter}).redo_start_lsn
$FirstRecoveryForkID = ($ContinuePoints | Where-Object {$_.Database -eq $DatabaseFilter}).FirstRecoveryForkID
Write-Message -Level Verbose -Message "Found LogBaseLsn: $LogBaseLsn and FirstRecoveryForkID: $FirstRecoveryForkID from Continue information"
} catch {
Stop-Function -Message "Failed to find LSN or RecoveryForkID for $DatabaseFilter" -Category InvalidOperation -Target $DatabaseFilter
}
}
if ($true -eq $IgnoreFull -and $true -eq $IgnoreDiffs) {
#Set a Fake starting LSN
}
if ($false -eq $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
if ($Log.FullName) {
$Log.FullName = ($DatabaseHistory | Where-Object { $_.BackupSetID -eq $Group.group[0].BackupSetID }).Fullname
} else {
Stop-Function -Message "Fullname property not found. This could mean that a full backup could not be found or the command must be re-run with the -Continue switch."
return
}
#$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
}
}
}
#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
The target SQL Server instance or instances. 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
Tags: Agent, Alert
Author: Garry Bargsley (@gbargsley), garrybargsley.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaAgentAlert
.EXAMPLE
PS C:\> Set-DbaAgentAlert -SqlInstance sql1 -Alert 'Severity 025: Fatal Error' -Disabled
Changes the alert to disabled.
.EXAMPLE
PS C:\> Set-DbaAgentAlert -SqlInstance sql1 -Alert 'Severity 025: Fatal Error', 'Error Number 825', 'Error Number 824' -Enabled
Changes multiple alerts to enabled.
.EXAMPLE
PS C:\> 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
PS C:\> Set-DbaAgentAlert -SqlInstance sql1 -Alert 'Severity 025: Fatal Error' -Disabled -WhatIf
Doesn't Change the alert but shows what would happen.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$Alert,
[string]$NewName,
[switch]$Enabled,
[switch]$Disabled,
[switch]$Force,
[parameter(ValueFromPipeline)]
[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
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
}
}
}
}
#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
The target SQL Server instance or instances. 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
Tags: Agent, Job
Author: Sander Stad (@sqlstad), sqlstad.nl
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaAgentJob
.EXAMPLE
PS C:\> Set-DbaAgentJob sql1 -Job Job1 -Disabled
Changes the job to disabled
.EXAMPLE
PS C:\> Set-DbaAgentJob sql1 -Job Job1 -OwnerLogin user1
Changes the owner of the job
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Set-DbaAgentJob -SqlInstance sql1 -Job Job1, Job2, Job3 -Enabled
Changes multiple jobs to enabled
.EXAMPLE
PS C:\> Set-DbaAgentJob -SqlInstance sql1, sql2, sql3 -Job Job1, Job2, Job3 -Enabled
Changes multiple jobs to enabled on multiple servers
.EXAMPLE
PS C:\> Set-DbaAgentJob -SqlInstance sql1 -Job Job1 -Description 'Just another job' -Whatif
Doesn't Change the job but shows what would happen.
.EXAMPLE
PS C:\> 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
PS C:\> 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, 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)]
[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
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
The target SQL Server instance or instances. 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
Tags: Agent, Job, JobCategory
Author: Sander Stad (@sqlstad), sqlstad.nl
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaAgentJobCategory
.EXAMPLE
PS C:\> New-DbaAgentJobCategory -SqlInstance sql1 -Category 'Category 1' -NewName 'Category 2'
Change the name of the category from 'Category 1' to 'Category 2'.
.EXAMPLE
PS C:\> Set-DbaAgentJobCategory -SqlInstance sql1, sql2 -Category Category1, Category2 -NewName cat1, cat2
Rename multiple jobs in one go on multiple servers.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[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
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
}
Get-DbaAgentJobCategory -SqlInstance $server -Category $newCategoryName
} catch {
Stop-Function -Message "Something went wrong changing the job category $cat on $instance" -Target $cat -Continue -ErrorRecord $_
}
}
}
}
}
end {
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 target SQL Server instance or 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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Set-DbaAgentJobOutputFile -SqlInstance SERVERNAME -Job '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)]
param (
[Parameter(Mandatory, HelpMessage = 'The SQL Server Instance',
ValueFromPipeline,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false,
Position = 0)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Parameter(HelpMessage = 'SQL Credential',
ValueFromPipeline,
ValueFromPipelineByPropertyName = $true,
ValueFromRemainingArguments = $false)]
[PSCredential]$SqlCredential,
[object[]]$Job,
[Parameter(HelpMessage = 'The Job Step name',
ValueFromPipeline,
ValueFromPipelineByPropertyName = $true)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[object[]]$Step,
[Parameter(Mandatory, HelpMessage = 'The Full Output File Path',
ValueFromPipeline,
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
The target SQL Server instance or instances. 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
Tags: Agent, Job, JobStep
Author: Sander Stad (@sqlstad), sqlstad.nl
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaAgentJobStep
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Set-DbaAgentJobStep -SqlInstance sql1 -Job Job1, Job2 -StepName Step1 -Database msdb
Changes job steps in multiple jobs with the name Step1 to msdb
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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, ConfirmImpact = "Low")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[object[]]$Job,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$StepName,
[string]$NewName,
[ValidateSet('ActiveScripting', 'AnalysisCommand', 'AnalysisQuery', 'CmdExec', 'Distribution', 'LogReader', 'Merge', 'PowerShell', 'QueueReader', 'Snapshot', 'Ssis', 'TransactSql')]
[string]$Subsystem,
[string]$Command,
[int]$CmdExecSuccessCode,
[ValidateSet('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep')]
[string]$OnSuccessAction,
[int]$OnSuccessStepId,
[ValidateSet('QuitWithSuccess', 'QuitWithFailure', 'GoToNextStep', 'GoToStep')]
[string]$OnFailAction,
[int]$OnFailStepId,
[string]$Database,
[string]$DatabaseUser,
[int]$RetryAttempts,
[int]$RetryInterval,
[string]$OutputFileName,
[ValidateSet('AppendAllCmdExecOutputToJobHistory', 'AppendToJobHistory', 'AppendToLogFile', 'LogToTableWithOverwrite', 'None', 'ProvideStopProcessEvent')]
[string[]]$Flag,
[string]$ProxyName,
[Alias('Silent')]
[switch]$EnableException,
[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
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
The target SQL Server instance or instances. 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
Tags: Agent, Job, JobStep
Author: Sander Stad (@sqlstad, sqlstad.nl)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaAgentSchedule
.EXAMPLE
PS C:\> Set-DbaAgentSchedule -SqlInstance sql1 -Job Job1 -ScheduleName daily -Enabled
Changes the schedule for Job1 with the name 'daily' to enabled
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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, ConfirmImpact = "Low")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Parameter(Mandatory, ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[object[]]$Job,
[Parameter(Mandatory, ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[string]$ScheduleName,
[string]$NewName,
[switch]$Enabled,
[switch]$Disabled,
[ValidateSet(1, "Once", 4, "Daily", 8, "Weekly", 16, "Monthly", 32, "MonthlyRelative", 64, "AgentStart", 128, "IdleComputer")]
[object]$FrequencyType,
[object[]]$FrequencyInterval,
[ValidateSet(1, "Time", 2, "Seconds", 4, "Minutes", 8, "Hours")]
[object]$FrequencySubdayType,
[int]$FrequencySubdayInterval,
[ValidateSet('Unused', 'First', 'Second', 'Third', 'Fourth', 'Last')]
[object]$FrequencyRelativeInterval,
[int]$FrequencyRecurrenceFactor,
[string]$StartDate,
[string]$EndDate,
[string]$StartTime,
[string]$EndTime,
[Alias('Silent')]
[switch]$EnableException,
[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
}
#Format dates and times
if ($StartDate) {
$StartDate = $StartDate.Insert(6, '-').Insert(4, '-')
}
if ($EndDate) {
$EndDate = $EndDate.Insert(6, '-').Insert(4, '-')
}
if ($StartTime) {
$StartTime = $StartTime.Insert(4, ':').Insert(2, ':')
}
if ($EndTime) {
$EndTime = $EndTime.Insert(4, ':').Insert(2, ':')
}
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($instance in $sqlinstance) {
foreach ($j in $Job) {
# Try connecting to the instance
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) {
Write-Message -Message "Setting job schedule start date to $StartDate for schedule $ScheduleName" -Level Verbose
$JobSchedule.StartDate = $StartDate
}
if ($EndDate) {
Write-Message -Message "Setting job schedule end date to $EndDate for schedule $ScheduleName" -Level Verbose
$JobSchedule.EndDate = $EndDate
}
if ($StartTime) {
Write-Message -Message "Setting job schedule start time to $StartTime for schedule $ScheduleName" -Level Verbose
$JobSchedule.ActiveStartTimeOfDay = $StartTime
}
if ($EndTime) {
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-DbaAgentServer {
<#
.SYNOPSIS
Set-DbaAgentServer updates properties of a SQL Agent Server.
.DESCRIPTION
Set-DbaAgentServer updates properties in the SQL Server Server with parameters supplied.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 InputObject
Enables piping agent server objects
.PARAMETER AgentLogLevel
Specifies the agent log level.
Allowed values 1, "Errors", 2, "Warnings", 3, "Errors, Warnings", 4, "Informational", 5, "Errors, Informational", 6, "Warnings, Informational", 7, "All"
The text value can either be lowercase, uppercase or something in between as long as the text is correct.
.PARAMETER AgentMailType
Specifies the agent mail type.
Allowed values 0, "SqlAgentMail", 1, "DatabaseMail"
The text value can either be lowercase, uppercase or something in between as long as the text is correct.
.PARAMETER AgentShutdownWaitTime
The Agent Shutdown Wait Time value of the server agent.
.PARAMETER DatabaseMailProfile
The Database Mail Profile to be used. Must exists on database mail profiles.
.PARAMETER ErrorLogFile
Error log file location
.PARAMETER IdleCpuDuration
Idle CPU Duration value to be used
.PARAMETER IdleCpuPercentage
Idle CPU Percentage value to be used
.PARAMETER CpuPolling
Enable or Disable the Polling.
Allowed values Enabled, Disabled
.PARAMETER LocalHostAlias
The value for Local Host Alias configuration
.PARAMETER LoginTimeout
The value for Login Timeout configuration
.PARAMETER MaximumHistoryRows
Indicates the Maximum job history log size (in rows). If you want to turn it off use the value -1
.PARAMETER MaximumJobHistoryRows
Indicates the Maximum job history rows per job. If you want to turn it off use the value 0
.PARAMETER NetSendRecipient
The Net send recipient value
.PARAMETER ReplaceAlertTokens
Enable or Disable the Token replacement property.
Allowed values Enabled, Disabled
.PARAMETER SaveInSentFolder
Enable or Disable the copy of the sent messages is save in the Sent Items folder.
Allowed values Enabled, Disabled
.PARAMETER SqlAgentAutoStart
Enable or Disable the SQL Agent Auto Start.
Allowed values Enabled, Disabled
.PARAMETER SqlAgentMailProfile
The SQL Server Agent Mail Profile to be used. Must exists on database mail profiles.
.PARAMETER SqlAgentRestart
Enable or Disable the SQL Agent Restart.
Allowed values Enabled, Disabled
.PARAMETER SqlServerRestart
Enable or Disable the SQL Server Restart.
Allowed values Enabled, Disabled
.PARAMETER WriteOemErrorLog
Enable or Disable the Write OEM Error Log.
Allowed values Enabled, Disabled
.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, Server
Author: Cláudio Silva (@claudioessilva), https://claudioessilva.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaAgentServer
.EXAMPLE
PS C:\> Set-DbaAgentServer -SqlInstance sql1 -MaximumHistoryRows 10000 -MaximumJobHistoryRows 100
Changes the job history retention to 10000 rows with an maximum of 100 rows per job.
.EXAMPLE
PS C:\> Set-DbaAgentServer -SqlInstance sql1 -CpuPolling Enabled
Enable the CPU Polling configurations.
.EXAMPLE
PS C:\> Set-DbaAgentServer -SqlInstance sql1, sql2, sql3 -AgentLogLevel 'Errors, Warnings'
Set the agent log level to Errors and Warnings on multiple servers.
.EXAMPLE
PS C:\> Set-DbaAgentServer -SqlInstance sql1 -CpuPolling Disabled
Disable the CPU Polling configurations.
#>
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Low")]
param (
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Agent.JobServer[]]$InputObject,
[ValidateSet(1, "Errors", 2, "Warnings", 3, "Errors, Warnings", 4, "Informational", 5, "Errors, Informational", 6, "Warnings, Informational", 7, "All")]
[object]$AgentLogLevel,
[ValidateSet(0, "SqlAgentMail", 1, "DatabaseMail")]
[object]$AgentMailType,
[int]$AgentShutdownWaitTime,
[string]$DatabaseMailProfile,
[string]$ErrorLogFile,
[int]$IdleCpuDuration,
[int]$IdleCpuPercentage,
[ValidateSet("Enabled", "Disabled")]
[string]$CpuPolling,
[string]$LocalHostAlias,
[int]$LoginTimeout,
[int]$MaximumHistoryRows,
[int]$MaximumJobHistoryRows,
[string]$NetSendRecipient,
[ValidateSet("Enabled", "Disabled")]
[string]$ReplaceAlertTokens,
[ValidateSet("Enabled", "Disabled")]
[string]$SaveInSentFolder,
[ValidateSet("Enabled", "Disabled")]
[string]$SqlAgentAutoStart,
[string]$SqlAgentMailProfile,
[ValidateSet("Enabled", "Disabled")]
[string]$SqlAgentRestart,
[ValidateSet("Enabled", "Disabled")]
[string]$SqlServerRestart,
[ValidateSet("Enabled", "Disabled")]
[string]$WriteOemErrorLog,
[switch]$EnableException
)
begin {
# Check of the agent mail type is of type string and set the integer value
if (($AgentMailType -notin 0, 1) -and ($null -ne $AgentMailType)) {
$AgentMailType = switch ($AgentMailType) { "SqlAgentMail" { 0 } "DatabaseMail" { 1 } }
}
# Check of the agent log level is of type string and set the integer value
if (($AgentLogLevel -notin 0, 1) -and ($null -ne $AgentLogLevel)) {
$AgentLogLevel = switch ($AgentLogLevel) { "Errors" { 1 } "Warnings" { 2 } "Errors, Warnings" { 3 } "Informational" { 4 } "Errors, Informational" { 5 } "Warnings, Informational" { 6 } "All" { 7 } }
}
}
process {
if (Test-FunctionInterrupt) { return }
if ((-not $InputObject) -and (-not $SqlInstance)) {
Stop-Function -Message "You must specify an Instance or pipe in results from another command" -Target $sqlinstance
return
}
foreach ($instance in $sqlinstance) {
# Try connecting to the instance
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$InputObject += $server.JobServer
$InputObject.Refresh()
}
foreach ($jobServer in $InputObject) {
$server = $jobServer.Parent
#region job server options
# Settings the options for the job server
if ($AgentLogLevel) {
Write-Message -Message "Setting Agent log level to $AgentLogLevel" -Level Verbose
$jobServer.AgentLogLevel = $AgentLogLevel
}
if ($AgentMailType) {
Write-Message -Message "Setting Agent Mail Type to $AgentMailType" -Level Verbose
$jobServer.AgentMailType = $AgentMailType
}
if ($AgentShutdownWaitTime) {
Write-Message -Message "Setting Agent Shutdown Wait Time to $AgentShutdownWaitTime" -Level Verbose
$jobServer.AgentShutdownWaitTime = $AgentShutdownWaitTime
}
if ($DatabaseMailProfile) {
if ($DatabaseMailProfile -in (Get-DbaDbMail -SqlInstance $server).Profiles.Name) {
Write-Message -Message "Setting Database Mail Profile to $DatabaseMailProfile" -Level Verbose
$jobServer.DatabaseMailProfile = $DatabaseMailProfile
} else {
Write-Message -Message "Database mail profile not found on $server" -Level Warning
}
}
if ($ErrorLogFile) {
Write-Message -Message "Setting agent server ErrorLogFile to $ErrorLogFile" -Level Verbose
$jobServer.ErrorLogFile = $ErrorLogFile
}
if ($IdleCpuDuration) {
Write-Message -Message "Setting agent server IdleCpuDuration to $IdleCpuDuration" -Level Verbose
$jobServer.IdleCpuDuration = $IdleCpuDuration
}
if ($IdleCpuPercentage) {
Write-Message -Message "Setting agent server IdleCpuPercentage to $IdleCpuPercentage" -Level Verbose
$jobServer.IdleCpuPercentage = $IdleCpuPercentage
}
if ($CpuPolling) {
Write-Message -Message "Setting agent server IsCpuPollingEnabled to $IsCpuPollingEnabled" -Level Verbose
$jobServer.IsCpuPollingEnabled = if ($CpuPolling -eq "Enabled") {$true} else {$false}
}
if ($LocalHostAlias) {
Write-Message -Message "Setting agent server LocalHostAlias to $LocalHostAlias" -Level Verbose
$jobServer.LocalHostAlias = $LocalHostAlias
}
if ($LoginTimeout) {
Write-Message -Message "Setting agent server LoginTimeout to $LoginTimeout" -Level Verbose
$jobServer.LoginTimeout = $LoginTimeout
}
if ($MaximumHistoryRows) {
Write-Message -Message "Setting agent server MaximumHistoryRows to $MaximumHistoryRows" -Level Verbose
$jobServer.MaximumHistoryRows = $MaximumHistoryRows
}
if ($MaximumJobHistoryRows) {
Write-Message -Message "Setting agent server MaximumJobHistoryRows to $MaximumJobHistoryRows" -Level Verbose
$jobServer.MaximumJobHistoryRows = $MaximumJobHistoryRows
}
if ($NetSendRecipient) {
Write-Message -Message "Setting agent server NetSendRecipient to $NetSendRecipient" -Level Verbose
$jobServer.NetSendRecipient = $NetSendRecipient
}
if ($ReplaceAlertTokens) {
Write-Message -Message "Setting agent server ReplaceAlertTokensEnabled to $ReplaceAlertTokens" -Level Verbose
$jobServer.ReplaceAlertTokens = if ($ReplaceAlertTokens -eq "Enabled") {$true} else {$false}
}
if ($SaveInSentFolder) {
Write-Message -Message "Setting agent server SaveInSentFolder to $SaveInSentFolder" -Level Verbose
$jobServer.SaveInSentFolder = if ($SaveInSentFolder -eq "Enabled") {$true} else {$false}
}
if ($SqlAgentAutoStart) {
Write-Message -Message "Setting agent server SqlAgentAutoStart to $SqlAgentAutoStart" -Level Verbose
$jobServer.SqlAgentAutoStart = if ($SqlAgentAutoStart -eq "Enabled") {$true} else {$false}
}
if ($SqlAgentMailProfile) {
Write-Message -Message "Setting agent server SqlAgentMailProfile to $SqlAgentMailProfile" -Level Verbose
$jobServer.SqlAgentMailProfile = $SqlAgentMailProfile
}
if ($SqlAgentRestart) {
Write-Message -Message "Setting agent server SqlAgentRestart to $SqlAgentRestart" -Level Verbose
$jobServer.SqlAgentRestart = if ($SqlAgentRestart -eq "Enabled") {$true} else {$false}
}
if ($SqlServerRestart) {
Write-Message -Message "Setting agent server SqlServerRestart to $SqlServerRestart" -Level Verbose
$jobServer.SqlServerRestart = if ($SqlServerRestart -eq "Enabled") {$true} else {$false}
}
if ($WriteOemErrorLog) {
Write-Message -Message "Setting agent server WriteOemErrorLog to $WriteOemErrorLog" -Level Verbose
$jobServer.WriteOemErrorLog = if ($WriteOemErrorLog -eq "Enabled") {$true} else {$false}
}
#endregion server agent options
# Execute
if ($PSCmdlet.ShouldProcess($SqlInstance, "Changing the agent server")) {
try {
Write-Message -Message "Changing the agent server" -Level Verbose
# Change the agent server
$jobServer.Alter()
} catch {
Stop-Function -Message "Something went wrong changing the agent server" -ErrorRecord $_ -Target $instance -Continue
}
Get-DbaAgentServer -SqlInstance $server | Where-Object Name -eq $jobServer.name
}
}
}
end {
Write-Message -Message "Finished changing agent server(s)" -Level Verbose
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAgListener {
<#
.SYNOPSIS
Sets a listener property for an availability group on a SQL Server instance.
.DESCRIPTION
Sets a listener property for an availability group on a SQL Server instance.
Basically, only the port is settable at this time, so this command updates the listener port.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER AvailabilityGroup
The Availability Group to which a property will be changed.
.PARAMETER Port
Sets the port number used to communicate with the availability group.
.PARAMETER Listener
Modify only specific listeners.
.PARAMETER InputObject
Enables piping from Get-DbaAvailabilityGroup
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaAgListener
.EXAMPLE
PS C:\> Set-DbaAgListener -SqlInstance sql2017 -AvailabilityGroup SharePoint -Port 14333
Changes the port for the SharePoint AG Listener on sql2017. Prompts for confirmation.
.EXAMPLE
PS C:\> Get-DbaAgListener -SqlInstance sql2017 | Out-GridView -Passthru | Set-DbaAgListener -Port 1433 -Confirm:$false
Changes the port for selected AG listeners to 1433. Does not prompt for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[string[]]$Listener,
[Parameter(Mandatory)]
[int]$Port,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName AvailabilityGroup)) {
Stop-Function -Message "You must specify one or more databases and one or more Availability Groups when using the SqlInstance parameter."
return
}
if ($SqlInstance) {
$InputObject += Get-DbaAgListener -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup -Listener $Listener
}
foreach ($aglistener in $InputObject) {
if ($Pscmdlet.ShouldProcess($ag.Parent.Name, "Setting port to $Port for $($ag.Name)")) {
try {
$aglistener.PortNumber = $Port
$aglistener.Alter()
$aglistener
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAgReplica {
<#
.SYNOPSIS
Sets the properties for a replica to an availability group on a SQL Server instance.
.DESCRIPTION
Sets the properties for a replica to an availability group on a SQL Server instance.
Automatically creates a database mirroring endpoint if required.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Replica
The replicas to modify.
.PARAMETER AvailabilityGroup
The availability group of the replica.
.PARAMETER AvailabilityMode
Sets the availability mode of the availability group replica. Options are: AsynchronousCommit and SynchronousCommit. SynchronousCommit is default.
.PARAMETER FailoverMode
Sets the failover mode of the availability group replica. Options are Automatic and Manual.
.PARAMETER BackupPriority
Sets the backup priority availability group replica. Default is 50.
.PARAMETER EndpointUrl
The endpoint URL.
.PARAMETER InputObject
Enables piping from Get-DbaAgReplica.
.PARAMETER ConnectionModeInPrimaryRole
Sets the connection intent modes of an Availability Replica in primary role.
.PARAMETER ConnectionModeInSecondaryRole
Sets the connection modes of an Availability Replica in secondary role.
.PARAMETER ReadonlyRoutingConnectionUrl
Sets the read only routing connection url for the availability replica.
.PARAMETER SeedingMode
Specifies how the secondary replica will be initially seeded.
Automatic enables direct seeding. This method will seed the secondary replica over the network. This method does not require you to backup and restore a copy of the primary database on the replica.
Manual requires you to create a backup of the database on the primary replica and manually restore that backup on the secondary replica.
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaAgReplica
.EXAMPLE
PS C:\> Set-DbaAgReplica -SqlInstance sql2016 -Replica sql2016 -AvailabilityGroup SharePoint -BackupPriority 5000
Sets the backup priority to 5000 for the sql2016 replica for the SharePoint availability group on sql2016
.EXAMPLE
PS C:\> Get-DbaAgReplica -SqlInstance sql2016 | Out-GridView -Passthru | Set-DbaAgReplica -BackupPriority 5000
Sets the backup priority to 5000 for the selected availability groups.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string]$AvailabilityGroup,
[string]$Replica,
[ValidateSet('AsynchronousCommit', 'SynchronousCommit')]
[string]$AvailabilityMode,
[ValidateSet('Automatic', 'Manual', 'External')]
[string]$FailoverMode,
[int]$BackupPriority,
[ValidateSet('AllowAllConnections', 'AllowReadWriteConnections')]
[string]$ConnectionModeInPrimaryRole,
[ValidateSet('AllowAllConnections', 'AllowNoConnections', 'AllowReadIntentConnectionsOnly')]
[string]$ConnectionModeInSecondaryRole,
[ValidateSet('Automatic', 'Manual')]
[string]$SeedingMode,
[string]$EndpointUrl,
[string]$ReadonlyRoutingConnectionUrl,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityReplica]$InputObject,
[switch]$EnableException
)
process {
if (-not $InputObject) {
if (-not $AvailabilityGroup -or -not $Replica) {
Stop-Function -Message "You must specify an AvailabilityGroup and replica or pipe in an availabilty group to continue."
return
}
}
if ($SqlInstance) {
$InputObject += Get-DbaAgReplica -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup -Replica $Replica
}
foreach ($agreplica in $InputObject) {
$server = $agreplica.Parent.Parent
if ($Pscmdlet.ShouldProcess($server.Name, "Modifying replica for $($agreplica.Name) named $Name")) {
try {
if ($EndpointUrl) {
$agreplica.EndpointUrl = $EndpointUrl
}
if ($FailoverMode) {
$agreplica.FailoverMode = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaFailoverMode]::$FailoverMode
}
if ($AvailabilityMode) {
$agreplica.AvailabilityMode = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaAvailabilityMode]::$AvailabilityMode
}
if ($ConnectionModeInPrimaryRole) {
$agreplica.ConnectionModeInPrimaryRole = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaConnectionModeInPrimaryRole]::$ConnectionModeInPrimaryRole
}
if ($ConnectionModeInSecondaryRole) {
$agreplica.ConnectionModeInSecondaryRole = [Microsoft.SqlServer.Management.Smo.AvailabilityReplicaConnectionModeInSecondaryRole]::$ConnectionModeInSecondaryRole
}
if ($BackupPriority) {
$agreplica.BackupPriority = $BackupPriority
}
if ($ReadonlyRoutingConnectionUrl) {
$agreplica.ReadonlyRoutingConnectionUrl = $ReadonlyRoutingConnectionUrl
}
if ($SeedingMode) {
$agreplica.SeedingMode = $SeedingMode
}
$agreplica.Alter()
$agreplica
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaAvailabilityGroup {
<#
.SYNOPSIS
Sets availability group properties on a SQL Server instance.
.DESCRIPTION
Sets availability group properties on a SQL Server instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER AvailabilityGroup
Only set specific availability group properties.
.PARAMETER AllAvailabilityGroups
Set properties for all availability group on an instance.
.PARAMETER DtcSupportEnabled
Enables DtcSupport.
.PARAMETER ClusterType
Cluster type of the Availability Group. Only supported in SQL Server 2017 and above.
Options include: External, Wsfc or None.
.PARAMETER AutomatedBackupPreference
Specifies how replicas in the primary role are treated in the evaluation to pick the desired replica to perform a backup.
.PARAMETER FailureConditionLevel
Specifies the different conditions that can trigger an automatic failover in Availability Group.
.PARAMETER HealthCheckTimeout
This setting used to specify the length of time, in milliseconds, that the SQL Server resource DLL should wait for information returned by the sp_server_diagnostics stored procedure before reporting the Always On Failover Cluster Instance (FCI) as unresponsive.
Changes that are made to the timeout settings are effective immediately and do not require a restart of the SQL Server resource.
Defaults is 30000 (30 seconds).
.PARAMETER BasicAvailabilityGroup
Indicates whether the availability group is basic. Basic availability groups like pumpkin spice and uggs.
https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/basic-availability-groups-always-on-availability-groups
.PARAMETER DatabaseHealthTrigger
Indicates whether the availability group triggers the database health.
.PARAMETER IsDistributedAvailabilityGroup
Indicates whether the availability group is distributed.
.PARAMETER InputObject
Enables piping from Get-DbaAvailabilityGroup.
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaAvailabilityGroup
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql2016 | Set-DbaAvailabilityGroup -DtcSupportEnabled
Enables DTC for all availability groups on sql2016
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql2016 -AvailabilityGroup AG1 | Set-DbaAvailabilityGroup -DtcSupportEnabled:$false
Disables DTC support for the availability group AG1
.EXAMPLE
PS C:\> Set-DbaAvailabilityGroup -SqlInstance sql2016 -AvailabilityGroup AG1 -DtcSupportEnabled:$false
Disables DTC support for the availability group AG1
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$AvailabilityGroup,
[switch]$AllAvailabilityGroups,
[switch]$DtcSupportEnabled,
[ValidateSet('External', 'Wsfc', 'None')]
[string]$ClusterType,
[ValidateSet('None', 'Primary', 'Secondary', 'SecondaryOnly')]
[string]$AutomatedBackupPreference,
[ValidateSet('OnAnyQualifiedFailureCondition', 'OnCriticalServerErrors', 'OnModerateServerErrors', 'OnServerDown', 'OnServerUnresponsive')]
[string]$FailureConditionLevel,
[int]$HealthCheckTimeout,
[switch]$BasicAvailabilityGroup,
[switch]$DatabaseHealthTrigger,
[switch]$IsDistributedAvailabilityGroup,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName AvailabilityGroup, AllAvailabilityGroups)) {
Stop-Function -Message "You must specify AllAvailabilityGroups groups or AvailabilityGroups when using the SqlInstance parameter."
return
}
if ($SqlInstance) {
$InputObject += Get-DbaAvailabilityGroup -SqlInstance $SqlInstance -SqlCredential $SqlCredential -AvailabilityGroup $AvailabilityGroup
}
$props = "Name", "AutomatedBackupPreference", "BasicAvailabilityGroup", "ClusterType", "DatabaseHealthTrigger", "DtcSupportEnabled", "FailureConditionLevel", "HealthCheckTimeout", "IsDistributedAvailabilityGroup"
foreach ($ag in $InputObject) {
try {
if ($Pscmdlet.ShouldProcess($ag.Parent.Name, "Seting properties on $ag")) {
foreach ($prop in $props) {
if (Test-Bound -ParameterName $prop) {
$ag.$prop = (Get-Variable -Name $prop -ValueOnly)
}
}
$ag.Alter()
$ag
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
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
Explicitly 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 status 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-DbatoolsConfig -Module ComputerManagement".
.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: ComputerManagement, CIM
Author: Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/set-DbaCmConnection
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaCmConnection | Export-Clixml .\connections.xml
PS C:\> 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 re-import 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(SupportsShouldProcess, DefaultParameterSetName = 'Credential')]
param (
[Parameter(ValueFromPipeline)]
[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-DbatoolsConfigValue -Name 'ComputerManagement.Cache.Disable.All' -Fallback $false
}
process {
foreach ($connectionObject in $ComputerName) {
if ($Pscmdlet.ShouldProcess($($connectionObject.Connection.ComputerName), "Setting Connection")) {
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-DbaDbCompatibility {
<#
.SYNOPSIS
Sets the compatibility level for SQL Server databases.
.DESCRIPTION
Sets the current database compatibility level for all databases on a server or list of databases passed in to the function.
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.PARAMETER SqlCredential
SqlCredential object used to connect to the SQL Server as a different user.
.PARAMETER Database
The database or databases to process. If unspecified, all databases will be processed.
.PARAMETER TargetCompatibility
The target compatibility level version. This is an int and follows Microsoft's versioning:
9 = SQL Server 2005
10 = SQL Server 2008
11 = SQL Server 2012
12 = SQL Server 2014
13 = SQL Server 2016
14 = SQL Server 2017
15 = SQL Server 2019
.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: Compatibility, Database
Author: Garry Bargsley, http://blog.garrybargsley.com/
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaDbCompatibility
.EXAMPLE
PS C:\> Set-DbaDbCompatibility -SqlInstance localhost\sql2017
Changes database compatibility level for all user databases on server localhost\sql2017 that have a Compatibility level that do not match
.EXAMPLE
PS C:\> Set-DbaDbCompatibility -SqlInstance localhost\sql2017 -TargetCompatibility 12
Changes database compatibility level for all user databases on server localhost\sql2017 to Version120
.EXAMPLE
PS C:\> Set-DbaDbCompatibility -SqlInstance localhost\sql2017 -Database Test -TargetCompatibility 12
Changes database compatibility level for database Test on server localhost\sql2017 to Version 120
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[ValidateSet(9, 10, 11, 12, 13, 14, 15)]
[int]$TargetCompatibility,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[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 ($SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database
}
foreach ($db in $InputObject) {
$server = $db.Parent
$ServerVersion = $server.VersionMajor
Write-Message -Level Verbose -Message "SQL Server is using Version: $ServerVersion"
$ogcompat = $db.CompatibilityLevel
$dbversion = switch ($ogcompat) {
"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
"Version150" { 15 } # SQL Server 2019
default { 9 } # SQL Server 2005
}
if (-not $TargetCompatibility) {
if ($dbversion -lt $ServerVersion) {
If ($Pscmdlet.ShouldProcess($server.Name, "Updating $db version from $dbversion to $ServerVersion")) {
$comp = $ServerVersion * 10
$sql = "ALTER DATABASE $db SET COMPATIBILITY_LEVEL = $comp"
try {
$db.ExecuteNonQuery($sql)
$db.Refresh()
Get-DbaDbCompatibility -SqlInstance $server -Database $db.Name
} catch {
Stop-Function -Message "Failed to change Compatibility Level" -ErrorRecord $_ -Target $instance -Continue
}
}
}
} else {
if ($Pscmdlet.ShouldProcess($server.Name, "Updating $db version from $dbversion to $TargetCompatibility")) {
$comp = $TargetCompatibility * 10
$sql = "ALTER DATABASE $db SET COMPATIBILITY_LEVEL = $comp"
try {
$db.ExecuteNonQuery($sql)
$db.Refresh()
Get-DbaDbCompatibility -SqlInstance $server -Database $db.Name
} catch {
Stop-Function -Message "Failed to change Compatibility Level" -ErrorRecord $_ -Target $instance -Continue
}
}
}
}
}
}
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
The target SQL Server instance or instances. 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
Tags: Compression, Table, Database
Author: Jason Squires (@js_0505), [email protected]
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaDbCompression
.EXAMPLE
PS C:\> 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
PS C:\> Set-DbaDbCompression -SqlInstance ServerA -Database DBName -CompressionType Page
Utilizes Page compression for all objects in DBName on ServerA with no time limit.
.EXAMPLE
PS C:\> 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 formatted GridView.
.EXAMPLE
PS C:\> $testCompression = Test-DbaDbCompression -SqlInstance ServerA -Database DBName
PS C:\> 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
PS C:\> $cred = Get-Credential sqladmin
PS C:\> 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
PS C:\> $servers = 'Server1','Server2'
PS C:\> 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)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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 {
$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 = 'AlreadyProcessed'; e = {"False"}}
foreach ($obj in ($results | Where-Object {$_.CompressionTypeRecommendation -notin @('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.AlreadyProcessed = "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.AlreadyProcessed = "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 -notin ($CompressionType, 'ColumnStore', 'ColumnStoreArchive')})) {
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
AlreadyProcessed = "True"
}
}
foreach ($index in $($obj.Indexes | Where-Object {!$_.IsMemoryOptimized -and $_.IndexType -notmatch 'Columnstore'})) {
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
AlreadyProcessed = "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
AlreadyProcessed = "True"
}
}
}
}
}
} catch {
Stop-Function -Message "Compression failed for $instance - $db" -Target $db -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaDbMirror {
<#
.SYNOPSIS
Sets properties of database mirrors.
.DESCRIPTION
Sets properties of database mirrors.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 target database.
.PARAMETER Partner
Sets the partner fqdn.
.PARAMETER Witness
Sets the witness fqdn.
.PARAMETER SafetyLevel
Sets the mirroring safety level.
.PARAMETER State
Sets the mirror state.
.PARAMETER InputObject
Allows piping from Get-DbaDatabase.
.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: Mirror, HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaDbMirror
.EXAMPLE
PS C:\> Set-DbaDbMirror -SqlInstance sql2005 -Database dbatools -Partner TCP://SQL2008.ad.local:5374
Prompts for confirmation then sets the partner to TCP://SQL2008.ad.local:5374 for the database "dbtools"
.EXAMPLE
PS C:\> Set-DbaDbMirror -SqlInstance sql2005 -Database dbatools -Witness TCP://SQL2012.ad.local:5502 -Confirm:$false
Does not prompt for confirmation and sets the witness to TCP://SQL2012.ad.local:5502 for the database "dbtools"
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2005 | Out-GridView -PassThru | Set-DbaDbMirror -SafetyLevel Full -Confirm:$false
Sets the safety level to Full for databases selected from a grid view. Does not prompt for confirmation.
.EXAMPLE
PS C:\> Set-DbaDbMirror -SqlInstance sql2005 -Database dbatools -State Suspend -Confirm:$false
Does not prompt for confirmation and sets the state to suspend for the database "dbtools"
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[string]$Partner,
[string]$Witness,
[ValidateSet('Full', 'Off', 'None')]
[string]$SafetyLevel,
[ValidateSet('ForceFailoverAndAllowDataLoss', 'Failover', 'RemoveWitness', 'Resume', 'Suspend', 'Off')]
[string]$State,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -and (Test-Bound -Not -ParameterName Database)) {
Stop-Function -Message "Database is required when SqlInstance is specified"
return
}
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $instance -SqlCredential $SqlCredential -Database $Database
}
foreach ($db in $InputObject) {
try {
if ($Partner) {
if ($Pscmdlet.ShouldProcess($db.Parent.Name, "Setting partner on $db")) {
# use t-sql cuz $db.Alter() doesnt always work against restoring dbs
$db.Parent.Query("ALTER DATABASE $db SET PARTNER = N'$Partner'")
}
} elseif ($Witness) {
if ($Pscmdlet.ShouldProcess($db.Parent.Name, "Setting witness on $db")) {
$db.Parent.Query("ALTER DATABASE $db SET WITNESS = N'$Witness'")
}
}
if ($SafetyLevel) {
if ($Pscmdlet.ShouldProcess($db.Parent.Name, "Changing safety level to $SafetyLevel on $db")) {
$db.Parent.Query("ALTER DATABASE $db SET PARTNER SAFETY $SafetyLevel")
# $db.MirroringSafetyLevel = $SafetyLevel
}
}
if ($State) {
if ($Pscmdlet.ShouldProcess($db.Parent.Name, "Changing mirror state to $State on $db")) {
$db.ChangeMirroringState($State)
$db.Alter()
$db
}
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_
}
}
}
}
function Set-DbaDbOwner {
<#
.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
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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaDbOwner
.EXAMPLE
PS C:\> Set-DbaDbOwner -SqlInstance localhost
Sets database owner to 'sa' on all databases where the owner does not match 'sa'.
.EXAMPLE
PS C:\> Set-DbaDbOwner -SqlInstance localhost -TargetLogin DOMAIN\account
Sets the database owner to DOMAIN\account on all databases where the owner does not match DOMAIN\account.
.EXAMPLE
PS C:\> Set-DbaDbOwner -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)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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) {
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
}
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Set-DbaDatabaseOwner
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
function Set-DbaDbQueryStoreOption {
<#
.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 target SQL Server instance or 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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaQueryStoreOptions
.EXAMPLE
PS C:\> Set-DbaDbQueryStoreOption -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
PS C:\> Set-DbaDbQueryStoreOption -SqlInstance ServerA\SQL -FlushInterval 600
Only configure the FlushInterval setting for all Query Store databases in the ServerA\SQL Instance.
.EXAMPLE
PS C:\> Set-DbaDbQueryStoreOption -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
PS C:\> Set-DbaDbQueryStoreOption -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)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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) {
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-DbaDbQueryStoreOption")) {
# Display resulting changes
Get-DbaDbQueryStoreOption -SqlInstance $server -Database $db.name -Verbose:$false
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Set-DbaDbQueryStoreOptions
}
}
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: RecoveryModel, Database
Author: Viorel Ciucu (@viorelciucu), https://www.cviorel.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaDbRecoveryModel
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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
PS C:\> 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 {
$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 {
if ($Pscmdlet.ShouldProcess("$db on $instance", "ALTER DATABASE $db SET RECOVERY $RecoveryModel")) {
$db.RecoveryModel = $RecoveryModel
$db.Alter()
Write-Message -Level Verbose -Message "Recovery Model set to $RecoveryModel for database $db"
}
}
Get-DbaDbRecoveryModel -SqlInstance $db.Parent -Database $db.name
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline#
function Set-DbaDbState {
<#
.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 target SQL Server instance or instances
.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: Simone Bizzotto (@niphold)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaDbState
.EXAMPLE
PS C:\> Set-DbaDbState -SqlInstance sqlserver2014a -Database HR -Offline
Sets the HR database as OFFLINE
.EXAMPLE
PS C:\> Set-DbaDbState -SqlInstance sqlserver2014a -AllDatabases -Exclude HR -ReadOnly -Force
Sets all databases of the sqlserver2014a instance, except for HR, as READ_ONLY
.EXAMPLE
PS C:\> Get-DbaDbState -SqlInstance sql2016 | Where-Object Status -eq 'Offline' | Set-DbaDbState -Online
Finds all offline databases and sets them to online
.EXAMPLE
PS C:\> Set-DbaDbState -SqlInstance sqlserver2014a -Database HR -SingleUser
Sets the HR database as SINGLE_USER
.EXAMPLE
PS C:\> Set-DbaDbState -SqlInstance sqlserver2014a -Database HR -SingleUser -Force
Sets the HR database as SINGLE_USER, dropping all other connections (and rolling back open transactions)
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sqlserver2014a -Database HR | Set-DbaDbState -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)]
param (
[parameter(Mandatory, 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, 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-DbaDbState
$dbs += $InputObject.Database
} elseif ($InputObject.Name) {
# comes from Get-DbaDatabase
$dbs += $InputObject
}
} else {
foreach ($instance in $SqlInstance) {
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-DbaDbState -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 {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Set-DbaDatabaseState
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Set-DbaEndpoint {
<#
.SYNOPSIS
Sets endpoint properties on a SQL Server instance.
.DESCRIPTION
Sets endpoint properties on a SQL Server instance.
.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 Owner
Change the endpoint owner.
.PARAMETER Type
Change the endpoint type. Options: DatabaseMirroring, ServiceBroker, Soap, TSql
.PARAMETER Endpoint
Only set specific endpoint properties.
.PARAMETER AllEndpoints
Set all endpoint properties on an instance.
.PARAMETER InputObject
Enables piping from Get-Endpoint.
.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: Endpoint
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaEndpoint
.EXAMPLE
PS C:\> Set-DbaEndpoint -SqlInstance sql2016 -AllEndpoints -Owner sa
Sets all endpoint owners to sa on sql2016
.EXAMPLE
PS C:\> Get-DbaEndpoint -SqlInstance sql2016 -Endpoint ep1 | Set-DbaEndpoint -Type TSql
Changes the endpoint type to tsql on endpoint ep1
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$EndPoint,
[string]$Owner,
[ValidateSet('DatabaseMirroring', 'ServiceBroker', 'Soap', 'TSql')]
[string]$Type,
[switch]$AllEndpoints,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Endpoint[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -And (Test-Bound -Not -ParameterName Endpoint, AllEndpoints)) {
Stop-Function -Message "You must specify AllEndpoints or Endpoint when using the SqlInstance parameter."
return
}
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaEndpoint -SqlInstance $instance -SqlCredential $SqlCredential -EndPoint $Endpoint
}
$props = "Owner", "Type"
foreach ($ep in $InputObject) {
try {
if ($Pscmdlet.ShouldProcess($ep.Parent.Name, "Seting properties on $ep")) {
foreach ($prop in $props) {
if ($prop -eq "Type") {
$realprop = "EndpointType"
if (Test-Bound -ParameterName $prop) {
$ep.$realprop = (Get-Variable -Name $prop -ValueOnly)
}
} elseif (Test-Bound -ParameterName $prop) {
$ep.$prop = (Get-Variable -Name $prop -ValueOnly)
}
}
$ep.Alter()
$ep
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
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 or instances
.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), https://wsmelton.github.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaErrorLogConfig
.EXAMPLE
PS C:\> Set-DbaErrorLogConfig -SqlInstance sql2017,sql2014 -LogCount 25
Sets the number of error log files to 25 on sql2017 and sql2014
.EXAMPLE
PS C:\> 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
PS C:\> 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) {
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
If the 'sa' account was renamed, the new name will be used.
.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 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 InputObject
Enables piped input from Get-DbaAgentJob
.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.
.NOTES
Tags: Agent, Job
Author: Michael Fal (@Mike_Fal), http://mikefal.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaJobOwner
.EXAMPLE
PS C:\> Set-DbaJobOwner -SqlInstance localhost
Sets SQL Agent Job owner to sa on all jobs where the owner does not match sa.
.EXAMPLE
PS C:\> Set-DbaJobOwner -SqlInstance localhost -Login DOMAIN\account
Sets SQL Agent Job owner to 'DOMAIN\account' 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
PS C:\> 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
PS C:\> '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.
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance vmsql | Where-Object OwnerLoginName -eq login1 | Set-DbaJobOwner -TargetLogin login2 | Out-Gridview
Sets SQL Agent Job owner to login2 where their current owner is login1 on instance vmsql. Send result to gridview.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Jobs")]
[object[]]$Job,
[object[]]$ExcludeJob,
[Parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Agent.Job[]]$InputObject,
[Alias("TargetLogin")]
[string]$Login,
[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
}
#Get job 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 {$Job -contains $_.Name}
} else {
$jobcollection = $server.JobServer.Jobs
}
if ($ExcludeJob) {
$jobcollection = $jobcollection | Where-Object { $ExcludeJob -notcontains $_.Name }
}
$InputObject += $jobcollection
}
Write-Message -Level Verbose -Message "Updating $($InputObject.Count) job(s)."
foreach ($agentJob in $InputObject) {
$jobname = $agentJob.Name
$server = $agentJob.Parent.Parent
if (-not $Login) {
# dynamic sa name for orgs who have changed their sa name
$newLogin = ($server.logins | Where-Object { $_.id -eq 1 }).Name
} else {
$newLogin = $Login
}
#Validate login
if ($agentJob.OwnerLoginName -eq $newLogin) {
$status = 'Skipped'
$notes = "Owner already set"
} else {
if (($server.Logins.Name) -notcontains $newLogin) {
$status = 'Failed'
$notes = "Login $newLogin not valid"
} else {
if ($server.logins[$newLogin].LoginType -eq 'WindowsGroup') {
$status = 'Failed'
$notes = "$newLogin is a Windows Group and can not be a job owner."
} else {
if ($PSCmdlet.ShouldProcess($instance, "Setting job owner for $jobname to $newLogin")) {
try {
Write-Message -Level Verbose -Message "Setting job owner for $jobname to $newLogin on $instance."
#Set job owner to $TargetLogin (default 'sa')
$agentJob.OwnerLoginName = $newLogin
$agentJob.Alter()
$status = 'Succesful'
$notes = ''
} catch {
Stop-Function -Message "Issue setting job owner on $jobName." -Target $jobName -InnerErrorRecord $_ -Category InvalidOperation
}
}
}
}
}
Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name ComputerName -value $server.ComputerName
Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name InstanceName -value $server.ServiceName
Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName
Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name Status -value $status
Add-Member -Force -InputObject $agentJob -MemberType NoteProperty -Name Notes -value $notes
Select-DefaultView -InputObject $agentJob -Property ComputerName, InstanceName, SqlInstance, Name, Category, OwnerLoginName, Status, Notes
}
}
}
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
The target SQL Server instance or instances. 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 SecurePassword
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 -SecurePassword 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 -SecurePassword 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 InputObject
Allows logins to be piped in from Get-DbaLogin
.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: Login
Author: Sander Stad (@sqlstad), sqlstad.nl
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaLogin
.EXAMPLE
PS C:\> $SecurePassword = ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force
PS C:\> $cred = New-Object System.Management.Automation.PSCredential ("username", $SecurePassword)
PS C:\> Set-DbaLogin -SqlInstance sql1 -Login login1 -SecurePassword $cred -Unlock -MustChange
Set the new password for login1 using a credential, unlock the account and set the option
that the user must change password at next logon.
.EXAMPLE
PS C:\> Set-DbaLogin -SqlInstance sql1 -Login login1 -Enable
Enable the login
.EXAMPLE
PS C:\> Set-DbaLogin -SqlInstance sql1 -Login login1, login2, login3, login4 -Enable
Enable multiple logins
.EXAMPLE
PS C:\> Set-DbaLogin -SqlInstance sql1, sql2, sql3 -Login login1, login2, login3, login4 -Enable
Enable multiple logins on multiple instances
.EXAMPLE
PS C:\> Set-DbaLogin -SqlInstance sql1 -Login login1 -Disable
Disable the login
.EXAMPLE
PS C:\> Set-DbaLogin -SqlInstance sql1 -Login login1 -DenyLogin
Deny the login to connect to the instance
.EXAMPLE
PS C:\> Set-DbaLogin -SqlInstance sql1 -Login login1 -GrantLogin
Grant the login to connect to the instance
.EXAMPLE
PS C:\> Set-DbaLogin -SqlInstance sql1 -Login login1 -PasswordPolicyEnforced
Enforces the password policy on a login
.EXAMPLE
PS C:\> Set-DbaLogin -SqlInstance sql1 -Login login1 -PasswordPolicyEnforced:$false
Disables enforcement of the password policy on a login
.EXAMPLE
PS C:\> Set-DbaLogin -SqlInstance sql1 -Login test -AddRole serveradmin
Add the server role "serveradmin" to the login
.EXAMPLE
PS C:\> Set-DbaLogin -SqlInstance sql1 -Login test -RemoveRole bulkadmin
Remove the server role "bulkadmin" to the login
.EXAMPLE
PS C:\> $login = Get-DbaLogin -SqlInstance sql1 -Login test
PS C:\> $login | Set-DbaLogin -Disable
Disable the login from the pipeline
#>
[CmdletBinding(SupportsShouldProcess)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "", Justification = "For Parameter Password")]
param (
[Alias('ServerInstance', 'SqlServer')]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Login,
[Alias("Password")]
[object]$SecurePassword, #object so that it can accept credential or securestring
[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,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Login[]]$InputObject,
[Alias('Silent')]
[switch]$EnableException
)
begin {
# Check the parameters
if ((Test-Bound -ParameterName 'SqlInstance') -and (Test-Bound -ParameterName 'Login' -Not)) {
Stop-Function -Message 'You must specify a Login when using SqlInstance'
}
if ((Test-Bound -ParameterName 'NewName') -and $Login -eq $NewName) {
Stop-Function -Message 'Login name is the same as the value in -NewName' -Target $Login -Continue
}
if ((Test-Bound -ParameterName 'Disable') -and (Test-Bound -ParameterName 'Enable')) {
Stop-Function -Message 'You cannot use both -Enable and -Disable together' -Target $Login -Continue
}
if ((Test-Bound -ParameterName 'GrantLogin') -and (Test-Bound -ParameterName 'DenyLogin')) {
Stop-Function -Message 'You cannot use both -GrantLogin and -DenyLogin together' -Target $Login -Continue
}
if (Test-bound -ParameterName 'SecurePassword') {
switch ($SecurePassword.GetType().Name) {
'PSCredential' { $NewSecurePassword = $SecurePassword.Password }
'SecureString' { $NewSecurePassword = $SecurePassword }
default {
Stop-Function -Message 'Password must be a PSCredential or SecureString' -Target $Login
}
}
}
}
process {
if (Test-FunctionInterrupt) { return }
$allLogins = @{}
foreach ($instance in $sqlinstance) {
# Try connecting to the instance
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
} catch {
Stop-Function -Message 'Failure' -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$allLogins[$instance.ToString()] = Get-DbaLogin -SqlInstance $server
$InputObject += $allLogins[$instance.ToString()] | Where-Object { ($_.Name -eq $Login) -and ($_.IsSystemObject -eq $false) -and ($_.Name -notlike '##*') }
}
# Loop through all the logins
foreach ($l in $InputObject) {
if ($Pscmdlet.ShouldProcess($l, "Setting Changes to Login on $($server.name)")) {
$server = $l.Parent
# Create the notes
$notes = @()
# Change the name
if (Test-Bound -ParameterName 'NewName') {
# Check if the new name doesn't already exist
if ($allLogins[$server.Name].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 (Test-bound -ParameterName 'SecurePassword') {
try {
$l.ChangePassword($NewSecurePassword, $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 (Test-Bound -ParameterName '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 (Test-Bound -ParameterName '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 (Test-Bound -ParameterName 'DenyLogin') {
if ($l.DenyWindowsLogin) {
Write-Message -Message "Login $l already has login access denied" -Level Verbose
} else {
$l.DenyWindowsLogin = $true
}
}
# Grant access
if (Test-Bound -ParameterName '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 -ParameterName '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-DbaServerRoleMember -SqlInstance $server | Where-Object { $_.Name -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
}
$rolenames = $roles.Role | Select-Object -Unique
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name PasswordChanged -Value $passwordChanged
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name ServerRole -Value ($rolenames -join ', ')
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name Notes -Value $notes
# backwards compatibility: LoginName, DenyLogin
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name LoginName -Value $l.Name
Add-Member -Force -InputObject $l -MemberType NoteProperty -Name DenyLogin -Value $l.DenyWindowsLogin
$defaults = 'ComputerName', 'InstanceName', 'SqlInstance', 'LoginName', 'DenyLogin', 'IsDisabled', 'IsLocked',
'PasswordPolicyEnforced', 'MustChangePassword', 'PasswordChanged', 'ServerRole', 'Notes'
Select-DefaultView -InputObject $l -Property $defaults
}
}
}
}
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 target SQL Server instance or instances. Defaults to localhost.
.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 InputObject
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaMaxDop
.EXAMPLE
PS C:\> Set-DbaMaxDop -SqlInstance sql2008, sql2012
Sets Max DOP to the recommended value for servers sql2008 and sql2012.
.EXAMPLE
PS C:\> Set-DbaMaxDop -SqlInstance sql2014 -MaxDop 4
Sets Max DOP to 4 for server sql2014.
.EXAMPLE
PS C:\> Test-DbaMaxDop -SqlInstance sql2008 | Set-DbaMaxDop
Gets the recommended Max DOP from Test-DbaMaxDop and applies it to to sql2008.
.EXAMPLE
PS C:\> Set-DbaMaxDop -SqlInstance sql2016 -Database db1
Set recommended Max DOP for database db1 on server sql2016.
.EXAMPLE
PS C:\> Set-DbaMaxDop -SqlInstance sql2016 -AllDatabases
Set recommended Max DOP for all databases on server sql2016.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[int]$MaxDop = -1,
[Parameter(ValueFromPipeline)]
[pscustomobject]$InputObject,
[Alias("All")]
[switch]$AllDatabases,
[switch]$EnableException
)
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 InputObject)) {
$InputObject = Test-DbaMaxDop -SqlInstance $sqlinstance -SqlCredential $SqlCredential -Verbose:$false
} elseif ($null -eq $InputObject.SqlInstance) {
$InputObject = Test-DbaMaxDop -SqlInstance $sqlinstance -SqlCredential $SqlCredential -Verbose:$false
}
$InputObject | Add-Member -Force -NotePropertyName PreviousInstanceMaxDopValue -NotePropertyValue 0
$InputObject | Add-Member -Force -NotePropertyName PreviousDatabaseMaxDopValue -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 = $InputObject | Select-Object SqlInstance -Unique
foreach ($server in $servers) {
$servername = $server.SqlInstance
try {
$server = Connect-SqlInstance -SqlInstance $servername -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $servername -Continue
}
if (!(Test-SqlSa -SqlInstance $server -SqlCredential $SqlCredential)) {
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
$InputObject = $InputObject | Where-Object { $_.DatabaseMaxDop -eq "N/A" }
} else {
$dbscopedconfiguration = $true
if ((Test-Bound -Not -Parameter AllDatabases) -and (Test-Bound -Parameter Database)) {
$InputObject = $InputObject | Where-Object { $_.Database -in $Database }
} elseif ((Test-Bound -Not -Parameter AllDatabases) -and (Test-Bound -Parameter ExcludeDatabase)) {
$InputObject = $InputObject | Where-Object { $_.Database -notin $ExcludeDatabase }
} else {
if (Test-Bound -Parameter AllDatabases) {
$InputObject = $InputObject | Where-Object { $_.DatabaseMaxDop -ne "N/A" }
} else {
$InputObject = $InputObject | 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 $InputObject | 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.PreviousInstanceMaxDopValue = $row.CurrentInstanceMaxDop
try {
if ($UseRecommended) {
if ($dbscopedconfiguration) {
$row.PreviousDatabaseMaxDopValue = $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.PreviousDatabaseMaxDopValue = $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
PreviousDatabaseMaxDopValue = $row.PreviousDatabaseMaxDopValue
PreviousInstanceMaxDopValue = $row.PreviousInstanceMaxDopValue
}
if ($dbscopedconfiguration) {
Select-DefaultView -InputObject $results -Property InstanceName, Database, PreviousDatabaseMaxDopValue, @{
name = "CurrentDatabaseMaxDopValue"; expression = {
$_.DatabaseMaxDop
}
}
} else {
Select-DefaultView -InputObject $results -Property InstanceName, PreviousInstanceMaxDopValue, CurrentInstanceMaxDop
}
} catch {
Stop-Function -Message "Could not modify Max Degree of Parallelism for $server." -ErrorRecord $_ -Target $server -Continue
}
}
}
}
}
#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
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 Max
Specifies the max megabytes (MB)
.PARAMETER InputObject
A InputObject returned by Test-DbaMaxMemory
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaMaxMemory
.EXAMPLE
PS C:\> Set-DbaMaxMemory sqlserver1
Set max memory to the recommended on just one server named "sqlserver1"
.EXAMPLE
PS C:\> Set-DbaMaxMemory -SqlInstance sqlserver1 -Max 2048
Explicitly set max memory to 2048 on just one server, "sqlserver1"
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlserver | Test-DbaMaxMemory | Where-Object { $_.MaxValue -gt $_.Total } | 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, ConfirmImpact = 'Medium')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[int]$Max,
[Parameter(ValueFromPipeline)]
[PSCustomObject[]]$InputObject,
[switch]$EnableException
)
begin {
if ($Max -eq 0) {
$UseRecommended = $true
}
}
process {
if ($SqlInstance) {
$InputObject += Test-DbaMaxMemory -SqlInstance $SqlInstance -SqlCredential $SqlCredential
}
foreach ($result in $InputObject) {
$server = $result.Server
Add-Member -Force -InputObject $result -NotePropertyName PreviousMaxValue -NotePropertyValue $result.MaxValue
try {
if ($UseRecommended) {
Write-Message -Level Verbose -Message "Change $server SQL Server Max Memory from $($result.MaxValue) to $($result.RecommendedValue) "
if ($result.RecommendedValue -eq 0 -or $null -eq $result.RecommendedValue) {
$maxMem = $result.RecommendedValue
Write-Message -Level VeryVerbose -Message "Max memory recommended: $maxMem"
$server.Configuration.MaxServerMemory.ConfigValue = $maxMem
} else {
$server.Configuration.MaxServerMemory.ConfigValue = $result.RecommendedValue
}
} else {
Write-Message -Level Verbose -Message "Change $server SQL Server Max Memory from $($result.MaxValue) to $Max "
$server.Configuration.MaxServerMemory.ConfigValue = $Max
}
if ($PSCmdlet.ShouldProcess($server.Name, "Change Max Memory from $($result.PreviousMaxValue) to $($server.Configuration.MaxServerMemory.ConfigValue)")) {
try {
$server.Configuration.Alter()
$result.MaxValue = $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 $result -Force -MemberType NoteProperty -Name MaxValue -Value $result.MaxValue
Select-DefaultView -InputObject $result -Property ComputerName, InstanceName, SqlInstance, Total, MaxValue, PreviousMaxValue
}
}
}
#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 instance or instances. 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.
.NOTES
Tags: Certificate
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaNetworkCertificate
.EXAMPLE
PS C:\> 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
PS C:\> 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
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low", DefaultParameterSetName = 'Default')]
param (
[Parameter(ValueFromPipeline)]
[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]$EnableException
)
process {
# Registry access
if (Test-FunctionInterrupt) { return }
if (-not $Certificate -and -not $Thumbprint) {
Stop-Function -Message "You must specify a certificate or thumbprint"
return
}
if (-not $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
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) {
<# DO NOT use Write-Message as this is inside of a script block #>
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 {
<# DO NOT use Write-Message as this is inside of a script block #>
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 Credential
Specifies a PSCredential object to use in authenticating to the server(s), instead of the current user account.
.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 InputObject
Enables piping from Get-DbaPowerPlan
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: WMI access to servers
.LINK
https://dbatools.io/Set-DbaPowerPlan
.EXAMPLE
PS C:\> Set-DbaPowerPlan -ComputerName sql2017
Sets the Power Plan to High Performance. Skips it if its already set.
.EXAMPLE
PS C:\> 'Server1', 'Server2' | Set-DbaPowerPlan -PowerPlan Balanced
Sets the Power Plan to Balanced for Server1 and Server2. Skips it if its already set.
.EXAMPLE
PS C:\> $cred = Get-Credential 'Domain\User'
PS C:\> Set-DbaPowerPlan -ComputerName sql2017 -Credential $cred
Connects using alternative Windows credential and sets the Power Plan to High Performance. Skips it if its already set.
.EXAMPLE
PS C:\> 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)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[parameter(ValueFromPipeline)]
[DbaInstance[]]$ComputerName,
[PSCredential]$Credential,
[string]$PowerPlan = 'High Performance',
[string]$CustomPowerPlan,
[parameter(ValueFromPipeline)]
[pscustomobject]$InputObject,
[switch]$EnableException
)
begin {
if ($CustomPowerPlan) {
$powerPlanRequested = $CustomPowerPlan
} else {
$powerPlanRequested = $PowerPlan
}
function Set-DbaPowerPlanInternal {
[CmdletBinding(SupportsShouldProcess)]
param (
[string]$ComputerName,
[PSCredential]$Credential
)
if (Test-Bound -ParameterName Credential) {
$IncludeCred = $true
}
try {
Write-Message -Level Verbose -Message "Testing connection to $computer"
$computerResolved = Resolve-DbaNetworkName -ComputerName $computer -Credential $Credential
$computerResolved = $computerResolved.FullComputerName
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $computer
return
}
$splatDbaCmObject = @{
ComputerName = $computerResolved
EnableException = $true
}
if ($IncludeCred) {
$splatDbaCmObject["Credential"] = $Credential
}
try {
Write-Message -Level Verbose -Message "Getting Power Plan information from $computer."
$currentplan = Get-DbaCmObject @splatDbaCmObject -ClassName Win32_PowerPlan -Namespace "root\cimv2\power" | Where-Object IsActive -eq 'True'
$currentplan = $currentplan.ElementName
} 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
}
}
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 $computer." -Category ConnectionError -ErrorRecord $_ -Target $computer
return
}
$planinfo = [PSCustomObject]@{
ComputerName = $computer
PreviousPowerPlan = $currentplan
ActivePowerPlan = $powerPlanRequested
}
if ($Pscmdlet.ShouldProcess($powerPlanRequested, "Setting Powerplan on $computer")) {
if ($powerPlanRequested -ne $currentplan) {
if ($Pscmdlet.ShouldProcess($computer, "Changing Power Plan from $CurrentPlan to $powerPlanRequested")) {
Write-Message -Level Verbose -Message "Creating CIMSession on $computer over WSMan"
if ($IncludeCred) {
$cimSession = New-CimSession -ComputerName $computer -ErrorAction SilentlyContinue -Credential $Credential
} else {
$cimSession = New-CimSession -ComputerName $computer -ErrorAction SilentlyContinue
}
if (-not $cimSession) {
Write-Message -Level Verbose -Message "Creating CIMSession on $computer over WSMan failed. Creating CIMSession on $computer over DCom"
$sessionOption = New-CimSessionOption -Protocol DCom
if ($IncludeCred) {
$cimSession = New-CimSession -ComputerName $computer -SessionOption $sessionoption -ErrorAction SilentlyContinue -Credential $Credential
} else {
$cimSession = New-CimSession -ComputerName $computer -SessionOption $sessionoption -ErrorAction SilentlyContinue
}
}
if ($cimSession) {
Write-Message -Level Verbose -Message "Setting Power Plan to $powerPlanRequested."
$cimInstance = Get-CimInstance -Namespace root\cimv2\power -ClassName win32_PowerPlan -Filter "ElementName = '$powerPlanRequested'" -CimSession $CIMSession
if ($cimInstance) {
$cimResult = Invoke-CimMethod -InputObject $cimInstance[0] -MethodName Activate -CimSession $cimSession
if (!$cimResult) {
Stop-Function -Message "Couldn't set the requested Power Plan '$powerPlanRequested' on $computer." -Category ConnectionError -Target $computer
return
}
} else {
Stop-Function -Message "Couldn't find the requested Power Plan '$powerPlanRequested' on $computer." -Category ConnectionError -Target $computer
return
}
} else {
Stop-Function -Message "Couldn't set Power Plan on $computer." -Category ConnectionError -ErrorRecord $_ -Target $computer
return
}
}
} else {
if ($Pscmdlet.ShouldProcess($computer, "Stating power plan is already set to $powerPlanRequested, won't change.")) {
Write-Message -Level Verbose -Message "PowerPlan on $computer is already set to $powerPlanRequested. Skipping."
}
}
return $planInfo
}
}
}
process {
# uses cim commands
if (Test-Bound -ParameterName ComputerName) {
$InputObject += Get-DbaPowerPlan -ComputerName $ComputerName -Credential $Credential
}
foreach ($pplan in $InputObject) {
$computer = $pplan.ComputerName
$Credential = $pplan.Credential
Write-Message -Level Verbose -Message "Calling Set-DbaPowerPlanInternal for $computer"
if (Test-Bound -ParameterName Credential) {
$data = Set-DbaPowerPlanInternal -ComputerName $Computer -Credential $Credential
} else {
$data = Set-DbaPowerPlanInternal -ComputerName $Computer
}
if ($data.Count -gt 1) {
$data.GetEnumerator() | ForEach-Object {
$_
}
} else {
$data
}
}
}
}
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 target SQL Server instance or instances.
.PARAMETER Credential
Credential object used to connect to the computer as a different user.
.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'.
.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: Privilege
Author: Klaas Vandenberghe ( @PowerDBAKlaas )
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaPrivilege
.EXAMPLE
PS C:\> Set-DbaPrivilege -ComputerName sqlserver2014a -Type LPIM,IFI
Adds the SQL Service account(s) on computer sqlserver2014a to the local privileges 'SeManageVolumePrivilege' and 'SeLockMemoryPrivilege'.
.EXAMPLE
PS C:\> '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(SupportsShouldProcess)]
param (
[parameter(ValueFromPipeline)]
[Alias("cn", "host", "Server")]
[dbainstanceparameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[Parameter(Mandatory)]
[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) {
if ($Pscmdlet.ShouldProcess($computer, "Setting Privilege for SQL Service Account")) {
try {
$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-DbaService -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
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose "Added $acc to Batch Logon Privileges on $env:ComputerName"
} else {
<# DO NOT use Write-Message as this is inside of a script block #>
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
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose "Added $acc to Instant File Initialization Privileges on $env:ComputerName"
} else {
<# DO NOT use Write-Message as this is inside of a script block #>
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
<# DO NOT use Write-Message as this is inside of a script block #>
Write-Verbose "Added $acc to Lock Pages in Memory Privileges on $env:ComputerName"
} else {
<# DO NOT use Write-Message as this is inside of a script block #>
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 -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
The target SQL Server instance or instances. 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 objects 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaSpConfigure
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Set-DbaSpConfigure -SqlInstance localhost -Name XPCmdShellEnabled -Value 1
Adjusts the xp_cmdshell configuration value to 1.
.EXAMPLE
PS C:\> 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
$currentConfigValue = $configobject.ConfiguredValue
$minValue = $configobject.MinValue
$maxValue = $configobject.MaxValue
$isDynamic = $configobject.IsDynamic
$configuration = $configobject.Name
#Let us not waste energy setting the value to itself
if ($currentConfigValue -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 $currentConfigValue to $value.")) {
try {
$server.Configuration.$configuration.ConfigValue = $value
$server.Configuration.Alter()
[pscustomobject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
ConfigName = $configuration
PreviousValue = $currentConfigValue
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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaSpn
.EXAMPLE
PS C:\> Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account
PS C:\> Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -EnableException
Connects to Active Directory and adds a provided SPN to the given account.
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
PS C:\> Set-DbaSpn -SPN MSSQLSvc\SQLSERVERA.domain.something -ServiceAccount domain\account -Credential ad\sqldba
Connects to Active Directory and adds a provided SPN to the given account. Uses alternative account to connect to AD.
.EXAMPLE
PS C:\> 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
PS C:\> Test-DbaSpn -ComputerName sql2016 | Where { $_.isSet -eq $false } | Set-DbaSpn
Sets all missing SPNs for sql2016
.EXAMPLE
PS C:\> 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, DefaultParameterSetName = "Default")]
param (
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[Alias("RequiredSPN")]
[string]$SPN,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[Alias("InstanceServiceAccount", "AccountName")]
[string]$ServiceAccount,
[Parameter(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: Startup, Parameter, Configure
Author: Stuart Moore (@napalmgram), stuart-moore.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser
Will configure the SQL Instance server1\instance1 to startup up in Single User mode at next startup
.EXAMPLE
PS C:\> Set-DbaStartupParameter -SqlInstance sql2016 -IncreasedExtents
Will configure the SQL Instance sql2016 to IncreasedExtents = True (-E)
.EXAMPLE
PS C:\> 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
PS C:\> Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser -TraceFlags 8032,8048
This will append Trace Flags 8032 and 8048 to the startup parameters
.EXAMPLE
PS C:\> Set-DbaStartupParameter -SqlInstance sql2016 -SingleUser:$false -TraceFlagsOverride
This will remove all trace flags and set SingleUser to false
.EXAMPLE
PS C:\> 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
PS C:\> Set-DbaStartupParameter -SqlInstance sql2016 -SingleUser:$false -TraceFlagsOverride -Offline
This will remove all trace flags and set SingleUser to false from an offline instance
.EXAMPLE
PS C:\> 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
PS C:\> $StartupConfig = Get-DbaStartupParameter -SqlInstance server1\instance1
PS C:\> Set-DbaStartupParameter -SqlInstance server1\instance1 -SingleUser -NoLoggingToWinEvents
PS C:\> #Restart your SQL instance with the tool of choice
PS C:\> #Do Some work
PS C:\> Set-DbaStartupParameter -SqlInstance server1\instance1 -StartUpConfig $StartUpConfig
PS C:\> #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, ConfirmImpact = "High")]
param ([parameter(Mandatory)]
[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 {
$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 $SqlInstance -Credential $Credential
$originalparamstring = $currentstartup.ParameterString
Write-Message -Level Verbose -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-DbaPath -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-DbaPath -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-DbaPath -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
if ($instancename.Length -eq 0) { $instancename = "MSSQLSERVER" }
$displayname = "SQL Server ($instancename)"
$Scriptblock = {
#Variable marked as unused by PSScriptAnalyzer
#$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) {
#Variable $response marked as unused by PSScriptAnalyzer replace with $null to catch output
$null = Invoke-ManagedComputerCommand -ComputerName $server.ComputerName -Credential $Credential -ScriptBlock $Scriptblock -ArgumentList $server.ComputerName, $displayname, $ParameterString -EnableException
$output = Get-DbaStartupParameter -SqlInstance $server.ComputerName -Credential $Credential -EnableException
Add-Member -Force -InputObject $output -MemberType NoteProperty -Name OriginalStartupParameters -Value $originalparamstring
} else {
#Variable $response marked as unused by PSScriptAnalyzer replace with $null to catch output
$null = Invoke-ManagedComputerCommand -ComputerName $server.ComputerName -ScriptBlock $Scriptblock -ArgumentList $server.ComputerName, $displayname, $ParameterString -EnableException
$output = Get-DbaStartupParameter -SqlInstance $server.ComputerName -EnableException
Add-Member -Force -InputObject $output -MemberType NoteProperty -Name OriginalStartupParameters -Value $originalparamstring
Add-Member -Force -InputObject $output -MemberType NoteProperty -Name Notes -Value "Startup parameters changed on $SqlInstance. You must restart SQL Server for changes to take effect."
}
$output
} 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 target SQL Server instance or instances.
.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 (like SQL Configuration Manager)
.PARAMETER Port
TCPPort that SQLService should listen on.
.PARAMETER IpAddress
Which IpAddress should the portchange , if omitted allip (0.0.0.0) will be changed with the new port number.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: @H0s0n77
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaTcpPort
.EXAMPLE
PS C:\> Set-DbaTcpPort -SqlInstance sql2017 -Port 1433
Sets the port number 1433 for all IP Addresses on the default instance on sql2017. Prompts for confirmation.
.EXAMPLE
PS C:\> Set-DbaTcpPort -SqlInstance winserver\sqlexpress -IpAddress 192.168.1.22 -Port 1433 -Confirm:$false
Sets the port number 1433 for IP 192.168.1.22 on the sqlexpress instance on winserver. Does not prompt for confirmation.
.EXAMPLE
PS C:\> Set-DbaTcpPort -SqlInstance sql2017, sql2019 -port 1337 -Credential ad\dba
Sets the port number 1337 for all IP Addresses on SqlInstance sql2017 and sql2019 using the credentials for ad\dba. Prompts for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$Credential,
[parameter(Mandatory)]
[ValidateRange(1, 65535)]
[int]$Port,
[IpAddress[]]$IpAddress,
[switch]$EnableException
)
begin {
if (-not $IpAddress) {
$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
}
}
$scriptblock = {
$computername = $args[0]
$wmiinstancename = $args[1]
$port = $args[2]
$IpAddress = $args[3]
$sqlinstanceName = $args[4]
$wmi = New-Object Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer $computername
$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 = $computername
InstanceName = $wmiinstancename
SqlInstance = $sqlinstanceName
PreviousPortNumber = $oldport
PortNumber = $Port
Status = "Success"
}
} catch {
[pscustomobject]@{
ComputerName = $computername
InstanceName = $wmiinstancename
SqlInstance = $sqlinstanceName
PreviousPortNumber = $oldport
PortNumber = $Port
Status = "Failed: $_"
}
}
}
}
process {
if (Test-FunctionInterrupt) {
return
}
foreach ($instance in $SqlInstance) {
$wmiinstancename = $instance.InstanceName
$computerName = $instance.ComputerName
if ($Pscmdlet.ShouldProcess($computerName, "Setting port to $Port for $wmiinstancename")) {
try {
$computerName = $instance.ComputerName
$resolved = Resolve-DbaNetworkName -ComputerName $computerName
Invoke-ManagedComputerCommand -ComputerName $resolved.FullComputerName -ScriptBlock $scriptblock -ArgumentList $instance.ComputerName, $wmiinstancename, $port, $IpAddress, $instance.InputObject -Credential $Credential
} catch {
try {
Invoke-ManagedComputerCommand -ComputerName $instance.ComputerName -ScriptBlock $scriptblock -ArgumentList $instance.ComputerName, $wmiinstancename, $port, $IpAddress, $instance.InputObject -Credential $Credential
} catch {
Stop-Function -Message "Failure setting port to $Port for $wmiinstancename on $computerName" -Continue
}
}
}
}
}
}
function Set-DbaTempdbConfig {
<#
.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 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 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 DataFileSize
Specifies the total data file size in megabytes. This is distributed across the total number of data files.
.PARAMETER LogFileSize
Specifies the log file size in megabytes. If not specified, this will be set to 25% of total data file size.
.PARAMETER DataFileGrowth
Specifies the growth amount for the data file(s) in megabytes. The default is 512 MB.
.PARAMETER LogFileGrowth
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 -DataFileGrowth and -LogFileGrowth.
.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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Set-DbaTempdbConfig
.EXAMPLE
PS C:\> Set-DbaTempdbConfig -SqlInstance localhost -DataFileSize 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
PS C:\> Set-DbaTempdbConfig -SqlInstance localhost -DataFileSize 1000 -DataFileCount 8
Creates tempdb with 8 data files, each one sized at 125MB, with a log file of 250MB.
.EXAMPLE
PS C:\> Set-DbaTempdbConfig -SqlInstance localhost -DataFileSize 1000 -OutputScriptOnly
Provides a SQL script output to configure tempdb according to the passed parameters.
.EXAMPLE
PS C:\> Set-DbaTempdbConfig -SqlInstance localhost -DataFileSize 1000 -DisableGrowth
Disables the growth for the data and log files.
.EXAMPLE
PS C:\> Set-DbaTempdbConfig -SqlInstance localhost -DataFileSize 1000 -OutputScriptOnly
Returns the T-SQL script representing tempdb configuration.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[parameter(Mandatory)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[int]$DataFileCount,
[Parameter(Mandatory)]
[int]$DataFileSize,
[int]$LogFileSize,
[int]$DataFileGrowth = 512,
[int]$LogFileGrowth = 512,
[string]$DataPath,
[string]$LogPath,
[string]$OutFile,
[switch]$OutputScriptOnly,
[switch]$DisableGrowth,
[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
}
$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
}
$DataFilesizeSingle = $([Math]::Floor($DataFileSize / $DataFileCount))
Write-Message -Message "Single data file size (MB): $DataFilesizeSingle." -Level Verbose
if (Test-Bound -ParameterName DataPath) {
if ((Test-DbaPath -SqlInstance $server -Path $DataPath) -eq $false) {
Stop-Function -Message "$datapath is an invalid path." -Continue
}
} 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 (Test-Bound -ParameterName LogPath) {
if ((Test-DbaPath -SqlInstance $server -Path $LogPath) -eq $false) {
Stop-Function -Message "$LogPath is an invalid path." -Continue
}
} 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) {
$DataFileGrowth = 0
$LogFileGrowth = 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 > $DataFilesizeSingle AND type = 0").Tables[0].Rows[0].Size
if ($CurrentFileCount -gt $DataFileCount) {
Stop-Function -Message "Current tempdb in $instance is not suitable to be reconfigured. The current tempdb has a greater number of files ($CurrentFileCount) than the calculated configuration ($DataFileCount)." -Continue
}
if ($TooBigCount) {
Stop-Function -Message "Current tempdb in $instance is not suitable to be reconfigured. The current tempdb ($TooBigCount MB) is larger than the calculated individual file configuration ($DataFilesizeSingle MB)." -Continue
}
$EqualCount = $server.Databases['tempdb'].ExecuteWithResults("SELECT count(1) as FileCount FROM sys.database_files WHERE size/128 = $DataFilesizeSingle AND type = 0").Tables[0].Rows[0].FileCount
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=$DataFilesizeSingle MB,filegrowth=$DataFileGrowth);"
} else {
$NewName = "tempdev$i.ndf"
$NewPath = "$datapath\$NewName"
$sql += "ALTER DATABASE tempdb ADD FILE(name=tempdev$i,filename='$NewPath',size=$DataFilesizeSingle MB,filegrowth=$DataFileGrowth);"
}
}
if (-not $LogFileSize) {
$LogFileSize = [Math]::Floor($DataFileSize / 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=$LogFileSize MB,filegrowth=$LogFileGrowth);"
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($instance, "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
DataFileSize = [dbasize]($DataFileSize * 1024 * 1024)
SingleDataFileSize = [dbasize]($DataFilesizeSingle * 1024 * 1024)
LogSize = [dbasize]($LogFileSize * 1024 * 1024)
DataPath = $DataPath
LogPath = $LogPath
DataFileGrowth = [dbasize]($DataFileGrowth * 1024 * 1024)
LogFileGrowth = [dbasize]($LogFileGrowth * 1024 * 1024)
}
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 -ErrorRecord $_ -Continue
}
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Set-SqlTempDbConfiguration
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Set-DbaTempDbConfiguration
}
}
function Show-DbaDbList {
<#
.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 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 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, FileSystem
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Show-DbaDbList
.EXAMPLE
PS C:\> Show-DbaDbList -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
PS C:\> Show-DbaDbList -SqlInstance 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.
.EXAMPLE
PS C:\> Show-DbaDbList -SqlInstance sqlserver2014a -DefaultDb master
Shows a GUI list of databases using Windows Authentication to connect to the SQL Server. The "master" database will be selected when the lists shows. Returns a string of the selected database.
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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
#Variable marked as unused by PSScriptAnalyzer
#$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
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Show-DbaDatabaseList
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
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 target SQL Server instance or instances. Defaults to localhost.
.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: Storage, FileSystem
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Show-DbaServerFileSystem
.EXAMPLE
PS C:\> Show-DbaServerFileSystem -SqlInstance sql2017
Shows a list of databases using Windows Authentication to connect to the SQL Server. Returns a string of the selected path.
.EXAMPLE
PS C:\> Show-DbaServerFileSystem -SqlInstance sql2017 -SqlCredential $cred
Shows a list of databases using SQL credentials to connect to the SQL Server. Returns a string of the selected path.
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter]$SqlInstance,
[PSCredential]$SqlCredential,
[switch]$EnableException
)
begin {
try {
Add-Type -AssemblyName PresentationFramework
} catch {
Stop-Function -Message "Windows Presentation Framework required but not installed."
return
}
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 = $server.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="
}
process {
if (Test-FunctionInterrupt) { return }
try {
$server = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance
return
}
# 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 = ($server.EnumAvailableMedia()).Name
} catch {
Stop-Function -Message "No access to remote SQL Server files." -Target $SqlInstance
return
}
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
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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Start-DbaAgentJob
.EXAMPLE
PS C:\> Start-DbaAgentJob -SqlInstance localhost
Starts all running SQL Agent Jobs on the local SQL Server instance
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture | Start-DbaAgentJob
Starts the cdc.DBWithCDC_capture SQL Agent Job on sql2016
.EXAMPLE
PS C:\> Start-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture
Starts the cdc.DBWithCDC_capture SQL Agent Job on sql2016
.EXAMPLE
PS C:\> $servers | Find-DbaAgentJob -IsFailed | Start-DbaAgentJob
Restarts all failed jobs on all servers in the $servers collection
.EXAMPLE
PS C:\> 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 {
$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
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Start-DbaEndpoint {
<#
.SYNOPSIS
Starts endpoints on a SQL Server instance.
.DESCRIPTION
Starts endpoints on a SQL Server instance.
.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 Endpoint
Only start specific endpoints.
.PARAMETER AllEndpoints
Start all endpoints on an instance.
.PARAMETER InputObject
Enables piping from Get-Endpoint.
.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: Endpoint
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Start-DbaEndpoint
.EXAMPLE
PS C:\> Start-DbaEndpoint -SqlInstance sqlserver2012 -AllEndpoints
Starts all endpoints on the sqlserver2014 instance.
.EXAMPLE
PS C:\> Start-DbaEndpoint -SqlInstance sqlserver2012 -Endpoint endpoint1,endpoint2 -SqlCredential sqladmin
Logs into sqlserver2012 using alternative credentials and starts the endpoint1 and endpoint2 endpoints.
.EXAMPLE
PS C:\> Get-Endpoint -SqlInstance sqlserver2012 -Endpoint endpoint1 | Start-DbaEndpoint
Starts the endpoints returned from the Get-Endpoint function.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$EndPoint,
[switch]$AllEndpoints,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Endpoint[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -And (Test-Bound -Not -ParameterName Endpoint, AllEndpoints)) {
Stop-Function -Message "You must specify AllEndpoints or Endpoint when using the SqlInstance parameter."
return
}
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaEndpoint -SqlInstance $instance -SqlCredential $SqlCredential -EndPoint $Endpoint
}
foreach ($ep in $InputObject) {
try {
if ($Pscmdlet.ShouldProcess($ep.Parent.Name, "Starting $ep")) {
$ep.Start()
$ep
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
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 Exclude or 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 -Exclude Databases to skip.
All logins. Use -Exclude Logins to skip.
All database mail objects. Use -Exclude DatabaseMail
All credentials. Use -Exclude Credentials to skip.
All objects within the Job Server (SQL Agent). Use -Exclude AgentServer to skip.
All linked servers. Use -Exclude LinkedServers to skip.
All groups and servers within Central Management Server. Use -Exclude CentralManagementServer to skip.
All SQL Server configuration objects (everything in sp_configure). Use -Exclude SpConfigure to skip.
All user objects in system databases. Use -Exclude SysDbUserObjects to skip.
All system triggers. Use -Exclude SystemTriggers to skip.
All system backup devices. Use -Exclude BackupDevices to skip.
All Audits. Use -Exclude Audits to skip.
All Endpoints. Use -Exclude Endpoints to skip.
All Extended Events. Use -Exclude ExtendedEvents to skip.
All Policy Management objects. Use -Exclude PolicyManagement to skip.
All Resource Governor objects. Use -Exclude ResourceGovernor to skip.
All Server Audit Specifications. Use -Exclude ServerAuditSpecifications to skip.
All Custom Errors (User Defined Messages). Use -Exclude CustomErrors to skip.
Copies All Data Collector collection sets. Does not configure the server. Use -Exclude DataCollector 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 -ExcludeBackupCleanup).
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 -SharedPath with a valid UNC format as well (\\server\share).
.PARAMETER SharedPath
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 Exclude
Exclude one or more objects to migrate
Databases
Logins
AgentServer
Credentials
LinkedServers
SpConfigure
CentralManagementServer
DatabaseMail
SysDbUserObjects
SystemTriggers
BackupDevices
Audits
Endpoints
ExtendedEvents
PolicyManagement
ResourceGovernor
ServerAuditSpecifications
CustomErrors
DataCollector
.PARAMETER ExcludeSaRename
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 UseLastBackup
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 Continue
If specified, will to attempt to restore transaction log backups on top of existing database(s) in Recovering or Standby states. Only usable with -UseLastBackup
.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 (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Start-DbaMigration
.EXAMPLE
PS C:\> 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
PS C:\> $params = @{
>> Source = "sqlcluster"
>> Destination = "sql2016"
>> SourceSqlCredential = $scred
>> DestinationSqlCredential = $cred
>> SharedPath = "\\fileserver\share\sqlbackups\Migration"
>> BackupRestore = $true
>> ReuseSourceFolderStructure" = $true
>> Force = $true
>> }
>>
PS C:\> Start-DbaMigration @params -Verbose
Utilizes splatting technique to set all the needed parameters. This will migrate databases using the backup/restore method. It will also include migration of the logins, database mail configuration, credentials, SQL Agent, Central Management Server, and SQL Server global configuration.
.EXAMPLE
PS C:\> Start-DbaMigration -Verbose -Source sqlcluster -Destination sql2016 -DetachAttach -Reattach -SetSourceReadonly
Migrates databases using detach/copy/attach. Reattach at source and set source databases read-only. Also migrates everything else.
.EXAMPLE
PS C:\> $PSDefaultParameters = @{
>> "dbatools:Source" = "sqlcluster"
>> "dbatools:Destination" = "sql2016"
>> }
>>
PS C:\> Start-DbaMigration -Verbose -Exclude Databases, Logins
Utilizes the PSDefaultParameterValues system variable, and sets the Source and Destination parameters for any function in the module that has those parameter names. This prevents the need from passing them in constantly.
The execution of the function will migrate everything but logins and databases.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[DbaInstanceParameter]$Source,
[DbaInstanceParameter[]]$Destination,
[switch]$DetachAttach,
[switch]$Reattach,
[switch]$BackupRestore,
[Alias("NetworkShare")]
[parameter(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]$SharedPath,
[switch]$WithReplace,
[switch]$NoRecovery,
[switch]$SetSourceReadOnly,
[switch]$ReuseSourceFolderStructure,
[switch]$IncludeSupportDbs,
[PSCredential]$SourceSqlCredential,
[PSCredential]$DestinationSqlCredential,
[ValidateSet('Databases', 'Logins', 'AgentServer', 'Credentials', 'LinkedServers', 'SpConfigure', 'CentralManagementServer', 'DatabaseMail', 'SysDbUserObjects', 'SystemTriggers', 'BackupDevices', 'Audits', 'Endpoints', 'ExtendedEvents', 'PolicyManagement', 'ResourceGovernor', 'ServerAuditSpecifications', 'CustomErrors', 'DataCollector')]
[string[]]$Exclude,
[switch]$DisableJobsOnDestination,
[switch]$DisableJobsOnSource,
[switch]$ExcludeSaRename,
[switch]$UseLastBackup,
[switch]$Continue,
[switch]$Force,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter NetworkShare -CustomMessage "Using the parameter NetworkShare is deprecated. This parameter will be removed in version 1.0.0 or before. Use SharedPath instead."
if ($Exclude -notcontains "Databases") {
if (-not $BackupRestore -and -not $DetachAttach) {
Stop-Function -Message "You must specify a database migration method (-BackupRestore or -DetachAttach) or -Exclude Databases"
return
}
}
$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
$started = Get-Date
$sourceserver = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
if ($BackupRestore -and (-not $SharedPath -and -not $UseLastBackup)) {
Stop-Function -Message "When using -BackupRestore, you must specify -SharedPath or -UseLastBackup"
return
}
if ($SharedPath -and $UseLastBackup) {
Stop-Function -Message "-SharedPath cannot be used with -UseLastBackup 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 ($Continue -and -not $UseLastBackup) {
Stop-Function -Message "-Continue cannot be used without -UseLastBackup"
return
}
}
process {
if (Test-FunctionInterrupt) { return }
# testing twice for whatif reasons
if ($BackupRestore -and (-not $SharedPath -and -not $UseLastBackup)) {
Stop-Function -Message "When using -BackupRestore, you must specify -SharedPath or -UseLastBackup"
return
}
if ($SharedPath -and $UseLastBackup) {
Stop-Function -Message "-SharedPath cannot be used with -UseLastBackup 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 ($Exclude -notcontains 'SpConfigure') {
Write-Message -Level Verbose -Message "Migrating SQL Server Configuration"
Copy-DbaSpConfigure -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential
}
if ($Exclude -notcontains 'CustomErrors') {
Write-Message -Level Verbose -Message "Migrating custom errors (user defined messages)"
Copy-DbaCustomError -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'Credentials') {
Write-Message -Level Verbose -Message "Migrating SQL credentials"
Copy-DbaCredential -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'DatabaseMail') {
Write-Message -Level Verbose -Message "Migrating database mail"
Copy-DbaDbMail -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'CentralManagementServer') {
Write-Message -Level Verbose -Message "Migrating Central Management Server"
Copy-DbaCmsRegServer -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'BackupDevices') {
Write-Message -Level Verbose -Message "Migrating Backup Devices"
Copy-DbaBackupDevice -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'SystemTriggers') {
Write-Message -Level Verbose -Message "Migrating System Triggers"
Copy-DbaServerTrigger -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'Databases') {
# Do it
Write-Message -Level Verbose -Message "Migrating databases"
if ($BackupRestore) {
if ($UseLastBackup) {
Copy-DbaDatabase -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -AllDatabases -SetSourceReadOnly:$SetSourceReadOnly -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -BackupRestore -Force:$Force -NoRecovery:$NoRecovery -WithReplace:$WithReplace -IncludeSupportDbs:$IncludeSupportDbs -UseLastBackup:$UseLastBackup -Continue:$Continue
} else {
Copy-DbaDatabase -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -AllDatabases -SetSourceReadOnly:$SetSourceReadOnly -ReuseSourceFolderStructure:$ReuseSourceFolderStructure -BackupRestore -SharedPath $SharedPath -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 ($Exclude -notcontains 'Logins') {
Write-Message -Level Verbose -Message "Migrating logins"
$syncit = $ExcludeSaRename -eq $false
Copy-DbaLogin -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force -SyncSaName:$syncit
}
if ($Exclude -notcontains 'Logins' -and $Exclude -notcontains 'Databases' -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 ($Exclude -notcontains 'LinkedServers') {
Write-Message -Level Verbose -Message "Migrating linked servers"
Copy-DbaLinkedServer -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'DataCollector') {
Write-Message -Level Verbose -Message "Migrating Data Collector collection sets"
Copy-DbaDataCollector -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'Audits') {
Write-Message -Level Verbose -Message "Migrating Audits"
Copy-DbaServerAudit -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'ServerAuditSpecifications') {
Write-Message -Level Verbose -Message "Migrating Server Audit Specifications"
Copy-DbaServerAuditSpecification -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'Endpoints') {
Write-Message -Level Verbose -Message "Migrating Endpoints"
Copy-DbaEndpoint -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'PolicyManagement') {
Write-Message -Level Verbose -Message "Migrating Policy Management"
Copy-DbaPolicyManagement -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'ResourceGovernor') {
Write-Message -Level Verbose -Message "Migrating Resource Governor"
Copy-DbaResourceGovernor -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'SysDbUserObjects') {
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 ($Exclude -notcontains 'ExtendedEvents') {
Write-Message -Level Verbose -Message "Migrating Extended Events"
Copy-DbaXESession -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
}
if ($Exclude -notcontains 'AgentServer') {
Write-Message -Level Verbose -Message "Migrating job server"
Copy-DbaAgentServer -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"
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Start-DbaPfDataCollectorSet
.EXAMPLE
PS C:\> Start-DbaPfDataCollectorSet
Attempts to start all ready Collectors on localhost.
.EXAMPLE
PS C:\> Start-DbaPfDataCollectorSet -ComputerName sql2017
Attempts to start all ready Collectors on localhost.
.EXAMPLE
PS C:\> Start-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential ad\sqldba -CollectorSet 'System Correlation'
Starts the 'System Correlation' Collector on sql2017 and sql2016 using alternative credentials.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Start-DbaPfDataCollectorSet
Starts the 'System Correlation' Collector.
#>
[CmdletBinding(SupportsShouldProcess)]
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 ($Pscmdlet.ShouldProcess($computer, "Starting Performance Monitor collection set")) {
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
}
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-DbaService {
<#
.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 target SQL Server instance or 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-DbaService
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires Local Admin rights on destination computer(s).
.LINK
https://dbatools.io/Start-DbaService
.EXAMPLE
PS C:\> Start-DbaService -ComputerName sqlserver2014a
Starts the SQL Server related services on computer sqlserver2014a.
.EXAMPLE
PS C:\> 'sql1','sql2','sql3'| Get-DbaService | Start-DbaService
Gets the SQL Server related services on computers sql1, sql2 and sql3 and starts them.
.EXAMPLE
PS C:\> Start-DbaService -ComputerName sql1,sql2 -Instance MSSQLSERVER
Starts the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.
.EXAMPLE
PS C:\> Start-DbaService -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)]
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, Mandatory, ParameterSetName = "Service")]
[Alias("ServiceCollection")]
[object[]]$InputObject,
[int]$Timeout = 60,
[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.EnableException = $EnableException }
$InputObject = Get-DbaService @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 ($PSCmdlet.ShouldProcess("$ProcessArray", "Starting Service")) {
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
}
}
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Start-DbaSqlService
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 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: Security, Trace
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Start-DbaTrace -SqlInstance sql2008
Starts all traces on sql2008
.EXAMPLE
PS C:\> Start-DbaTrace -SqlInstance sql2008 -Id 1
Starts all trace with ID 1 on sql2008
.EXAMPLE
PS C:\> Get-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Start-DbaTrace
Starts selected traces on sql2008
#>
[CmdletBinding(SupportsShouldProcess)]
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"
if ($Pscmdlet.ShouldProcess($traceid, "Starting the TraceID on $server")) {
try {
$server.Query($sql)
Get-DbaTrace -SqlInstance $server -Id $traceid
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $server -Continue
return
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Start-DbaXESession {
<#
.SYNOPSIS
Starts Extended Events sessions.
.DESCRIPTION
This script starts Extended Events sessions on a SQL Server instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 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
Author: Doug Meyers
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Start-DbaXESession
.EXAMPLE
PS C:\> Start-DbaXESession -SqlInstance sqlserver2012 -AllSessions
Starts all Extended Event Session on the sqlserver2014 instance.
.EXAMPLE
PS C:\> Start-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2
Starts the xesession1 and xesession2 Extended Event sessions.
.EXAMPLE
PS C:\> 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
PS C:\> Get-DbaXESession -SqlInstance sqlserver2012 -Session xesession1 | Start-DbaXESession
Starts the sessions returned from the Get-DbaXESession function.
#>
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Session')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Internal functions are ignored")]
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(SupportsShouldProcess)]
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."
if ($Pscmdlet.ShouldProcess("$instance", "Starting XEvent Session $session")) {
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(SupportsShouldProcess)]
param (
[Microsoft.SqlServer.Management.XEvent.Session[]]$xeSessions,
[datetime]$StopAt
)
foreach ($xe in $xeSessions) {
$server = $xe.Parent
$session = $xe.Name
$name = "XE Session Stop - $session"
if ($Pscmdlet.ShouldProcess("$Server", "Making New XEvent StopJob for $session")) {
# Setup the schedule time
$time = $(($StopAt).ToString("HHmmss"))
# Create the schedule
$schedule = New-DbaAgentSchedule -SqlInstance $server -Schedule $name -FrequencyType Once -StartTime $time -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;"
#Variable $jobstep marked as unused by PSScriptAnalyzer replace with $null to catch output
$null = 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 }
}
if ($Pscmdlet.ShouldProcess("$instance", "Configuring XEvent Session $session to start")) {
Start-XESessions $xeSessions
if ($StopAt) {
New-StopJob -xeSessions $xeSessions -StopAt $stopat
}
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. 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 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
Author: Chrissy LeMaire (@cl) | SmartTarget by Gianluca Sartori (@spaghettidba)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Start-DbaXESmartTarget
.EXAMPLE
PS C:\>$response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query "update table set whatever = 1"
PS C:\>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
PS C:\>$response = New-DbaXESmartQueryExec -SqlInstance sql2017 -Database dbadb -Query "update table set whatever = 1"
PS C:\>$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"
>> }
PS C:\> $emailresponse = New-DbaXESmartEmail @params
PS C:\> 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
PS C:\> $columns = "cpu_time", "duration", "physical_reads", "logical_reads", "writes", "row_count", "batch_text"
PS C:\> $response = New-DbaXESmartTableWriter -SqlInstance sql2017 -Database dbadb -Table deadlocktracker -OutputColumns $columns -Filter "duration > 10000"
PS C:\> Start-DbaXESmartTarget -SqlInstance sql2017 -Session deadlock_tracker -Responder $response
Writes Extended Events to the deadlocktracker table in dbadb on sql2017.
#>
[CmdletBinding(SupportsShouldProcess)]
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(SupportsShouldProcess)]
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 {
$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)
}
if ($Pscmdlet.ShouldProcess("$instance", "Starting SmartTarget on $($server.name)")) {
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 ($Pscmdlet.ShouldProcess("$instance", "Configuring SmartTarget to start")) {
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) {
if ($property.Value -is [Array]) {
$name = $property.Name
$newResponder.$name = [object[]]$property.Value
} else {
$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
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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Stop-DbaAgentJob
.EXAMPLE
PS C:\> Stop-DbaAgentJob -SqlInstance localhost
Stops all running SQL Agent Jobs on the local SQL Server instance
.EXAMPLE
PS C:\> Get-DbaAgentJob -SqlInstance sql2016 -Job cdc.DBWithCDC_capture | Stop-DbaAgentJob
Stops the cdc.DBWithCDC_capture SQL Agent Job on sql2016
.EXAMPLE
PS C:\> 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) {
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
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Stop-DbaEndpoint {
<#
.SYNOPSIS
Stops endpoints on a SQL Server instance.
.DESCRIPTION
Stops endpoints on a SQL Server instance.
.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 Endpoint
Only stop specific endpoints.
.PARAMETER AllEndpoints
Stops all endpoints on an instance.
.PARAMETER InputObject
Enables piping from Get-Endpoint.
.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: Endpoint
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Stop-DbaEndpoint
.EXAMPLE
PS C:\> Stop-DbaEndpoint -SqlInstance sql2017a -AllEndpoints
Stops all endpoints on the sqlserver2014 instance.
.EXAMPLE
PS C:\> Stop-DbaEndpoint -SqlInstance sql2017a -Endpoint endpoint1,endpoint2
Stops the endpoint1 and endpoint2 endpoints.
.EXAMPLE
PS C:\> Get-Endpoint -SqlInstance sql2017a -Endpoint endpoint1 | Stop-DbaEndpoint
Stops the endpoints returned from the Get-Endpoint command.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$EndPoint,
[switch]$AllEndpoints,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Endpoint[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance) -And (Test-Bound -Not -ParameterName Endpoint, AllEndpoints)) {
Stop-Function -Message "You must specify AllEndpoints or Endpoint when using the SqlInstance parameter."
return
}
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaEndpoint -SqlInstance $instance -SqlCredential $SqlCredential -EndPoint $Endpoint
}
foreach ($ep in $InputObject) {
try {
if ($Pscmdlet.ShouldProcess($ep.Parent.Name, "Stopping $ep")) {
$ep.Stop()
$ep
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Stop-DbaPfDataCollectorSet
.EXAMPLE
PS C:\> Stop-DbaPfDataCollectorSet
Attempts to stop all ready Collectors on localhost.
.EXAMPLE
PS C:\> Stop-DbaPfDataCollectorSet -ComputerName sql2017
Attempts to stop all ready Collectors on localhost.
.EXAMPLE
PS C:\> Stop-DbaPfDataCollectorSet -ComputerName sql2017, sql2016 -Credential ad\sqldba -CollectorSet 'System Correlation'
Stops the 'System Correlation' Collector on sql2017 and sql2016 using alternative credentials.
.EXAMPLE
PS C:\> Get-DbaPfDataCollectorSet -CollectorSet 'System Correlation' | Stop-DbaPfDataCollectorSet
Stops the 'System Correlation' Collector.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[Alias("DataCollectorSet")]
[string[]]$CollectorSet,
[parameter(ValueFromPipeline)]
[object[]]$InputObject,
[switch]$NoWait,
[switch]$EnableException
)
begin {
#Variable marked as unused by PSScriptAnalyzer
#$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
}
if ($Pscmdlet.ShouldProcess($computer, "Stoping Performance Monitor collection set")) {
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 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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Stop-DbaProcess
.EXAMPLE
PS C:\> 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
PS C:\> Stop-DbaProcess -SqlInstance sqlserver2014a -SqlCredential $credential -Spid 56, 77
Finds processes for spid 56 and 57, then kills them. Uses alternative (SQL or Windows) credentials to login to sqlserver2014a.
.EXAMPLE
PS C:\> Stop-DbaProcess -SqlInstance sqlserver2014a -Program 'Microsoft SQL Server Management Studio'
Finds processes that were created in Microsoft SQL Server Management Studio, then kills them.
.EXAMPLE
PS C:\> Stop-DbaProcess -SqlInstance sqlserver2014a -Hostname workstationx, server100
Finds processes that were initiated (computers/clients) workstationx and server 1000, then kills them.
.EXAMPLE
PS C:\> Stop-DbaProcess -SqlInstance sqlserver2014 -Database tempdb -WhatIf
Shows what would happen if the command were executed.
.EXAMPLE
PS C:\> Get-DbaProcess -SqlInstance sql2016 -Program '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, Mandatory, 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-DbaService {
<#
.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 target SQL Server instance or 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-DbaService
.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, Stop
Author: Kirill Kravtsov (@nvarscar)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires Local Admin rights on destination computer(s).
.LINK
https://dbatools.io/Stop-DbaService
.EXAMPLE
PS C:\> Stop-DbaService -ComputerName sqlserver2014a
Stops the SQL Server related services on computer sqlserver2014a.
.EXAMPLE
PS C:\> 'sql1','sql2','sql3'| Get-DbaService | Stop-DbaService
Gets the SQL Server related services on computers sql1, sql2 and sql3 and stops them.
.EXAMPLE
PS C:\> Stop-DbaService -ComputerName sql1,sql2 -Instance MSSQLSERVER
Stops the SQL Server services related to the default instance MSSQLSERVER on computers sql1 and sql2.
.EXAMPLE
PS C:\> Stop-DbaService -ComputerName $MyServers -Type SSRS
Stops the SQL Server related services of type "SSRS" (Reporting Services) on computers in the variable MyServers.
.EXAMPLE
PS C:\> Stop-DbaService -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)]
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, Mandatory, ParameterSetName = "Service")]
[Alias("ServiceCollection")]
[object[]]$InputObject,
[int]$Timeout = 60,
[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-DbaService @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-DbaService
$serviceParams = @{
ComputerName = $service.ComputerName
InstanceName = $service.InstanceName
Type = 'Agent'
}
if ($Credential) { $serviceParams.Credential = $Credential }
if ($EnableException) { $serviceParams.EnableException = $EnableException }
$processArray += @(Get-DbaService @serviceParams)
}
}
if ($PSCmdlet.ShouldProcess("$ProcessArray", "Stopping Service")) {
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
}
}
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Stop-DbaSqlService
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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 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: Security, Trace
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.EXAMPLE
PS C:\> Stop-DbaTrace -SqlInstance sql2008
Stops all traces on sql2008
.EXAMPLE
PS C:\> Stop-DbaTrace -SqlInstance sql2008 -Id 1
Stops all trace with ID 1 on sql2008
.EXAMPLE
PS C:\> Get-DbaTrace -SqlInstance sql2008 | Out-GridView -PassThru | Stop-DbaTrace
Stops selected traces on sql2008
#>
[CmdletBinding(SupportsShouldProcess)]
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"
if ($Pscmdlet.ShouldProcess($traceid, "Stopping the TraceID on $server")) {
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
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Stop-DbaXESession {
<#
.SYNOPSIS
Stops Extended Events sessions.
.DESCRIPTION
This script stops Extended Events sessions on a SQL Server instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 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
Author: Doug Meyers
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Stop-DbaXESession
.EXAMPLE
PS C:\> Stop-DbaXESession -SqlInstance sqlserver2012 -AllSessions
Stops all Extended Event Session on the sqlserver2014 instance.
.EXAMPLE
PS C:\> Stop-DbaXESession -SqlInstance sqlserver2012 -Session xesession1,xesession2
Stops the xesession1 and xesession2 Extended Event sessions.
.EXAMPLE
PS C:\> Get-DbaXESession -SqlInstance sqlserver2012 -Session xesession1 | Stop-DbaXESession
Stops the sessions returned from the Get-DbaXESession function.
#>
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Session')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Internal functions are ignored")]
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(SupportsShouldProcess)]
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."
if ($Pscmdlet.ShouldProcess("$instance", "Stopping XEvent Session $session")) {
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) {
if ($Pscmdlet.ShouldProcess("Configuring XEvent Sessions to stop")) {
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 }
}
if ($Pscmdlet.ShouldProcess("$instance", "Configuring XEvent Session $xeSessions to Stop")) {
Stop-XESessions $xeSessions
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
Author: Chrissy LeMaire (@cl) | SmartTarget by Gianluca Sartori (@spaghettidba)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Stop-DbaXESmartTarget
https://github.com/spaghettidba/XESmartTarget/wiki
.EXAMPLE
PS C:\> Get-DbaXESmartTarget | Stop-DbaXESmartTarget
Stops all XESmartTarget jobs.
.EXAMPLE
PS C:\> 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 $_
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Suspend-DbaAgDbDataMovement {
<#
.SYNOPSIS
Suspends data movement for an availability group database on a SQL Server instance.
.DESCRIPTION
Suspends data movement for an availability group database on a SQL Server instance.
.PARAMETER SqlInstance
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SqlCredential
Login to the SqlInstance instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Database
The database or databases to suspend movement upon.
.PARAMETER AvailabilityGroup
The availability group where the database movement will be suspended.
.PARAMETER InputObject
Enables piping from Get-DbaAgDatabase
.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: AvailabilityGroup, HA, AG
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Suspend-DbaAgDbDataMovement
.EXAMPLE
PS C:\> Suspend-DbaAgDbDataMovement -SqlInstance sql2017a -AvailabilityGroup ag1 -Database db1, db2
Suspends data movement on db1 and db2 to ag1 on sql2017a. Prompts for confirmation.
.EXAMPLE
PS C:\> Get-DbaAgDatabase -SqlInstance sql2017a, sql2019 | Out-GridView -Passthru | Suspend-DbaAgDbDataMovement -Confirm:$false
Suspends data movement on the selected availability group databases. Does not prompt for confirmation.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string]$AvailabilityGroup,
[string[]]$Database,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityDatabase[]]$InputObject,
[switch]$EnableException
)
process {
if ((Test-Bound -ParameterName SqlInstance)) {
if ((Test-Bound -Not -ParameterName Database) -and (Test-Bound -Not -ParameterName AvailabilityGroup)) {
Stop-Function -Message "You must specify one or more databases and one Availability Groups when using the SqlInstance parameter."
return
}
}
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaAgDatabase -SqlInstance $instance -SqlCredential $SqlCredential -Database $Database
}
foreach ($agdb in $InputObject) {
if ($Pscmdlet.ShouldProcess($ag.Parent.Name, "Seting availability group $db to $($db.Parent.Name)")) {
try {
$null = $agdb.SuspendDataMovement()
$agdb
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Sync-DbaAvailabilityGroup {
<#
.SYNOPSIS
Syncs depdendent objects such as jobs, logins and custom errors for availability groups
.DESCRIPTION
Syncs depdendent objects for availability groups. Such objects include:
SpConfigure
CustomErrors
Credentials
DatabaseMail
LinkedServers
Logins
LoginPermissions
SystemTriggers
DatabaseOwner
AgentCategory
AgentOperator
AgentAlert
AgentProxy
AgentSchedule
AgentJob
Note that any of these can be excluded. For specific object exclusions (such as a single job), using the underlying Copy-Dba* command will be required.
This command does not filter by which logins are in use by the ag databases or which linked servers are used. All objects that are not excluded will be copied like hulk smash.
.PARAMETER Primary
The primary SQL Server instance. Server version must be SQL Server version 2012 or higher.
.PARAMETER PrimarySqlCredential
Login to the primary instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER Secondary
The target SQL Server instance or instances. Server version must be SQL Server version 2012 or higher.
.PARAMETER SecondarySqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER AvailabilityGroup
The name of the Availability Group.
.PARAMETER Exclude
Exclude one or more objects to export
SpConfigure
CustomErrors
Credentials
DatabaseMail
LinkedServers
Logins
LoginPermissions
SystemTriggers
DatabaseOwner
AgentCategory
AgentOperator
AgentAlert
AgentProxy
AgentSchedule
AgentJob
.PARAMETER Login
Specific logins to sync. If unspecified, all logins will be processed.
.PARAMETER ExcludeLogin
Specific logins to exclude when performing the sync. If unspecified, all logins will be processed.
.PARAMETER Job
Specific jobs to sync. If unspecified, all logins will be processed.
.PARAMETER ExcludeJob
Specific jobs to exclude when performing the sync. If unspecified, all logins will be processed.
.PARAMETER InputObject
Enables piping from Get-DbaAvailabilityGroup.
.PARAMETER Force
If this switch is enabled, the objects will dropped and recreated on Destination.
.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: HA
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Sync-DbaAvailabilityGroup
.EXAMPLE
PS C:\> Sync-DbaAvailabilityGroup -Primary sql2016a -AvailabilityGroup db3
Syncs the following on all replicas found in the db3 AG:
SpConfigure, CustomErrors, Credentials, DatabaseMail, LinkedServers
Logins, LoginPermissions, SystemTriggers, DatabaseOwner, AgentCategory,
AgentOperator, AgentAlert, AgentProxy, AgentScheduleAgentJob
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql2016a | Sync-DbaAvailabilityGroup -ExcludeType LoginPermissions, LinkedServers -ExcludeLogin login1, login2 -Job job1, job2
Syncs the following on all replicas found in the db3 AG:
SpConfigure, CustomErrors, Credentials, DatabaseMail, Logins,
SystemTriggers, DatabaseOwner, AgentCategory, AgentOperator
AgentAlert, AgentProxy, AgentScheduleAgentJob.
Copies all logins except for login1 and login2 and only syncs job1 and job2
.EXAMPLE
PS C:\> Get-DbaAvailabilityGroup -SqlInstance sql2016a | Sync-DbaAvailabilityGroup -WhatIf
Shows what would happen if the command were to run but doesn't actually perform the action.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
param (
[DbaInstanceParameter]$Primary,
[PSCredential]$PrimarySqlCredential,
[DbaInstanceParameter[]]$Secondary,
[PSCredential]$SecondarySqlCredential,
[string]$AvailabilityGroup,
[Alias("ExcludeType")]
[ValidateSet('AgentCategory', 'AgentOperator', 'AgentAlert', 'AgentProxy', 'AgentSchedule', 'AgentJob', 'Credentials', 'CustomErrors', 'DatabaseMail', 'DatabaseOwner', 'LinkedServers', 'Logins', 'LoginPermissions', 'SpConfigure', 'SystemTriggers')]
[string[]]$Exclude,
[string[]]$Login,
[string[]]$ExcludeLogin,
[string[]]$Job,
[string[]]$ExcludeJob,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.AvailabilityGroup[]]$InputObject,
[switch]$Force,
[switch]$EnableException
)
begin {
$allcombos = @()
}
process {
if (-not $AvailabilityGroup -and -not $Secondary -and -not $InputObject) {
Stop-Function -Message "You must specify a secondary or an availability group."
return
}
if ($InputObject) {
$server = $InputObject.Parent
} else {
try {
$server = Connect-SqlInstance -SqlInstance $Primary -SqlCredential $PrimarySqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Primary
return
}
}
if ($AvailabilityGroup) {
$InputObject += Get-DbaAvailabilityGroup -SqlInstance $server -AvailabilityGroup $AvailabilityGroup
}
if ($InputObject) {
$Secondary += (($InputObject.AvailabilityReplicas | Where-Object Name -ne $server.DomainInstanceName).Name | Select-Object -Unique)
}
if ($Secondary) {
$Secondary = $Secondary | Sort-Object
$secondaries = @()
foreach ($computer in $Secondary) {
try {
$secondaries += Connect-SqlInstance -SqlInstance $computer -SqlCredential $SecondarySqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Primary
return
}
}
}
$thiscombo = [pscustomobject]@{
PrimaryServer = $server
SecondaryServer = $secondaries
}
# In the event that someone pipes in an availability group, this will keep the syncer from running a bunch of times
$dupe = $false
foreach ($ag in $allcombos) {
if ($ag.PrimaryServer.Name -eq $thiscombo.PrimaryServer.Name -and
$ag.SecondaryServer.Name.ToString() -eq $thiscombo.SecondaryServer.Name.ToString()) {
$dupe = $true
}
}
if ($dupe -eq $false) {
$allcombos += $thiscombo
}
}
end {
if (Test-FunctionInterrupt) { return }
# now that all combinations have been figured out, begin sync without duplicating work
foreach ($ag in $allcombos) {
$server = $ag.PrimaryServer
$secondaries = $ag.SecondaryServer
$stepCounter = 0
$activity = "Syncing availability group $AvailabilityGroup"
if (-not $secondaries) {
Stop-Function -Message "No secondaries found."
return
}
$primaryserver = $server.Name
$secondaryservers = $secondaries.Name -join ", "
if ($Exclude -notcontains "SpConfigure") {
if ($PSCmdlet.ShouldProcess("Syncing SQL Server Configuration from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing SQL Server Configuration"
Copy-DbaSpConfigure -Source $server -Destination $secondaries
}
}
if ($Exclude -notcontains "Logins") {
if ($PSCmdlet.ShouldProcess("Syncing logins from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing logins"
Copy-DbaLogin -Source $server -Destination $secondaries -ExcludeLogin $ExcludeLogin -Force:$Force
}
}
if ($Exclude -notcontains "DatabaseOwner") {
if ($PSCmdlet.ShouldProcess("Updating database owners to match newly migrated logins from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Updating database owners to match newly migrated logins"
foreach ($sec in $secondaries) {
$null = Update-SqlDbOwner -Source $server -Destination $sec
}
}
}
if ($Exclude -notcontains "CustomErrors") {
if ($PSCmdlet.ShouldProcess("Syncing custom errors (user defined messages) from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing custom errors (user defined messages)"
Copy-DbaCustomError -Source $server -Destination $secondaries -Force:$Force
}
}
if ($Exclude -notcontains "Credentials") {
if ($PSCmdlet.ShouldProcess("Syncing SQL credentials from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing SQL credentials"
Copy-DbaCredential -Source $server -Destination $secondaries -Force:$Force
}
}
if ($Exclude -notcontains "DatabaseMail") {
if ($PSCmdlet.ShouldProcess("Syncing database mail from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing database mail"
Copy-DbaDbMail -Source $server -Destination $secondaries -Force:$Force
}
}
if ($Exclude -notcontains "LinkedServers") {
if ($PSCmdlet.ShouldProcess("Syncing linked servers from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing linked servers"
Copy-DbaLinkedServer -Source $server -Destination $secondaries -Force:$Force
}
}
if ($Exclude -notcontains "SystemTriggers") {
if ($PSCmdlet.ShouldProcess("Syncing System Triggers from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing System Triggers"
Copy-DbaServerTrigger -Source $server -Destination $secondaries -Force:$Force
}
}
if ($Exclude -notcontains "AgentCategory") {
if ($PSCmdlet.ShouldProcess("Syncing Agent Categories from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing Agent Categories"
Copy-DbaAgentJobCategory -Source $server -Destination $secondaries -Force:$force
$secondaries.JobServer.JobCategories.Refresh()
$secondaries.JobServer.OperatorCategories.Refresh()
$secondaries.JobServer.AlertCategories.Refresh()
}
}
if ($Exclude -notcontains "AgentOperator") {
if ($PSCmdlet.ShouldProcess("Syncing Agent Operators from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing Agent Operators"
Copy-DbaAgentOperator -Source $server -Destination $secondaries -Force:$force
$secondaries.JobServer.Operators.Refresh()
}
}
if ($Exclude -notcontains "AgentAlert") {
if ($PSCmdlet.ShouldProcess("Syncing Agent Alerts from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing Agent Alerts"
Copy-DbaAgentAlert -Source $server -Destination $secondaries -Force:$force -IncludeDefaults
$secondaries.JobServer.Alerts.Refresh()
}
}
if ($Exclude -notcontains "AgentProxy") {
if ($PSCmdlet.ShouldProcess("Syncing Agent Proxy Accounts from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing Agent Proxy Accounts"
Copy-DbaAgentProxy -Source $server -Destination $secondaries -Force:$force
$secondaries.JobServer.ProxyAccounts.Refresh()
}
}
if ($Exclude -notcontains "AgentSchedule") {
if ($PSCmdlet.ShouldProcess("Syncing Agent Schedules from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing Agent Schedules"
Copy-DbaAgentSchedule -Source $server -Destination $secondaries -Force:$force
$secondaries.JobServer.SharedSchedules.Refresh()
$secondaries.JobServer.Refresh()
$secondaries.Refresh()
}
}
if ($Exclude -notcontains "AgentJob") {
if ($PSCmdlet.ShouldProcess("Syncing Agent Jobs from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing Agent Jobs"
Copy-DbaAgentJob -Source $server -Destination $secondaries -Force:$force -Job $Job -ExcludeJob $ExcludeJob
}
}
if ($Exclude -notcontains "LoginPermissions") {
if ($PSCmdlet.ShouldProcess("Syncing login permissions from $primaryserver to $secondaryservers")) {
Write-ProgressHelper -Activity $activity -StepNumber ($stepCounter++) -Message "Syncing login permissions"
Sync-DbaLoginPermission -Source $server -Destination $secondaries -Login $Login -ExcludeLogin $ExcludeLogin
}
}
}
}
}
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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Sync-DbaLoginPermission
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Sync-DbaLoginPermission -Source sqlserver2014a -Destination sqlcluster -Login realcajun, netnerds
Copies permissions ONLY for logins netnerds and realcajun.
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[DbaInstanceParameter[]]$Destination,
[PSCredential]$DestinationSqlCredential,
[string[]]$Login,
[string[]]$ExcludeLogin,
[switch]$EnableException
)
begin {
function Sync-Only {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[object]$sourceServer,
[object]$destServer,
[array]$Logins,
[array]$Exclude
)
try {
$sa = Get-SqlSaLogin -SqlInstance $destServer -ErrorAction Stop
} catch {
$sa = "sa"
}
foreach ($sourceLogin in $sourceServer.Logins) {
$username = $sourceLogin.Name
$currentLogin = $sourceServer.ConnectionContext.TrueLogin
if (!$Login -and $currentLogin -eq $username) {
Write-Message -Level Verbose -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-SqlPermission -SourceServer $sourceServer -SourceLogin $sourceLogin -DestServer $destServer -DestLogin $destLogin
}
}
try {
$sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $sqlcredential
if ((Test-Bound -ParameterName Login)) {
$Login = ($sourceServer.Logins | Where-Object Name -NotIn $ExcludeLogin).Name
}
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source -Continue
return
}
}
process {
if (Test-FunctionInterrupt) { return }
foreach ($dest in $Destination) {
try {
$destServer = Connect-SqlInstance -SqlInstance $dest -SqlCredential $DestinationSqlCredential -MinimumVersion 8
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $dest -Continue
}
if ($PSCmdlet.ShouldProcess("Syncing Logins $Login")) {
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-DbaSqlLoginPermission
}
}
function Test-DbaBackupInformation {
<#
.SYNOPSIS
Tests a dbatools backup history object is correct for restoring
.DESCRIPTION
Input is normally from a backup history object generated 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 OutputScriptOnly
Switch to disable path creation. Will write a warning that a path does 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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaBackupInformation
.EXAMPLE
PS C:\> $BackupHistory | Test-DbaBackupInformation -SqlInstance MyInstance
PS C:\> $PassedDbs = $BackupHistory | Where-Object {$_.IsVerified -eq $True}
PS C:\> $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, ConfirmImpact = "Low")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[object[]]$BackupHistory,
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter]$SqlInstance,
[PSCredential]$SqlCredential,
[switch]$WithReplace,
[switch]$Continue,
[switch]$VerifyOnly,
[switch]$OutputScriptOnly,
[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-DbaPath -SqlInstance $RestoreInstance -Path $DBHistoryPhysicalPaths
$DBHistoryPhysicalPathsExists = ($DBHistoryPhysicalPathsTest | Where-Object FileExists -eq $True).FilePath
$pathSep = Get-DbaPathSep -Server $RestoreInstance
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 -and $RestoreInstance.VersionMajor -gt 8) {
Write-Message -Message "File $path already exists on $($SqlInstance.ComputerName), not owned by any database in $SqlInstance, will not overwrite." -Level Warning
$VerificationErrors++
}
} else {
<#
dang, Split-Path converts path separators always using the "current system" settings
PS C:> Split-Path -Path '/var/opt/mssql/data/foo.bak' -Parent
\var\opt\mssql\data
I'm not aware of a safe way to change this so...we do a little hack.
#>
$pathSep = Get-DbaPathSep -Server $RestoreInstance
$ParentPath = Split-Path $path -Parent
$ParentPath = $ParentPath.Replace('\', $pathSep)
if (!(Test-DbaPath -SqlInstance $RestoreInstance -Path $ParentPath) ) {
if (-not $OutputScriptOnly) {
$ConfirmMessage = "`n Creating Folder $ParentPath on $SqlInstance `n"
if ($Pscmdlet.ShouldProcess("$Path on $SqlInstance `n `n", $ConfirmMessage)) {
if (New-DbaDirectory -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++
}
}
} else {
Write-Message -Message "Parth $ParentPath on $SqlInstance does not exist" -Level Verbose
}
}
}
}
}
#Test all backups readable
$allpaths = $DbHistory | Select-Object -ExpandProperty FullName
$allpaths_validity = Test-DbaPath -SqlInstance $RestoreInstance -Path $allpaths
foreach ($path in $allpaths_validity) {
if ($path.FileExists -eq $false -and ($path.FilePath -notlike 'http*')) {
Write-Message -Message "Backup File $($path.FilePath) cannot be read by $($RestoreInstance.Name). Does the service account ($($RestoreInstance.ServiceAccount)) have permission?" -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
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Test-DbaBuild {
<#
.SYNOPSIS
Returns SQL Server Build "compliance" level on a build.
.DESCRIPTION
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 the Examples for more information.
.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-DbaBuild in your own scripts.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: SqlBuild, Version
Author: Simone Bizzotto (@niphold) | Friedrich Weinmann (@FredWeinmann)
dbatools PowerShell module (https://dbatools.io, [email protected])
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaBuild
.EXAMPLE
PS C:\> Test-DbaBuild -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
PS C:\> Test-DbaBuild -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.
Output column CUTarget is not relevant (empty). SPTarget and BuildTarget are filled in the result.
.EXAMPLE
PS C:\> Test-DbaBuild -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
PS C:\> Test-DbaBuild -Build "12.0.5540" -MaxBehind "0CU"
Returns information about a build identified by "12.0.5540", making sure it is the latest CU release.
Output columns CUTarget, SPTarget and BuildTarget are relevant. If the latest build is a service pack (not a CU), CUTarget will be empty.
.EXAMPLE
PS C:\> Test-DbaBuild -Build "12.0.5540" -Latest
Returns information about a build identified by "12.0.5540", making sure it is the latest build available.
Output columns CUTarget and SPTarget are not relevant (empty), only the BuildTarget is.
.EXAMPLE
PS C:\> Test-DbaBuild -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
PS C:\> Test-DbaBuild -Build "12.0.4502","10.50.4260" -MinimumBuild "12.0.4511"
Returns information builds identified by these versions strings.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlserver2014a | Test-DbaBuild -MinimumBuild "12.0.4511"
Integrate with other cmdlets to have builds checked for all your registered servers on sqlserver2014a.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
[CmdletBinding()]
param (
[version[]]$Build,
[version]$MinimumBuild,
[string]$MaxBehind,
[switch] $Latest,
[parameter(ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[Alias("Credential")]
[PSCredential]$SqlCredential,
[switch]$Update,
[switch]$Quiet,
[switch][Alias('Silent')]$EnableException
)
begin {
#region Helper functions
function Get-DbaBuildReferenceIndex {
[CmdletBinding()]
$DbatoolsData = Get-DbatoolsConfigValue -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
}
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'
}
if ($Build) {
$BuildVersions = Get-DbaBuildReference -Build $Build -Update:$Update -EnableException:$EnableException
} elseif ($SqlInstance) {
$BuildVersions = Get-DbaBuildReference -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Update:$Update -EnableException:$EnableException
}
# Moving it down here to only trigger after -Update was properly called
if (!$IdxRef) {
try {
$IdxRef = Get-DbaBuildReferenceIndex
} catch {
Stop-Function -Message "Error loading SQL build reference" -ErrorRecord $_
return
}
}
foreach ($BuildVersion in $BuildVersions) {
$inputbuild = $BuildVersion.Build
$compliant = $false
$targetSPName = $null
$targetCUName = $null
if ($BuildVersion.MatchType -eq 'Approximate') {
Write-Message -Level Warning -Message "$($BuildVersion.Build) is not recognized as a correct version"
}
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')) {
[string[]]$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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaSqlBuild
}
}
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: Friedrich Weinmann (@FredWeinmann)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
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
PS C:\> 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
PS C:\> 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()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingWMICmdlet", "", Justification = "Using Get-WmiObject is used as a fallback for testing connections")]
param (
[Parameter(ValueFromPipeline)]
[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-DbatoolsConfigValue -Name "ComputerManagement.Cache.Disable.All" -Fallback $false
#Variable marked as unused by PSScriptAnalyzer
#$disable_badcredentialcache = Get-DbatoolsConfigValue -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 {
#Variable $os marked as unused by PSScriptAnalyzer replace with $null to catch output
$null = $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 {
#Variable $os marked as unused by PSScriptAnalyzer replace with $null to catch output
$null = $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 {
#Variable $os marked as unused by PSScriptAnalyzer replace with $null to catch output
$null = 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 }
#Variable $os marked as unused by PSScriptAnalyzer replace with $null to catch output
$null = 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
#Variable marked as unused by PSScriptAnalyzer
#$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.
.NOTES
Tags: CIM, Test, Connection
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaConnection
.EXAMPLE
PS C:\> 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
```
Test connection to SQL2016 and outputs information collected
#>
[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") {
#Variable marked as unused by PSScriptAnalyzer, need to be in PSCustomObject?
#$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()
} catch {
# here to avoid an empty catch
$null = 1
}
}
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 target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaConnectionAuthScheme
.EXAMPLE
PS C:\> Test-DbaConnectionAuthScheme -SqlInstance sqlserver2014a, sql2016
Returns ConnectName, ServerName, Transport and AuthScheme for sqlserver2014a and sql2016.
.EXAMPLE
PS C:\> Test-DbaConnectionAuthScheme -SqlInstance sqlserver2014a -Kerberos
Returns $true or $false depending on if the connection is Kerberos or not.
.EXAMPLE
PS C:\> Test-DbaConnectionAuthScheme -SqlInstance sqlserver2014a | Select-Object *
Returns the results of "SELECT * from sys.dm_exec_connections WHERE session_id = @@SPID"
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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-DbaDbCollation {
<#
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaDbCollation
.EXAMPLE
PS C:\> Test-DbaDbCollation -SqlInstance sqlserver2014a
Returns server name, database name and true/false if the collations match for all databases on sqlserver2014a.
.EXAMPLE
PS C:\> Test-DbaDbCollation -SqlInstance sqlserver2014a -Database db1, db2
Returns information for the db1 and db2 databases on sqlserver2014a.
.EXAMPLE
PS C:\> Test-DbaDbCollation -SqlInstance sqlserver2014a, sql2016 -Exclude db1
Returns information for database and server collations for all databases except db1 on sqlserver2014a and sql2016.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sql2016 | Test-DbaDbCollation
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
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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaDatabaseCollation
}
}
function Test-DbaDbCompatibility {
<#
.SYNOPSIS
Compares Database Compatibility level to Server Compatibility
.DESCRIPTION
Compares Database Compatibility level to Server Compatibility
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaDbCompatibility
.EXAMPLE
PS C:\> Test-DbaDbCompatibility -SqlInstance sqlserver2014a
Returns server name, database name and true/false if the compatibility level match for all databases on sqlserver2014a.
.EXAMPLE
PS C:\> Test-DbaDbCompatibility -SqlInstance sqlserver2014a -Database db1, db2
Returns detailed information for database and server compatibility level for the db1 and db2 databases on sqlserver2014a.
.EXAMPLE
PS C:\> Test-DbaDbCompatibility -SqlInstance sqlserver2014a, sql2016 -Exclude db1
Returns detailed information for database and server compatibility level for all databases except db1 on sqlserver2014a and sql2016.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sql2014 | Test-DbaDbCompatibility
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, ValueFromPipeline)]
[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) {
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
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaDatabaseCompatibility
}
}
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 and the estimated, best option to date for either NONE, Page, or Row Compression.
Remember Uptime is critical, the longer uptime, the more accurate the analysis is, and it would be best if you utilized Get-DbaUptime first, before running this command.
Test-DbaDbCompression 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.
This script will execute on the context of the current database.
Also be aware that this may take a while 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.
It only considers Row or Page Compression (not column compression)
It only evaluates User Tables
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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 Schema
Filter to only get specific schemas If unspecified, all schemas will be processed.
.PARAMETER Table
Filter to only get specific tables If unspecified, all User tables will be processed.
.PARAMETER ResultSize
Allows you to limit the number of results returned, as some systems can have very large number of tables. Default value is no restriction.
.PARAMETER Rank
Allows you to specify the field used for ranking when determining the ResultSize
Can be either TotalPages, UsedPages or TotalRows with default of TotalPages. Only applies when ResultSize is used.
.PARAMETER FilterBy
Allows you to specify level of filtering when determining the ResultSize
Can be at either Table, Index or Partition level with default of Partition. Only applies when ResultSize is used.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.INPUTS
Accepts a DbaInstanceParameter. Any collection of SQL Server Instance names or SMO objects can be piped to command.
.OUTPUTS
Returns a PsCustomObject with following fields: ComputerName, InstanceName, SqlInstance, Database, IndexName, Partition, IndexID, PercentScan, PercentUpdate, RowEstimatePercentOriginal, PageEstimatePercentOriginal, CompressionTypeRecommendation, SizeCurrent, SizeRequested, PercentCompression
.NOTES
Tags: Compression, Table, Database
Author: Jason Squires (@js_0505), [email protected]
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaDbCompression
.EXAMPLE
PS C:\> Test-DbaDbCompression -SqlInstance localhost
Returns results of all potential compression options for all databases for the default instance on the local host. Returns a recommendation of either Page, Row or NO_GAIN
.EXAMPLE
PS C:\> Test-DbaDbCompression -SqlInstance ServerA
Returns results of all potential compression options for all databases on the instance ServerA
.EXAMPLE
PS C:\> Test-DbaDbCompression -SqlInstance ServerA -Database DBName | Out-GridView
Returns results of all potential compression options for a single database DBName with the recommendation of either Page or Row or NO_GAIN in a nicely formatted GridView
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Test-DbaDbCompression -SqlInstance ServerA -ExcludeDatabase MyDatabase -SqlCredential $cred
Returns results of all potential compression options for all databases except MyDatabase on instance ServerA using SQL credentials to authentication to ServerA.
Returns the recommendation of either Page, Row or NO_GAIN
.EXAMPLE
PS C:\> Test-DbaDbCompression -SqlInstance ServerA -Schema Test -Table MyTable
Returns results of all potential compression options for the Table Test.MyTable in instance ServerA on ServerA and ServerB.
Returns the recommendation of either Page, Row or NO_GAIN.
Returns a result for each partition of any Heap, Clustered or NonClustered index.
.EXAMPLE
PS C:\> Test-DbaDbCompression -SqlInstance ServerA, ServerB -ResultSize 10
Returns results of all potential compression options for all databases on ServerA and ServerB.
Returns the recommendation of either Page, Row or NO_GAIN.
Returns results for the top 10 partitions by TotalPages used per database.
.EXAMPLE
PS C:\> ServerA | Test-DbaDbCompression -Schema Test -ResultSize 10 -Rank UsedPages -FilterBy Table
Returns results of all potential compression options for all databases on ServerA containing a schema Test
Returns results for the top 10 Tables by Used Pages per database.
Results are split by Table, Index and Partition so more than 10 results may be returned.
.EXAMPLE
PS C:\> $servers = 'Server1','Server2'
PS C:\> $servers | Test-DbaDbCompression -Database DBName | Out-GridView
Returns results of all potential compression options for a single database DBName on Server1 or Server2
Returns the recommendation of either Page, Row or NO_GAIN in a nicely formatted GridView
.EXAMPLE
PS C:\> $cred = Get-Credential sqladmin
PS C:\> Test-DbaDbCompression -SqlInstance ServerA -Database MyDB -SqlCredential $cred -Schema Test -Table Test1, Test2
Returns results of all potential compression options for objects in Database MyDb on instance ServerA using SQL credentials to authentication to ServerA.
Returns the recommendation of either Page, Row or NO_GAIN for tables with SchemA Test and name in Test1 or Test2
.EXAMPLE
PS C:\> $servers = 'Server1','Server2'
PS C:\> foreach ($svr in $servers) {
>> Test-DbaDbCompression -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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[object[]]$Database,
[object[]]$ExcludeDatabase,
[string[]]$Schema,
[string[]]$Table,
[int]$ResultSize,
[ValidateSet('TotalPages', 'UsedPages', 'TotalRows')]
[string]$Rank = 'TotalPages',
[ValidateSet('Partition', 'Index', 'Table')]
[string]$FilterBy = 'Partition',
[Alias('Silent')]
[switch]$EnableException
)
begin {
Write-Message -Level System -Message "Bound parameters: $($PSBoundParameters.Keys -join ", ")"
if ($Schema) {
$sqlSchemaWhere = "AND s.name IN ('$($Schema -join "','")')"
}
if ($Table) {
$sqlTableWhere = "AND t.name IN ('$($Table -join "','")')"
}
if ($ResultSize) {
$sqlOrderBy = switch ($Rank) {
UsedPages { 'UsedSpaceKB' }
TotalRows { 'RowCounts' }
default { 'TotalSpaceKB' }
}
if ($FilterBy -eq 'Table') {
$sqlJoinFiltered = 'AND t.TableName = tdc.TableName COLLATE DATABASE_DEFAULT'
$indexSQL = '0 as [IndexID]'
$partitionSQL = '0 AS [Partition]'
$groupBySQL = 's.Name, t.Name'
} elseif ($FilterBy -eq 'Index') {
$sqlJoinFiltered = 'AND t.TableName = tdc.TableName COLLATE DATABASE_DEFAULT AND t.IndexID = tdc.IndexID'
$indexSQL = 'i.index_id as [IndexID]'
$partitionSQL = '0 AS [Partition]'
$groupBySQL = 's.Name, t.Name, i.index_id'
} else {
$sqlJoinFiltered = 'AND t.TableName = tdc.TableName COLLATE DATABASE_DEFAULT AND t.IndexID = tdc.IndexID AND t.[Partition] = tdc.[Partition]'
$indexSQL = 'i.index_id as [IndexID]'
$partitionSQL = 'p.partition_number AS [Partition]'
$groupBySQL = 's.Name, t.Name, i.index_id, p.partition_number'
}
$sqlRestrict = "-- remove tables not in Top N
With TopN(SchemaName, TableName, IndexID, [Partition], RowCounts, TotalSpaceKB, UsedSpaceKB) as
(
SELECT TOP $ResultSize
s.Name AS SchemaName,
t.NAME as TableName,
$indexSQL,
$partitionSQL,
SUM(p.rows) AS RowCounts,
SUM(a.total_pages) * 8 AS TotalSpaceKB,
SUM(a.used_pages) * 8 AS UsedSpaceKB
FROM
sys.tables t
INNER JOIN
sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN
sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN
sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN
sys.schemas s ON t.schema_id = s.schema_id
WHERE objectproperty(t.object_id, 'IsUserTable') = 1
AND p.data_compression_desc = 'NONE'
AND p.rows > 0
$sqlSchemaWhere
$sqlTableWhere
GROUP BY
$groupBySQL
ORDER BY
$sqlOrderBy Desc
)
DELETE tdc
FROM ##testdbacompression tdc
LEFT JOIN TopN t
ON t.SchemaName = tdc.[Schema] COLLATE DATABASE_DEFAULT
$sqlJoinFiltered
WHERE t.IndexID IS NULL;"
}
}
process {
foreach ($instance in $SqlInstance) {
try {
$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
$sqlVersion = $(Get-DbaBuildReference -SqlInstance $server).Build.Major
$sqlVersionRestrictions = @()
if ($sqlVersion -ge 12) {
$sqlVersionRestrictions += "
BEGIN
-- remove memory optimized tables
DELETE tdc
FROM ##testdbacompression tdc
INNER JOIN sys.tables t
ON SCHEMA_NAME(t.schema_id) = tdc.[Schema] COLLATE DATABASE_DEFAULT
AND t.name = tdc.TableName COLLATE DATABASE_DEFAULT
WHERE t.is_memory_optimized = 1
END"
}
if ($sqlVersion -ge 13) {
$sqlVersionRestrictions += "
BEGIN
-- remove tables with encrypted columns
DELETE tdc
FROM ##testdbacompression tdc
INNER JOIN sys.tables t
ON SCHEMA_NAME(t.schema_id) = tdc.[Schema] COLLATE DATABASE_DEFAULT
AND t.name = tdc.TableName COLLATE DATABASE_DEFAULT
INNER JOIN sys.columns c
ON t.object_id = c.object_id
WHERE encryption_type IS NOT NULL
END"
}
if ($sqlVersion -ge 14) {
$sqlVersionRestrictions += "
BEGIN
-- remove graph (node/edge) tables
DELETE tdc
FROM ##testdbacompression tdc
INNER JOIN sys.tables t
ON tdc.[Schema] = SCHEMA_NAME(t.schema_id) COLLATE DATABASE_DEFAULT
AND tdc.TableName = t.name COLLATE DATABASE_DEFAULT
WHERE (is_node = 1 OR is_edge = 1)
END"
}
$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
,[ObjectId] INT
,[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]
,[ObjectId]
,[IndexName]
,[Partition]
,[IndexID]
,[IndexType]
,[PercentScan]
,[PercentUpdate]
)
SELECT s.NAME AS [Schema]
,t.NAME AS [TableName]
,t.OBJECT_ID AS [OBJECTID]
,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
$sqlSchemaWhere
$sqlTableWhere
ORDER BY [TableName] ASC;
$sqlRestrict
$sqlVersionRestrictions
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] =
case when (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 THEN 0
ELSE 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)
END
,[PercentUpdate] =
case when (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 THEN 0
ELSE 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)
END
FROM sys.dm_db_index_operational_stats(db_id(), NULL, NULL, NULL) i
INNER JOIN ##testdbacompression tmp
ON tmp.ObjectId = i.object_id
AND tmp.IndexID = i.index_id;
WITH tmp_cte (
objname
,schname
,indid
,partnr
,pct_of_orig_row
,pct_of_orig_page
,SizeCurrent
,SizeRequested
)
AS (
SELECT tr.objname
,tr.schname
,tr.indid
,tr.partnr
,(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
AND tcte.partnr = tcomp.Partition;
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;
"
Write-Message -Level Debug -Message "SQL Statement: $sql"
[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 "Compression 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
}
#Filter Database list
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
}
}
}
}
}
function Test-DbaDbLogShipStatus {
<#
.SYNOPSIS
Test-DbaDbLogShipStatus 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
The target SQL Server instance or instances. 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaDbLogShipStatus
.EXAMPLE
PS C:\> Test-DbaDbLogShipStatus -SqlInstance sql1
Retrieves the log ship information from sql1 and displays all the information present including the status.
.EXAMPLE
PS C:\> Test-DbaDbLogShipStatus -SqlInstance sql1 -Database AdventureWorks2014
Retrieves the log ship information for just the database AdventureWorks.
.EXAMPLE
PS C:\> Test-DbaDbLogShipStatus -SqlInstance sql1 -Primary
Retrieves the log ship information and only returns the information for the databases on the primary instance.
.EXAMPLE
PS C:\> Test-DbaDbLogShipStatus -SqlInstance sql1 -Secondary
Retrieves the log ship information and only returns the information for the databases on the secondary instance.
.EXAMPLE
PS C:\> Test-DbaDbLogShipStatus -SqlInstance sql1 -Simple
Retrieves the log ship information and only returns the columns SQL Instance, Database, Instance Type and Status
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[string[]]$ExcludeDatabase,
[switch]$Simple,
[switch]$Primary,
[switch]$Secondary,
[Alias('Silent')]
[switch]$EnableException
)
begin {
# 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) ,
BackupThreshold INT ,
IsBackupAlertEnabled BIT ,
TimeSinceLastCopy INT ,
LastCopiedFile VARCHAR(255) ,
TimeSinceLastRestore INT ,
LastRestoredFile VARCHAR(255) ,
LastRestoredLatency INT ,
RestoreThreshold INT ,
IsRestoreAlertEnabled BIT
);
INSERT INTO #logshippingstatus
( Status ,
IsPrimary ,
Server ,
DatabaseName ,
TimeSinceLastBackup ,
LastBackupFile ,
BackupThreshold ,
IsBackupAlertEnabled ,
TimeSinceLastCopy ,
LastCopiedFile ,
TimeSinceLastRestore ,
LastRestoredFile ,
LastRestoredLatency ,
RestoreThreshold ,
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
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.BackupThreshold) {
$statusDetails += "The backup has not been executed in the last $($result.BackupThreshold) 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.RestoreThreshold) {
$statusDetails += "The restore has not been executed in the last $($result.RestoreThreshold) 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
$object = [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
BackupThreshold = $result.BackupThreshold
IsBackupAlertEnabled = $result.IsBackupAlertEnabled
TimeSinceLastCopy = $lastCopy
LastCopiedFile = $result.LastCopiedFile
TimeSinceLastRestore = $lastRestore
LastRestoredFile = $result.LastRestoredFile
LastRestoredLatency = $result.LastRestoredLatency
RestoreThreshold = $result.RestoreThreshold
IsRestoreAlertEnabled = $result.IsRestoreAlertEnabled
Status = $statusDetails -join ","
}
if ($Simple) {
$object | Select-DefaultView -Property SqlInstance, Database, InstanceType, Status
} else {
$object
}
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Test-DbaLogShippingStatus
}
}
function Test-DbaDbOwner {
<#
.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
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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaDbOwner
.EXAMPLE
PS C:\> Test-DbaDbOwner -SqlInstance localhost
Returns all databases where the owner does not match 'sa'.
.EXAMPLE
PS C:\> Test-DbaDbOwner -SqlInstance localhost -TargetLogin 'DOMAIN\account'
Returns all databases where the owner does not match 'DOMAIN\account'.
#>
[CmdletBinding()]
param (
[parameter(Mandatory)]
[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 {
$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
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaDatabaseOwner
}
}
function Test-DbaDbRecoveryModel {
<#
.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 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 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) 2018 by dbatools, licensed under MIT
License: GNU GPL v3 https://opensource.org/licenses/GPL-3.0
.LINK
https://dbatools.io/Test-DbaDbRecoveryModel
.EXAMPLE
PS C:\> Test-DbaDbRecoveryModel -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
PS C:\> Test-DbaDbRecoveryModel -SqlInstance . | Where-Object {$_.ActualRecoveryModel -ne "FULL"}
Only shows the databases that are functionally in 'simple' mode.
.EXAMPLE
PS C:\> Test-DbaDbRecoveryModel -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
PS C:\> Test-DbaDbRecoveryModel -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, ValueFromPipeline)]
[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 {
$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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -Alias Test-DbaRecoveryModel
}
}
#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
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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaDbVirtualLogFile
.EXAMPLE
PS C:\> Test-DbaDbVirtualLogFile -SqlInstance sqlcluster
Returns all user database virtual log file counts for the sqlcluster instance.
.EXAMPLE
PS C:\> Test-DbaDbVirtualLogFile -SqlInstance sqlserver | Where-Object {$_.Count -ge 50}
Returns user databases that have 50 or more VLFs.
.EXAMPLE
PS C:\> @('sqlserver','sqlcluster') | Test-DbaDbVirtualLogFile
Returns all VLF information for the sqlserver and sqlcluster SQL Server instances. Processes data via the pipeline.
.EXAMPLE
PS C:\> 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)]
[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 {
$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-DbaDbFile -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-DbaDeprecatedFeature {
<#
.SYNOPSIS
Displays information relating to deprecated features for SQL Server 2005 and above.
.DESCRIPTION
Displays information relating to deprecated features for SQL Server 2005 and above.
.PARAMETER SqlInstance
The target SQL Server instance
.PARAMETER SqlCredential
Login to the target instance using alternate Windows or SQL Login Authentication. 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 InputObject
A collection of databases (such as returned by Get-DbaDatabase), to be tested.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch.
.NOTES
Tags: Deprecated
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaDeprecatedFeature
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2008 -Database testdb, db2 | Test-DbaDeprecatedFeature
Check deprecated features on server sql2008 for only the testdb and db2 databases
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2008 -Database testdb, db2 | Test-DbaDeprecatedFeature | Select *
See the object definition in the output as well
.EXAMPLE
PS C:\> Test-DbaDeprecatedFeature -SqlInstance sql2008, sqlserver2012
Check deprecated features for all databases on the servers sql2008 and sqlserver2012.
.EXAMPLE
PS C:\> Test-DbaDeprecatedFeature -SqlInstance sql2008 -Database TestDB
Check deprecated features on server sql2008 for only the TestDB database
#>
[CmdletBinding()]
param (
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Database,
[string[]]$ExcludeDatabase,
[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, object_id as ID, Name, type_desc as Type, Object_Definition (object_id) as Definition FROM sys.all_objects
Where Type = 'P' AND is_ms_shipped = 0"
}
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase
}
foreach ($db in $InputObject) {
Write-Message -Level Verbose -Message "Processing $db on $($db.Parent.Name)"
if ($db.IsAccessible -eq $false) {
Stop-Function -Message "The database $db is not accessible. Skipping database." -Continue
}
$deps = $db.Query("select instance_name as dep from sys.dm_os_performance_counters where object_name like '%Deprecated%'")
try {
$results = $db.Query($sql)
foreach ($dep in $deps) {
$escaped = [Regex]::Escape("$($dep.dep)".Trim())
$matchedep = $results | Where-Object Definition -match $escaped
if ($matchedep) {
$matchedep | Add-Member -NotePropertyName DeprecatedFeature -NotePropertyValue $dep.dep.ToString().Trim() -PassThru -Force |
Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, DeprecatedFeature, ID, Name, Type
}
}
} catch {
Stop-Function -Message "Failure" -ErrorRecord $_ -Continue
}
}
}
}
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 target computer or computers.
.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.
.NOTES
Tags: Storage
Author: Constantine Kokkinos (@mobileck), https://constantinekokkinos.com
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
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
- 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!
.LINK
https://dbatools.io/Test-DbaDiskAlignment
.EXAMPLE
PS C:\> Test-DbaDiskAlignment -ComputerName sqlserver2014a
Tests the disk alignment of a single server named sqlserver2014a
.EXAMPLE
PS C:\> Test-DbaDiskAlignment -ComputerName sqlserver2014a, sqlserver2014b, sqlserver2014c
Tests the disk alignment of multiple servers
#>
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlInstance")]
[DbaInstanceParameter[]]$ComputerName,
[System.Management.Automation.PSCredential]$Credential,
[System.Management.Automation.PSCredential]$SqlCredential,
[switch]$NoSqlCheck,
[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) {
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
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 can't 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
}
[PSCustomObject]@{
ComputerName = $ogcomputer
Name = "$($partition.Name)"
PartitonSize = [dbasize]($($partition.Size / 1MB) * 1024 * 1024)
PartitionType = $partition.Type
TestingStripeSize = [dbasize]($size * 1024)
OffsetModuluCalculation = [dbasize]($OffsetModuloKB * 1024)
StartingOffset = [dbasize]($offset * 1024)
IsOffsetBestPractice = $IsOffsetBestPractice
IsBestPractice = $isBestPractice
NumberOfBlocks = $partition.NumberOfBlocks
BootPartition = $partition.BootPartition
PartitionBlockSize = $partition.BlockSize
IsDynamicDisk = $IsDynamicDisk
}
}
}
}
}
process {
# uses cim commands
foreach ($computer in $ComputerName) {
$computer = $ogcomputer = $computer.ComputerName
Write-Message -Level VeryVerbose -Message "Processing: $computer."
$computer = Resolve-DbaNetworkName -ComputerName $computer -Credential $Credential
$Computer = $computer.FullComputerName
if (-not $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 (-not $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
} else {
$cimsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoption -ErrorAction Ignore -Credential $Credential
}
}
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 {
Get-DiskAlignment -CimSession $cimsession -NoSqlCheck $NoSqlCheck -ComputerName $Computer -ErrorAction Stop
} catch {
Stop-Function -Message "Failed to process $($Computer): $($_.Exception.Message)" -Continue -InnerErrorRecord $_ -Target $Computer
}
}
}
}
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.
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."
.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 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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaDiskAllocation
.EXAMPLE
PS C:\> Test-DbaDiskAllocation -ComputerName sqlserver2014a
Scans all disks on server sqlserver2014a for best practice allocation unit size.
.EXAMPLE
PS C:\> Test-DbaDiskAllocation -ComputerName sqlserver2014 | Select-Output *
Scans all disks on server sqlserver2014a for allocation unit size and returns detailed results for each.
.EXAMPLE
PS C:\> 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)]
[OutputType("System.Collections.ArrayList", "System.Boolean")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlInstance")]
[object[]]$ComputerName,
[switch]$NoSqlCheck,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[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) {
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 {
# uses cim commands
foreach ($computer in $ComputerName) {
$computer = Resolve-DbaNetworkName -ComputerName $computer -Credential $Credential
$ipaddr = $computer.IpAddress
$Computer = $computer.FullComputerName
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 $sessionoptions -ErrorAction SilentlyContinue
} else {
$cimsession = New-CimSession -ComputerName $Computer -SessionOption $sessionoptions -ErrorAction SilentlyContinue -Credential $Credential
}
}
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"
if ($PScmdlet.ShouldProcess("$computer", "Getting Disk Allocation")) {
$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
The target SQL Server instance or instances.
.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
Tags: Performance
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaDiskSpeed
.EXAMPLE
PS C:\> Test-DbaDiskSpeed -SqlInstance sql2008, sqlserver2012
Tests how disks are performing on sql2008 and sqlserver2012.
.EXAMPLE
PS C:\> Test-DbaDiskSpeed -SqlInstance sql2008 -Database tempdb
Tests how disks storing tempdb files on sql2008 are performing.
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[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) {
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")
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Test-DbaEndpoint {
<#
.SYNOPSIS
Performs a simple connectivity test for TCP and SSL enabled endpoints.
.DESCRIPTION
Performs a simple connectivity test for TCP and SSL enabled endpoints. Tests if port is accessible, not if endpoint is working.
Note that if an endpoint does not have a tcp listener port, it 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 Endpoint
Test only specific endpoint or endpoints.
.PARAMETER InputObject
Enables piping from Get-DbaEndpoint.
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaEndpoint
.EXAMPLE
PS C:\> Test-DbaEndpoint -SqlInstance localhost
Tests all endpoints on the local default SQL Server instance.
Note that if an endpoint does not have a tcp listener port, it will be skipped.
.EXAMPLE
PS C:\> Get-DbaEndpoint -SqlInstance localhost, sql2016 -Endpoint Mirror | Test-DbaEndpoint
Tests all endpoints named Mirroring on sql2016 and localhost.
Note that if an endpoint does not have a tcp listener port, it will be skipped.
.EXAMPLE
PS C:\> Test-DbaEndpoint -SqlInstance localhost, sql2016 -Endpoint Mirror
Tests all endpoints named Mirroring on sql2016 and localhost.
Note that if an endpoint does not have a tcp listener port, it will be skipped.
.EXAMPLE
PS C:\> Test-DbaEndpoint -SqlInstance localhost -Verbose
Tests all endpoints on the local default SQL Server instance.
See all endpoints that were skipped due to not having a tcp listener port.
#>
[CmdletBinding()]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string[]]$Endpoint,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Endpoint[]]$InputObject,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
$InputObject += Get-DbaEndpoint -SqlInstance $instance -SqlCredential $SqlCredential -Endpoint $Endpoint
}
foreach ($end in $InputObject) {
if (-not $end.Protocol.Tcp.ListenerPort) {
Write-Message -Level Verbose -Message "$end on $($end.Parent) does not have a tcp listener port"
} else {
Write-Message "Connecting to port $($end.Protocol.Tcp.ListenerPort) on $($end.ComputerName) for endpoint $($end.Name)"
try {
$tcp = New-Object System.Net.Sockets.TcpClient
$tcp.Connect($end.ComputerName, $end.Protocol.Tcp.ListenerPort)
$tcp.Close()
$tcp.Dispose()
$connect = "Success"
} catch {
$connect = $_
}
try {
$ssl = $end.Protocol.Tcp.SslPort
if ($ssl) {
$tcp = New-Object System.Net.Sockets.TcpClient
$tcp.Connect($end.ComputerName, $ssl)
$tcp.Close()
$tcp.Dispose()
$sslconnect = "Success"
} else {
$sslconnect = "None"
}
} catch {
$sslconnect = $_
}
[pscustomobject]@{
ComputerName = $end.ComputerName
InstanceName = $end.InstanceName
SqlInstance = $end.SqlInstance
Endpoint = $end.Name
Port = $end.Protocol.Tcp.ListenerPort
Connection = $connect
SslConnection = $sslconnect
}
}
}
}
}
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
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 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 ExcludeSystem
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: Identity, Table, Column
Author: Brandon Abshire, netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaIdentityUsage
.EXAMPLE
PS C:\> Test-DbaIdentityUsage -SqlInstance sql2008, sqlserver2012
Check identity seeds for servers sql2008 and sqlserver2012.
.EXAMPLE
PS C:\> Test-DbaIdentityUsage -SqlInstance sql2008 -Database TestDB
Check identity seeds on server sql2008 for only the TestDB database
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer", "SqlServers")]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Alias("Databases")]
[object[]]$Database,
[object[]]$ExcludeDatabase,
[parameter(Position = 1)]
[int]$Threshold = 0,
[parameter(Position = 2)]
[Alias("ExcludeSystemDb")]
[switch]$ExcludeSystem,
[Alias('Silent')]
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter ExcludeSystem
$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), NULLIF(MaxNumberRows,0))) * 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) {
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 ($ExcludeSystem) {
$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
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 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaJobOwner
.EXAMPLE
PS C:\> Test-DbaJobOwner -SqlInstance localhost
Returns all SQL Agent Jobs where the owner does not match 'sa'.
.EXAMPLE
PS C:\> Test-DbaJobOwner -SqlInstance localhost -ExcludeJob 'syspolicy_purge_history'
Returns SQL Agent Jobs except for the syspolicy_purge_history job
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[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 ($instance in $SqlInstance) {
#connect to the instance
$server = Connect-SqlInstance $instance -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 $instance. 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 SQL Server instance 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 target SQL Server instance or instances. 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 MaxSize
Max size in MB. 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 InputObject
Enables piping from Get-DbaDatabase
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaLastBackup
.EXAMPLE
PS C:\> 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
PS C:\> Test-DbaLastBackup -SqlInstance sql2016 -Database SharePoint_Config
Determines the last full backup for SharePoint_Config, attempts to restore it, then performs a DBCC CHECKDB.
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2016, sql2017 | Test-DbaLastBackup
Tests every database backup on sql2016 and sql2017
.EXAMPLE
PS C:\> Get-DbaDatabase -SqlInstance sql2016, sql2017 -Database SharePoint_Config | Test-DbaLastBackup
Tests the database backup for the SharePoint_Config database on sql2016 and sql2017
.EXAMPLE
PS C:\> Test-DbaLastBackup -SqlInstance sql2016 -Database model, master -VerifyOnly
Skips performing an action restore of the database and simply verifies the backup using VERIFYONLY option of the restore.
.EXAMPLE
PS C:\> 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
PS C:\> Test-DbaLastBackup -SqlInstance sql2016 -DataDirectory E:\bigdrive -LogDirectory L:\bigdrive -MaxSize 10240
Restores data and log files to alternative locations and only restores databases that are smaller than 10 GB.
.EXAMPLE
PS C:\> 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
PS C:\> 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)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "", Justification = "For Parameters DestinationCredential and AzureCredential")]
param (
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[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,
[Alias("MaxMB")]
[int]$MaxSize,
[switch]$IncludeCopyOnly,
[switch]$IgnoreLogBackup,
[string]$AzureCredential,
[parameter(ValueFromPipeline)]
[Microsoft.SqlServer.Management.Smo.Database[]]$InputObject,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn "1.0" -Parameter 'MaxMB'
}
process {
if ($SqlInstance) {
$InputObject += Get-DbaDatabase -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -ExcludeDatabase $ExcludeDatabase
}
foreach ($db in $InputObject) {
if ($db.Name -eq "tempdb") {
continue
}
$sourceserver = $db.Parent
$source = $db.Parent.Name
$instance = [DbaInstanceParameter]$source
$copysuccess = $true
$dbname = $db.Name
if (-not (Test-Bound -ParameterName Destination)) {
$destination = $sourceserver.Name
$DestinationCredential = $SqlCredential
}
try {
$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-DbaPath -SqlInstance $destserver -Path $CopyPath
if (-not $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 -not $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
}
}
}
if ($datadirectory) {
if (-not (Test-DbaPath -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 (-not (Test-DbaPath -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 -ParameterName AzureCredential) -and (Test-Bound -ParameterName CopyFile)) {
Stop-Function -Message "Cannot use copyfile with Azure backups, set to false." -continue
$CopyFile = $false
}
Write-Message -Level Verbose -Message "Getting recent backup history for $($db.Name) on $instance."
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 -WarningAction SilentlyContinue
$diff = Get-DbaBackupHistory -SqlInstance $sourceserver -Database $dbname -IncludeCopyOnly:$IncludeCopyOnly -LastDiff -WarningAction SilentlyContinue
if ($full.start -le $diff.start) {
$lastbackup += $diff
}
} else {
$lastbackup = Get-DbaBackupHistory -SqlInstance $sourceserver -Database $dbname -IncludeCopyOnly:$IncludeCopyOnly -Last -WarningAction SilentlyContinue
}
if (-not $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 (-not $copysuccess) {
Write-Message -Level Verbose -Message "Failed to copy backups."
$lastbackup = @{
Path = "Failed to copy backups"
}
$fileexists = $false
$success = $restoreresult = $dbccresult = "Skipped"
} elseif (-not ($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 -not $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-DbaPath -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
if ($MaxSize -and $MaxSize -lt $restorelist.BackupSize.Megabyte) {
$success = "The backup size for $dbname ($mb MB) exceeds the specified maximum size ($MaxSize 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-Message -Level Verbose -Message " 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 (-not $NoCheck -and -not $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 (-not $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 {
#Variable $removeresult marked as unused by PSScriptAnalyzer replace with $null to catch output
$null = 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
$destserver.Databases.Refresh()
if ($destserver.Databases[$dbname] -and -not $NoDrop) {
Write-Message -Level Warning -Message "$dbname was not dropped."
}
}
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
}
}
}
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
BackupDates = [String[]]($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 target SQL Server instance or instances.
.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 )
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaLinkedServerConnection
.EXAMPLE
PS C:\> Test-DbaLinkedServerConnection -SqlInstance DEV01
Test all Linked Servers for the SQL Server instance DEV01
.EXAMPLE
PS C:\> 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
PS C:\> Test-DbaLinkedServerConnection -SqlInstance sql2016, sql2014, sql2012
Test all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012
.EXAMPLE
PS C:\> $servers = "sql2016","sql2014","sql2012"
PS C:\> $servers | Test-DbaLinkedServerConnection -SqlCredential sqladmin
Test all Linked Servers for the SQL Server instances sql2016, sql2014 and sql2012 using SQL login credentials
.EXAMPLE
PS C:\> $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 {
$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
Tags: Login, Security
Author: Peter Samuelsson
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaLoginPassword
.EXAMPLE
PS C:\> Test-DbaLoginPassword -SqlInstance Dev01
Test all SQL logins that the password is null or same as username on SQL server instance Dev01
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Get-DbaLogin -SqlInstance "sql2017","sql2016" | Test-DbaLoginPassword
Test all logins on sql2017 and sql2016
.EXAMPLE
PS C:\> $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-DbaManagementObject {
<#
.SYNOPSIS
Tests to see if the SMO version specified exists on the computer.
.DESCRIPTION
The Test-DbaManagementObject 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaManagementObject
.EXAMPLE
PS C:\> Test-DbaManagementObject -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
}
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaSqlManagementObject
}
}
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 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 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)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on SQL Servers
.LINK
https://dbatools.io/Test-DbaMaxDop
.EXAMPLE
PS C:\> Test-DbaMaxDop -SqlInstance sql2008, sqlserver2012
Get Max DOP setting for servers sql2008 and sqlserver2012 and also the recommended one.
.EXAMPLE
PS C:\> 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
PS C:\> 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, ValueFromPipeline)]
[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 {
#Variable marked as unused by PSScriptAnalyzer
#$hasScopedConfig = $false
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
}
#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) {
#Variable marked as unused by PSScriptAnalyzer
#$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
The target SQL Server instance or instances.
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaMaxMemory
.EXAMPLE
PS C:\> Test-DbaMaxMemory -SqlInstance sqlcluster,sqlserver2012
Calculate the 'Max Server Memory' for SQL Server instances sqlcluster and sqlserver2012
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlcluster | Test-DbaMaxMemory
Calculate the 'Max Server Memory' settings for all servers within the SQL Server Central Management Server "sqlcluster"
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlcluster | Test-DbaMaxMemory | Where-Object { $_.MaxValue -gt $_.Total } | 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(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[PSCredential]$Credential,
[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-DbaService -ComputerName $instance -Credential $Credential -EnableException
} else {
$serverService = Get-DbaService -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.MaxValue
$totalMemory = $serverMemory.Total
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
Total = [int]$totalMemory
MaxValue = [int]$maxMemory
RecommendedValue = [int]$recommendedMax
Server = $server # This will allowing piping a non-connected object
} | Select-DefaultView -Property ComputerName, InstanceName, SqlInstance, InstanceCount, Total, MaxValue, RecommendedValue
}
}
}
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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaMigrationConstraint
.EXAMPLE
PS C:\> 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
PS C:\> Test-DbaMigrationConstraint -Source sqlserver2014a -Destination sqlcluster -SourceSqlCredential $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 sqlserver2014a and Windows Authentication is used for sqlcluster.
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[DbaInstance]$Source,
[PSCredential]$SourceSqlCredential,
[parameter(Mandatory)]
[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 {
$sourceServer = Connect-SqlInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source -Continue
}
try {
$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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaNetworkLatency
.EXAMPLE
PS C:\> Test-DbaNetworkLatency -SqlInstance sqlserver2014a, sqlcluster
Tests the round trip return of "SELECT TOP 100 * FROM INFORMATION_SCHEMA.TABLES" on sqlserver2014a and sqlcluster using Windows credentials.
.EXAMPLE
PS C:\> 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
PS C:\> 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, ValueFromPipeline)]
[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 {
$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 ad-hoc 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) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaOptimizeForAdHoc
.EXAMPLE
PS C:\> 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, ValueFromPipeline)]
[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 {
$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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Test-DbaPath {
<#
.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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaPath
.EXAMPLE
PS C:\> Test-DbaPath -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
PS C:\> $credential = Get-Credential
PS C:\> Test-DbaPath -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()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[Parameter(Mandatory)]
[object]$Path,
[Alias('Silent')]
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
try {
$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
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaSqlPath
}
}
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 InputObject
Enables piping from Get-DbaPowerPlan
.PARAMETER EnableException
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaPowerPlan
.EXAMPLE
PS C:\> Test-DbaPowerPlan -ComputerName sqlserver2014a
Checks the Power Plan settings for sqlserver2014a and indicates whether or not it complies with best practices.
.EXAMPLE
PS C:\> 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)]
[Alias("ServerInstance", "SqlServer", "SqlInstance")]
[DbaInstance[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[string]$CustomPowerPlan,
[parameter(ValueFromPipeline)]
[pscustomobject]$InputObject,
[switch]$Detailed,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
$bpPowerPlan = [PSCustomObject]@{
InstanceID = '8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c'
ElementName = $null
}
}
process {
if (Test-Bound -ParameterName ComputerName) {
$InputObject += Get-DbaPowerPlan -ComputerName $ComputerName -Credential $Credential
}
foreach ($powerPlan in $InputObject) {
$computer = $powerPlan.ComputerName
$Credential = $powerPlan.Credential
$server = Resolve-DbaNetworkName -ComputerName $computer -Credential $Credential
$computerResolved = $server.FullComputerName
if (-not $computerResolved) {
Stop-Function -Message "Couldn't resolve hostname. Skipping." -Continue
}
$splatDbaCmObject = @{
ComputerName = $computerResolved
EnableException = $true
}
if (Test-Bound "Credential") {
$splatDbaCmObject["Credential"] = $Credential
}
Write-Message -Level Verbose -Message "Getting Power Plan information from $computer."
try {
$powerPlans = Get-DbaCmObject @splatDbaCmObject -ClassName Win32_PowerPlan -Namespace "root\cimv2\power" | 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 ($null -eq $powerPlan.InstanceID) {
$powerPlan.ElementName = "Unknown"
}
if ($CustomPowerPlan) {
$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
Credential = $Credential
} | Select-DefaultView -ExcludeProperty Credential
}
}
}
function Test-DbaRepLatency {
<#
.SYNOPSIS
Displays replication latency for all transactional publications for a server or database.
.DESCRIPTION
Creates tracer tokens to determine latency between the publisher/distributor and the distributor/subscriber
for all transactional publications for a server, database, or publication.
.PARAMETER SqlInstance
The target SQL Server instance or instances.
.PARAMETER Database
The database(s) to process. If unspecified, all databases will be processed.
.PARAMETER SqlCredential
Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential)
.PARAMETER PublicationName
The publication(s) to process. If unspecified, all publications will be processed.
.PARAMETER TimeToLive
How long, in seconds, to wait for a tracer token to complete its journey from the publisher to the subscriber.
If unspecified, all tracer tokens will take as long as they need to process results.
.PARAMETER RetainToken
Retains the tracer tokens created for each publication. If unspecified, all tracer tokens created will be discarded.
.PARAMETER DisplayTokenHistory
Displays all tracer tokens in each publication. If unspecified, the current tracer token created will be only token 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: Replication
Author: Colin Douglas
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaRepLatency
.EXAMPLE
PS C:\> Test-DbaRepLatency -SqlInstance sql2008, sqlserver2012
Return replication latency for all transactional publications for servers sql2008 and sqlserver2012.
.EXAMPLE
PS C:\> Test-DbaRepLatency -SqlInstance sql2008 -Database TestDB
Return replication latency for all transactional publications on server sql2008 for only the TestDB database
.EXAMPLE
PS C:\> Test-DbaRepLatency -SqlInstance sql2008 -Database TestDB -PublicationName TestDB_Pub
Return replication latency for the TestDB_Pub publication for the TestDB database located on the server sql2008.
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]] $SqlInstance, #Publisher
[object[]]$Database,
[PSCredential]$SqlCredential,
[object[]]$PublicationName,
[int]$TimeToLive,
[switch]$RetainToken,
[switch]$DisplayTokenHistory,
[switch]$EnableException
)
process {
foreach ($instance in $SqlInstance) {
# Connect to the publisher
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
$publicationNames = Get-DbaRepPublication -SqlInstance $server -Database $Database -SqlCredential $SqlCredentials -PublicationType "Transactional"
if ($PublicationName) {
$publicationNames = $publicationNames | Where-Object PublicationName -in $PublicationName
}
foreach ($publication in $publicationNames) {
# Create an instance of TransPublication
$transPub = New-Object Microsoft.SqlServer.Replication.TransPublication
$transPub.Name = $publication.PublicationName
$transPub.DatabaseName = $publication.Database
# Set the Name and DatabaseName properties for the publication, and set the ConnectionContext property to the connection created in step 1.
$transPub.ConnectionContext = $server.ConnectionContext.SqlConnectionObject
# Call the LoadProperties method to get the properties of the object. If this method returns false, either the publication properties in Step 3 were defined incorrectly or the publication does not exist.
if (!$transPub.LoadProperties()) {
Stop-Function -Message "LoadProperties() failed. The publication does not exist." -Continue
}
# Call the PostTracerToken method. This method inserts a tracer token into the publication's Transaction log.
$transPub.PostTracerToken() | Out-Null
}
##################################################################################
### Determine Latency and validate connections for a transactional publication ###
` ##################################################################################
$repServer = New-Object Microsoft.SqlServer.Replication.ReplicationServer
# Set the Name and DatabaseName properties for the Replication Server, and set the ConnectionContext property to the connection created in step 1.
$repServer.ConnectionContext = $server.ConnectionContext.SqlConnectionObject
$distributionServer = $repServer.DistributionServer
$distributionDatabase = $repServer.DistributionDatabase
# Step 1: Connect to the distributor
try {
$distServer = Connect-SqlInstance -SqlInstance $DistributionServer -SqlCredential $SqlCredential -MinimumVersion 9
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $DistributionServer -Continue
}
foreach ($publication in $publicationNames) {
$pubMon = New-Object Microsoft.SqlServer.Replication.PublicationMonitor
$pubMon.Name = $publication.PublicationName
$pubMon.DistributionDBName = $distributionDatabase
$pubMon.PublisherName = $publication.Server
$pubMon.PublicationDBName = $publication.Database
$pubMon.ConnectionContext = $distServer.ConnectionContext.SqlConnectionObject;
# Call the LoadProperties method to get the properties of the object. If this method returns false, either the publication monitor properties in Step 3 were defined incorrectly or the publication does not exist.
if (!$pubMon.LoadProperties()) {
Stop-Function -Message "LoadProperties() failed. The publication does not exist." -Continue
}
$tokenList = $pubMon.EnumTracerTokens()
if (!$DisplayTokenHistory) {
$tokenList = $tokenList[0]
}
foreach ($token in $tokenList) {
$tracerTokenId = $token.TracerTokenId
$tokenInfo = $pubMon.EnumTracerTokenHistory($tracerTokenId)
$timer = 0
$continue = $true
while (($tokenInfo.Tables[0].Rows[0].subscriber_latency -eq [System.DBNull]::Value) -and $continue ) {
if ($TimeToLive -and ($timer -gt $TimeToLive)) {
$continue = $false
Stop-Function -Message "TimeToLive has been reached for token: $tracerTokenId" -Continue
}
Start-Sleep -Seconds 1
$timer += 1
$tokenInfo = $PubMon.EnumTracerTokenHistory($tracerTokenId)
}
foreach ($info in $tokenInfo.Tables[0].Rows) {
$totalLatency = if (($info.distributor_latency -eq [System.DBNull]::Value) -or ($info.subscriber_latency -eq [System.DBNull]::Value)) {
[System.DBNull]::Value
} else {
($info.distributor_latency + $info.subscriber_latency)
}
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.InstanceName
SqlInstance = $server.SqlInstance
TokenID = $tracerTokenId
TokenCreateDate = $token.PublisherCommitTime
PublicationServer = $publication.Server
PublicationDB = $publication.Database
PublicationName = $publication.PublicationName
PublicationType = $publication.PublicationType
DistributionServer = $distributionServer
DistributionDB = $distributionDatabase
SubscriberServer = $info.subscriber
SubscriberDB = $info.subscriber_db
PublisherToDistributorLatency = $info.distributor_latency
DistributorToSubscriberLatency = $info.subscriber_latency
TotalLatency = $totalLatency
} | Select-DefaultView -ExcludeProperty PublicationType
if (!$RetainToken) {
$pubMon.CleanUpTracerTokenHistory($tracerTokenId)
}
}
}
}
}
}
}
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 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 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaServerName
.EXAMPLE
PS C:\> Test-DbaServerName -SqlInstance sqlserver2014a
Returns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a.
.EXAMPLE
PS C:\> Test-DbaServerName -SqlInstance sqlserver2014a, sql2016
Returns ServerInstanceName, SqlServerName, IsEqual and RenameRequired for sqlserver2014a and sql2016.
.EXAMPLE
PS C:\> 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
PS C:\> 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 server name is not updatable.
#>
[CmdletBinding()]
[OutputType([System.Collections.ArrayList])]
param (
[parameter(Mandatory, ValueFromPipeline)]
[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) {
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-DbaService -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.
- 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 | niphlod
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaSpn
.EXAMPLE
Test-DbaSpn -ComputerName SQLSERVERA -Credential ad\sqldba
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 ad\sqldba
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 ad\sqldba
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()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "", Justification = "PSSA Rule Ignored by BOH")]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[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
}
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
}
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
function Test-DbaTempdbConfig {
<#
.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.
* Data File Size Equal - Are the sizes of all the tempdb data files the same?
Other rules can be added at a future date.
.PARAMETER SqlInstance
The target SQL Server instance or instances. 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Based on Amit Bannerjee's (@banerjeeamit) Get-TempDB function (https://github.com/amitmsft/SqlOnAzureVM/blob/master/Get-TempdbFiles.ps1)
.LINK
https://dbatools.io/Test-DbaTempdbConfig
.EXAMPLE
PS C:\> Test-DbaTempdbConfig -SqlInstance localhost
Checks tempdb on the localhost machine.
.EXAMPLE
PS C:\> Test-DbaTempdbConfig -SqlInstance localhost | Select-Object *
Checks tempdb on the localhost machine. All rest results are shown.
.EXAMPLE
PS C:\> Get-DbaCmsRegServer -SqlInstance sqlserver2014a | Test-DbaTempdbConfig | Select-Object * | Out-GridView
Checks tempdb configuration for a group of servers from SQL Server Central Management Server (CMS). Output includes all columns. Send output to GridView.
#>
[CmdletBinding()]
param (
[parameter(Mandatory, ValueFromPipeline)]
[Alias("ServerInstance", "SqlServer")]
[DbaInstance[]]$SqlInstance,
[PSCredential]$SqlCredential,
[switch]$Detailed,
[switch]$EnableException
)
begin {
Test-DbaDeprecation -DeprecatedOn 1.0.0 -Parameter Detailed
}
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
}
# removed previous assumption that 2016+ will have it enabled
$tfCheck = $server.Databases['tempdb'].Query("DBCC TRACEON (3604);DBCC TRACESTATUS(-1)")
$current = ($tfCheck.TraceFlag -join ',').Contains('1118')
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Rule = 'TF 1118 Enabled'
Recommended = $true
CurrentSetting = $current
IsBestPractice = $current -eq $true
Notes = 'KB328551 describes how TF 1118 can benefit performance. SQL Server 2016 has this functionality enabled by default.'
}
Write-Message -Level Verbose -Message "TF 1118 evaluated"
#get files and log files
$tempdbFiles = Get-DbaDbFile -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"
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Rule = 'File Count'
Recommended = [Math]::Min(8, $server.Processors)
CurrentSetting = $dataFiles.Count
IsBestPractice = $dataFiles.Count -eq [Math]::Min(8, $server.Processors)
Notes = 'Microsoft recommends that the number of tempdb data files is equal to the number of logical cores up to 8.'
}
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
}
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Rule = 'File Growth in Percent'
Recommended = $false
CurrentSetting = $totalCount
IsBestPractice = $totalCount -eq $false
Notes = 'Set file growth to explicit values, not by percent.'
}
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
}
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Rule = 'File Location'
Recommended = $false
CurrentSetting = $cdata
IsBestPractice = $cdata -eq $false
Notes = "Do not place your tempdb files on C:\."
}
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
}
[PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Rule = 'File MaxSize Set'
Recommended = $false
CurrentSetting = $growthLimits
IsBestPractice = $growthLimits -eq $false
Notes = "Consider setting your tempdb files to unlimited growth."
}
Write-Message -Level Verbose -Message "MaxSize values evaluated."
#Test Data File Size Equal
$distinctCountSizeDataFiles = ($dataFiles | Group-Object -Property Size | Measure-Object).Count
if ($distinctCountSizeDataFiles -eq 1) {
$equalSizeDataFiles = $true
} else {
$equalSizeDataFiles = $false
}
$value = [PSCustomObject]@{
ComputerName = $server.ComputerName
InstanceName = $server.ServiceName
SqlInstance = $server.DomainInstanceName
Rule = 'Data File Size Equal'
Recommended = $true
CurrentSetting = $equalSizeDataFiles
IsBestPractice = $equalSizeDataFiles -eq $true
Notes = "Consider creating equally sized data files."
}
Write-Message -Level Verbose -Message "Data File Size Equal evaluated."
}
}
end {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-SqlTempDbConfiguration
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Test-DbaTempDbConfiguration
}
}
#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/ | Chrissy LeMaire (@cl)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Test-DbaWindowsLogin
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> Test-DbaWindowsLogin -SqlInstance Dev01 -IgnoreDomains testdomain
Tests all Domain logins excluding any that are from the testdomain
#>
[CmdletBinding()]
param (
[parameter(Position = 0, Mandatory, ValueFromPipeline)]
[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
}
}
#ValidationTags#CodeStyle,Messaging,FlowControl,Pipeline#
Function Uninstall-DbaSqlWatch {
<#
.SYNOPSIS
Uninstalls SqlWatch.
.DESCRIPTION
Deletes all user objects, agent jobs, and historical data associated with SqlWatch.
.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 install SqlWatch into. Defaults to master.
.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: SqlWatch
Author: Ken K (github.com/koglerk)
Website: https://sqlwatch.io
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Uninstall-DbaSqlWatch
.EXAMPLE
Uninstall-DbaSqlWatch -SqlInstance server1
Deletes all user objects, agent jobs, and historical data associated with SqlWatch from the master database.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[DbaInstanceParameter[]]$SqlInstance,
[PSCredential]$SqlCredential,
[string]$Database = "master",
[switch]$EnableException
)
begin {
# validate database parameter
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.'
} 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.' -ErrorRecord $_
}
}
}
process {
if (Test-FunctionInterrupt) {
return
}
foreach ($instance in $SqlInstance) {
try {
$server = Connect-SqlInstance -SqlInstance $instance -SqlCredential $SqlCredential
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}
# get SqlWatch objects
$tables = Get-DbaDbTable -SqlInstance $server -Database $Database | Where-Object {$PSItem.Name -like "sql_perf_mon_*" -or $PSItem.Name -like "logger_*" }
$views = Get-DbaDbView -SqlInstance $server -Database $Database | Where-Object {$PSItem.Name -like "vw_sql_perf_mon_*" }
$sprocs = Get-DbaDbStoredProcedure -SqlInstance $server -Database $Database | Where-Object {$PSItem.Name -like "sp_sql_perf_mon_*" -or $PSItem.Name -like "usp_logger_*" }
$agentJobs = Get-DbaAgentJob -SqlInstance $server | Where-Object { ($PSItem.Name -like "SqlWatch-*") -or ($PSItem.Name -like "DBA-PERF-*") }
if ($PSCmdlet.ShouldProcess($server, "Removing SqlWatch SQL Agent jobs")) {
try {
Write-Message -Level Verbose -Message "Removing SQL Agent jobs from $server."
$agentJobs | Remove-DbaAgentJob | Out-Null
} catch {
Stop-Function -Message "Could not remove all SqlWatch Agent Jobs on $server." -ErrorRecord $_ -Target $server -Continue
}
}
if ($PSCmdlet.ShouldProcess($server, "Removing SqlWatch stored procedures")) {
try {
Write-Message -Level Verbose -Message "Removing SqlWatch stored procedures from $database on $server."
$dropScript = ""
$sprocs | ForEach-Object {
$dropScript += "DROP PROCEDURE $($PSItem.Name);`n"
}
if ($dropScript) {
Invoke-DbaQuery -SqlInstance $server -Database $Database -Query $dropScript
}
} catch {
Stop-Function -Message "Could not remove all SqlWatch stored procedures from $database on $server." -ErrorRecord $_ -Target $server -Continue
}
}
if ($PSCmdlet.ShouldProcess($server, "Removing SqlWatch views")) {
try {
Write-Message -Level Verbose -Message "Removing SqlWatch views from $database on $server."
$dropScript = ""
$views | ForEach-Object {
$dropScript += "DROP VIEW $($PSItem.Name);`n"
}
if ($dropScript) {
Invoke-DbaQuery -SqlInstance $server -Database $Database -Query $dropScript
}
} catch {
Stop-Function -Message "Could not remove all SqlWatch views from $database on $server." -ErrorRecord $_ -Target $server -Continue
}
}
if ($PSCmdlet.ShouldProcess($server, "Removing SqlWatch tables")) {
try {
Write-Message -Level Verbose -Message "Removing foreign keys from SqlWatch tables in $database on $server."
if ($tables.ForeignKeys) {
$tables.ForeignKeys | ForEach-Object { $PSItem.Drop() }
}
} catch {
Stop-Function -Message "Could not remove all foreign keys from SqlWatch tables in $database on $server." -ErrorRecord $_ -Target $server -Continue
}
try {
Write-Message -Level Verbose -Message "Removing SqlWatch tables from $database on $server."
$dropScript = ""
$tables | ForEach-Object {
$dropScript += "DROP TABLE $($PSItem.Name);`n"
}
if ($dropScript) {
Invoke-DbaQuery -SqlInstance $server -Database $Database -Query $dropScript
}
} catch {
Stop-Function -Message "Could not remove all SqlWatch tables from $database on $server." -ErrorRecord $_ -Target $server -Continue
}
}
if ($PSCmdlet.ShouldProcess($server, "Unpublishing DACPAC")) {
try {
Write-Message -Level Verbose -Message "Unpublishing SqlWatch DACPAC from $database on $server."
$connectionString = $server | Select-Object -ExpandProperty ConnectionContext
$dacServices = New-Object Microsoft.SqlServer.Dac.DacServices $connectionString
$dacServices.Unregister($Database)
} catch {
Stop-Function -Message "Failed to unpublish SqlWatch DACPAC from $database on $server." -ErrorRecord $_
}
}
}
}
}
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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Uninstall-DbaWatchUpdate
.EXAMPLE
PS C:\> Uninstall-DbaWatchUpdate
Removes the scheduled task created by Install-DbaWatchUpdate.
#>
process {
if (([Environment]::OSVersion).Version.Major -lt 10) {
Write-Message -Level Warning -Message "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-Message -Level Warning -Message "Task doesn't exist. Skipping removal."
} else {
Write-Message -Level Output -Message "Removing watchupdate.xml."
$file = "$env:LOCALAPPDATA\dbatools\watchupdate.xml"
Remove-Item $file -ErrorAction SilentlyContinue
Write-Message -Level Output -Message "Removing Scheduled Task 'dbatools version check'."
$task | Unregister-ScheduledTask -Confirm:$false -ErrorAction Stop
Write-Message -Level Output -Message "Task removed"
Start-Sleep -Seconds 2
}
} catch {
Write-Message -Level Warning -Message "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-Message -Level Warning -Message "dbatools update watcher is not installed."
return
}
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Message -Level Warning -Message "Removal of this scheduled task requires elevated permissions."
Start-Process powershell -Verb runAs -ArgumentList Uninstall-DbaWatchUpdate -Wait
} else {
Invoke-Command -ScriptBlock $script
}
Write-Message -Level Output -Message "All done!"
}
}
function Update-DbaInstance {
<#
.SYNOPSIS
Invokes installation of SQL Server Service Packs and Cumulative Updates on local and remote servers.
.DESCRIPTION
Starts and automated process of updating SQL Server installation to a specific version defined in the parameters.
The command will:
* Search for SQL Server installations in a remote registry
* Check if current settings are applicable to the current SQL Server versions
* Search for a KB executable in a folder specified in -Path
* Establish a PSRemote connection to the target machine if necessary
* Extract KB to a temporary folder in a current user's profile
* Run the installation from the temporary folder updating all instances on the computer at once
* Remove temporary files
* Restart the computer (if -Restart is specified)
* Repeat for each consequent KB and computer
The impact of this function is set to High, if you don't want to receive interactive prompts, set -Confirm to $false.
Credentials are a required parameter for remote machines. Without specifying -Credential, the installation will fail due to lack of permissions.
CredSSP is a recommended transport for running the updates remotely. Update-DbaInstance will attempt to reconfigure
local and remote hosts to support CredSSP, which is why it is desirable to run this command in an elevated console at all times.
CVE-2018-0886 security update is required for both local and remote hosts. If CredSSP connections are failing, make sure to
apply recent security updates prior to doing anything else.
Always backup databases and configurations prior to upgrade.
.PARAMETER ComputerName
Target computer with SQL instance or instsances.
.PARAMETER Credential
Windows Credential with permission to log on to the remote server. Must be specified for any remote connection.
.PARAMETER Type
Type of the update: All | ServicePack | CumulativeUpdate. Mutually exclusive with -Version.
Default: All
.PARAMETER KB
Install a specific update or list of updates. Can be a number of a string KBXXXXXXX.
.PARAMETER Version
A target version of the installation you want to reach. If not specified, a latest available version would be used by default.
Can be defined using the following general pattern: <MajorVersion><SPX><CUX>.
Any part of the pattern can be ommitted if needed:
2008R2SP1 - will update SQL 2008R2 to SP1
2016CU3 - will update SQL 2016 to CU3 of current Service Pack installed
SP0CU3 - will update all existing SQL Server versions to RTM CU3 without installing any service packs
SP1CU7 - will update all existing SQL Server versions to SP1 and then (after restart if -Restart is specified) to SP1CU7
CU7 - will update all existing SQL Server versions to CU7 of current Service Pack installed
.PARAMETER MajorVersion
When -Version is not specified, it allows user to only update specific version(s) of SQL Server. Syntax: SQL20XX or simply 20XX.
.PARAMETER Path
Path to the folder(s) with SQL Server patches downloaded. It will be scanned recursively for available patches.
Path should be available from both server with SQL Server installation and client that runs the command.
All file names should match the pattern used by Microsoft: SQLServer####*-KB###-*x##*.exe
If a file is missing in the repository, the installation will fail.
Consider setting the following configuration if you want to omit this parameter: `Set-DbatoolsConfig -Name Path.SQLServerUpdates -Value '\\path\to\updates'`
.PARAMETER Restart
Restart computer automatically after a successful installation of a patch and wait until it comes back online.
Using this parameter is the only way to chain-install more than 1 patch on a computer, since every single patch will require a restart of said computer.
.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: Install, Patching, SP, CU, Instance
Author: Kirill Kravtsov (@nvarscar) https://nvarscar.wordpress.com/
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires Local Admin rights on destination computer(s).
.EXAMPLE
PS C:\> Update-DbaInstance -ComputerName SQL1 -Version SP3 -Path \\network\share
Updates all applicable SQL Server installations on SQL1 to SP3.
Binary files for the update will be searched among all files and folders recursively in \\network\share.
.EXAMPLE
PS C:\> Update-DbaInstance -ComputerName SQL1, SQL2 -Restart -Path \\network\share
Updates all applicable SQL Server installations on SQL1 and SQL2 with the most recent patch.
It will install latest ServicePack, restart the computers, install latest Cumulative Update, and finally restart the computer once again.
Binary files for the update will be searched among all files and folders recursively in \\network\share.
.EXAMPLE
PS C:\> Update-DbaInstance -ComputerName SQL1 -MajorVersion 2012 -Type ServicePack -Path \\network\share
Updates SQL Server 2012 on SQL1 with the most recent ServicePack found in your patch repository.
Binary files for the update will be searched among all files and folders recursively in \\network\share.
.EXAMPLE
PS C:\> Update-DbaInstance -ComputerName SQL1 -KB 123456 -Restart -Path \\network\share
Installs KB 123456 on SQL1 and restarts the computer.
Binary files for the update will be searched among all files and folders recursively in \\network\share.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "")]
# Shouldprocess is handled by internal function Install-SqlServerUpdate
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = 'Latest')]
Param (
[parameter(ValueFromPipeline, Position = 1)]
[Alias("cn", "host", "Server")]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[pscredential]$Credential,
[Parameter(Mandatory, ParameterSetName = 'Version')]
[ValidateNotNullOrEmpty()]
[string[]]$Version,
[Parameter(ParameterSetName = 'Latest')]
[string[]]$MajorVersion,
[Parameter(ParameterSetName = 'Latest')]
[ValidateSet('All', 'ServicePack', 'CumulativeUpdate')]
[string]$Type = 'All',
[Parameter(Mandatory, ParameterSetName = 'KB')]
[ValidateNotNullOrEmpty()]
[string[]]$KB,
[string[]]$Path,
[switch]$Restart,
[switch]$EnableException
)
begin {
#Validating parameters
if ($PSCmdlet.ParameterSetName -eq 'Version') {
if ($Version -notmatch '^((SQL)?\d{4}(R2)?)?\s*(RTM|SP\d+)?\s*(CU\d+)?$') {
Stop-Function -Category InvalidArgument -Message "$Version is an incorrect Version value, please refer to Get-Help Update-DbaInstance -Parameter Version"
return
}
} elseif ($PSCmdlet.ParameterSetName -eq 'Latest') {
$majorVersions = @()
foreach ($mv in $MajorVersion) {
if ($mv -match '^(SQL)?(\d{4}(R2)?)$') {
$majorVersions += $Matches[2]
} else {
Stop-Function -Category InvalidArgument -Message "$mv is an incorrect MajorVersion value, please refer to Get-Help Update-DbaInstance -Parameter MajorVersion"
return
}
}
} elseif ($PSCmdlet.ParameterSetName -eq 'KB') {
$kbList = @()
foreach ($kbItem in $KB) {
if ($kbItem -match '^(KB)?(\d+)$') {
$kbList += $Matches[2]
} else {
Stop-Function -Category InvalidArgument -Message "$kbItem is an incorrect KB value, please refer to Get-Help Update-DbaInstance -Parameter KB"
return
}
}
}
$actions = @()
#Putting together list of actions based on current ParameterSet
if ($PSCmdlet.ParameterSetName -eq 'Latest') {
if ($Type -in 'All', 'ServicePack') {
$actions += @{
Type = 'ServicePack'
MajorVersion = $majorVersions
}
}
if ($Type -in 'All', 'CumulativeUpdate') {
$actions += @{
Type = 'CumulativeUpdate'
MajorVersion = $majorVersions
}
}
} elseif ($PSCmdlet.ParameterSetName -eq 'Version') {
foreach ($ver in $Version) {
$currentAction = @{
}
if ($ver -and $ver -match '^(SQL)?(\d{4}(R2)?)?\s*(RTM|SP)?(\d+)?(CU)?(\d+)?') {
Write-Message -Level Debug "Parsed Version as $($Matches[2, 5, 7] | ConvertTo-Json -Depth 1 -Compress)"
if (-not ($Matches[5] -or $Matches[7])) {
Stop-Function -Category InvalidArgument -Message "Either SP or CU should be specified in $ver, please refer to Get-Help Update-DbaInstance -Parameter Version"
return
}
if ($null -ne $Matches[2]) {
$currentAction += @{
MajorVersion = $Matches[2]
}
}
if ($null -ne $Matches[5]) {
$currentAction += @{
ServicePack = $Matches[5]
}
if ($Matches[5] -ne '0') {
$actions += $currentAction
}
}
if ($null -ne $Matches[7]) {
$actions += $currentAction.Clone() + @{
CumulativeUpdate = $Matches[7]
}
}
} else {
Stop-Function -Category InvalidArgument -Message "$ver is an incorrect Version value, please refer to Get-Help Update-DbaInstance -Parameter Version"
return
}
}
} elseif ($PSCmdlet.ParameterSetName -eq 'KB') {
foreach ($kbItem in $kbList) {
$actions += @{
KB = $kbItem
}
}
}
}
process {
if (Test-FunctionInterrupt) { return }
#Resolve all the provided names
$resolvedComputers = @()
foreach ($computer in $ComputerName) {
$null = Test-ElevationRequirement -ComputerName $computer -Continue
if ($resolvedComputer = Resolve-DbaNetworkName -ComputerName $computer.ComputerName) {
$resolvedComputers += $resolvedComputer.FullComputerName
}
}
#Initialize installations for each computer
:computers foreach ($resolvedName in $resolvedComputers) {
:actions foreach ($actionParam in $actions) {
if (Test-PendingReboot -ComputerName $resolvedName) {
#Exit the actions loop altogether - nothing can be installed here anyways
Stop-Function -Message "$resolvedName is pending a reboot. Reboot the computer before proceeding." -Continue -ContinueLabel computers
}
Write-Message -Level Verbose -Message "Launching installation on $resolvedName with following params: $($actionParam | ConvertTo-Json -Depth 1 -Compress)"
$install = Install-SqlServerUpdate @actionParam -ComputerName $resolvedName -Credential $Credential -Restart $Restart -Path $Path
if ($install) {
$install
if ($install.Successful -contains $false) {
#Exit the actions loop altogether - upgrade failed
Stop-Function -Message "Update failed to install on $resolvedName" -Continue -ContinueLabel computers
}
if ($install.Restarted -contains $false) {
Stop-Function -Message "Please restart $($install.ComputerName) to complete the installation of SQL$($install.MajorVersion)$($install.TargetLevel). No further updates will be installed on this computer." -EnableException $false -Continue -ContinueLabel computers
}
}
}
}
}
}
function Update-DbaServiceAccount {
<#
.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 target SQL Server instance or 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-DbaService.
.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 PreviousPassword
An old password of the service account. Optional when run under local admin privileges.
.PARAMETER SecurePassword
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 -SecurePassword 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)
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires Local Admin rights on destination computer(s).
.EXAMPLE
PS C:\> $SecurePassword = ConvertTo-SecureString 'Qwerty1234' -AsPlainText -Force
Update-DbaServiceAccount -ComputerName sql1 -ServiceName 'MSSQL$MYINSTANCE' -SecurePassword $SecurePassword
Changes the current service account's password of the service MSSQL$MYINSTANCE to 'Qwerty1234'
.EXAMPLE
PS C:\> $cred = Get-Credential
PS C:\> Get-DbaService sql1 -Type Engine,Agent -Instance MYINSTANCE | Update-DbaServiceAccount -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
PS C:\> Update-DbaServiceAccount -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
PS C:\> Get-DbaService sql1 -Type Engine -Instance MSSQLSERVER | Update-DbaServiceAccount -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, DefaultParameterSetName = "ServiceName" )]
param (
[parameter(ParameterSetName = "ServiceName")]
[Alias("cn", "host", "Server")]
[DbaInstanceParameter[]]$ComputerName = $env:COMPUTERNAME,
[PSCredential]$Credential,
[parameter(ValueFromPipeline, Mandatory, ParameterSetName = "InputObject")]
[Alias("ServiceCollection")]
[object[]]$InputObject,
[parameter(ParameterSetName = "ServiceName", Position = 1, Mandatory)]
[Alias("Name", "Service")]
[string[]]$ServiceName,
[Alias("User")]
[string]$Username,
[PSCredential]$ServiceCredential,
[securestring]$PreviousPassword = (New-Object System.Security.SecureString),
[Alias("Password", "NewPassword")]
[securestring]$SecurePassword = (New-Object System.Security.SecureString),
[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 ($SecurePassword.Length -eq 0 -and $PSBoundParameters.Keys -notcontains 'SecurePassword' -and $trimmedUsername -notin 'NETWORKSERVICE', 'LOCALSYSTEM', 'LOCALSERVICE' -and $Username.EndsWith('$') -eq $false -and $Username.StartsWith('NT Service\') -eq $false) {
$SecurePassword = 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", $SecurePassword)).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, $SecurePassword)
} 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 {
Test-DbaDeprecation -DeprecatedOn "1.0.0" -EnableException:$false -Alias Update-DbaSqlServiceAccount
foreach ($svc in $svcCollection) {
if ($serviceObject = Get-DbaService -ComputerName $svc.ComputerName -ServiceName $svc.ServiceName -Credential $Credential -EnableException:$EnableException) {
$outMessage = $outStatus = $agent = $null
if ($actionType -eq 'Password' -and $SecurePassword.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 = $SecurePassword
}
if ($serviceObject.ServiceType -eq 'Engine') {
#Get SQL Agent running status
$agent = Get-DbaService -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", $PreviousPassword)).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-DbaService -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-DbaService -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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Update-DbaTools
.EXAMPLE
PS C:\> Update-Dbatools
Updates dbatools. Deletes current copy and replaces it with freshest copy.
.EXAMPLE
PS C:\> Update-Dbatools -dev
Updates dbatools to the current development branch. Deletes current copy and replaces it with latest from github.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "It is the proper noun of the cmdlet")]
param(
[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 DMV's 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
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
Requires: sysadmin access on all SQL Servers for the most accurate results
.LINK
https://dbatools.io/Watch-DbaDbLogin
.EXAMPLE
PS C:\> 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
PS C:\> 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
PS C:\> 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)]
[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
}
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-DbaCmsRegServerName -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) {
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-Message -Level Warning -Message "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-Message -Level Output -Message "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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Watch-DbaUpdate
.EXAMPLE
PS C:\> Watch-DbaUpdate
Watches the gallery for updates to dbatools.
#>
[cmdletbinding()]
param()
process {
if (([Environment]::OSVersion).Version.Major -lt 10) {
Write-Message -Level Warning -Message "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
}
}
}
#ValidationTags#Messaging,FlowControl,Pipeline,CodeStyle#
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
The target SQL Server instance or instances. 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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Watch-DbaXESession
.EXAMPLE
PS C:\> Watch-DbaXESession -SqlInstance sql2017 -Session system_health
Shows events for the system_health session as it happens.
.EXAMPLE
PS C:\> 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
PS C:\> 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) {
} else {
try {
$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
}
foreach ($xesession in $InputObject) {
$server = $xesession.Parent
$sessionname = $xesession.Name
Write-Message -Level Verbose -Message "Watching $sessionname on $($server.Name)."
if (-not $xesession.IsRunning -and -not $xesession.IsRunning) {
Stop-Function -Message "$($xesession.Name) is not running on $($server.Name)" -Continue
}
# Setup all columns for csv but do it in an order
$columns = @("name", "timestamp")
$newcolumns = @()
$fields = ($xesession.Events.EventFields.Name | Select-Object -Unique)
foreach ($column in $fields) {
$newcolumns += $column.TrimStart("collect_")
}
$actions = ($xesession.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),
($xesession.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 $sessionname
if ($status.Status -ne "Running") {
Stop-Function -Message "$($xesession.Name) was stopped."
} else {
Stop-Function -Message "Failure" -ErrorRecord $_ -Target $sessionname
}
} finally {
if ($xevent -is [IDisposable]) {
$xevent.Dispose()
}
}
}
}
}
#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 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 to import the table into.
.PARAMETER InputObject
This is the DataTable (or data row) 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.
If the object has special characters please wrap them in square brackets [ ].
Using dbo.First.Table will try to import to a table named 'Table' on schema 'First' and database 'dbo'.
The correct way to import to a table named 'First.Table' on schema 'dbo' is by passing dbo.[First.Table]
Any actual usage of the ] must be escaped by duplicating the ] character.
The correct way to import to a table Name] in schema Schema.Name is by passing [Schema.Name].[Name]]]
.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
Author: Chrissy LeMaire (@cl), netnerds.net
Website: https://dbatools.io
Copyright: (c) 2018 by dbatools, licensed under MIT
License: MIT https://opensource.org/licenses/MIT
.LINK
https://dbatools.io/Write-DbaDataTable
.EXAMPLE
PS C:\> $DataTable = Import-Csv C:\temp\customers.csv
PS C:\> 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
PS C:\> $DataTable = Import-Csv C:\temp\customers.csv
PS C:\> $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
PS C:\> $DataTable = Import-Csv C:\temp\customers.csv
PS C:\> Write-DbaDataTable -SqlInstance sql2014 -InputObject $DataTable -Table mydb.dbo.customers -AutoCreateTable -Confirm
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.
Prompts for confirmation before a variety of steps.
.EXAMPLE
PS C:\> $DataTable = Import-Csv C:\temp\customers.csv
PS C:\> 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
PS C:\> $DataTable = Import-Csv C:\temp\customers.csv
PS C:\> 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
PS C:\> $passwd = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
PS C:\> $AzureCredential = New-Object System.Management.Automation.PSCredential("AzureAccount"),$passwd)
PS C:\> $DataTable = Import-Csv C:\temp\customers.csv
PS C:\> 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
PS C:\> $process = Get-Process
PS C:\> Write-DbaDataTable -InputObject $process -SqlInstance sql2014 -Table "[[DbName]]].[Schema.With.Dots].[`"[Process]]`"]" -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
Writes the results of Get-Process to a table named: "[Process]" in schema named: Schema.With.Dots in database named: [DbName]
The Table name, Schema name and Database name must be wrapped in square brackets [ ]
Special charcters like " must be escaped by a ` charcter.
In addition any actual instance of the ] character must be escaped by being duplicated.
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 ConvertTo-DbaDataTable.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Low")]
param (
[Parameter(Position = 0, Mandatory)]
[Alias("ServerInstance", "SqlServer")]
[ValidateNotNull()]
[DbaInstanceParameter]$SqlInstance,
[Parameter(Position = 1)]
[ValidateNotNull()]
[Alias("Credential")]
[PSCredential]$SqlCredential,
[Parameter(Position = 2)]
[object]$Database,
[Parameter(Mandatory, ValueFromPipeline)]
[Alias("DataTable")]
[ValidateNotNull()]
[object]$InputObject,
[Parameter(Position = 3, Mandatory)]
[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(SupportsShouldProcess)]
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
$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);
}
}
}'
# Load the basics
if (-not $script:core) {
try {
Add-Type -ReferencedAssemblies System.Data.dll -TypeDefinition $sourcecode -ErrorAction Stop
} catch {
$null = 1
}
}
#endregion Prepare type for bulk copy
#region Resolve Full Qualified Table Name
$fqtnObj = Get-TableNameParts $Table
if ($fqtnObj.$parsed) {
Stop-Function -Message "Unable to parse $($fqtnObj.InputValue) as a valid tablename."
return
}
if ($null -eq $fqtnObj.Database -and $null -eq $Database) {
Stop-Function -Message "You must specify a database or fully qualified table name."
return
}
if (Test-Bound -ParameterName Database) {
if ($null -eq $fqtnObj.Database) {
$databaseName = "$Database"
} else {
if ($fqtnObj.Database -eq $Database) {
$databaseName = "$Database"
} else {
Stop-Function -Message "The database parameter $($Database) differs from value from the fully qualified table name $($fqtnObj.Database)."
return
}
}
} else {
$databaseName = $fqtnObj.Database
}
if ($fqtnObj.Schema) {
$schemaName = $fqtnObj.Schema
} else {
$schemaName = $Schema
}
$tableName = $fqtnObj.Table
$quotedFQTN = New-Object System.Text.StringBuilder
[void]$quotedFQTN.Append( '[' )
if ($databaseName.Contains(']')) {
[void]$quotedFQTN.Append( $databaseName.Replace(']', ']]') )
} else {
[void]$quotedFQTN.Append( $databaseName )
}
[void]$quotedFQTN.Append( '].' )
[void]$quotedFQTN.Append( '[' )
if ($schemaName.Contains(']')) {
[void]$quotedFQTN.Append( $schemaName.Replace(']', ']]') )
} else {
[void]$quotedFQTN.Append( $schemaName )
}
[void]$quotedFQTN.Append( '].' )
[void]$quotedFQTN.Append( '[' )
if ($tableName.Contains(']')) {
[void]$quotedFQTN.Append( $tableName.Replace(']', ']]') )
} else {
[void]$quotedFQTN.Append( $tableName )
}
[void]$quotedFQTN.Append( ']' )
$fqtn = $quotedFQTN.ToString()
Write-Message -Level SomewhatVerbose -Message "FQTN processed: $fqtn"
#endregion Resolve Full Qualified Table Name
#region Connect to server and get database
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 {
# here to avoid an empty catch
$null = 1
}
}
$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(38,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(38,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-DbatoolsConfigValue -FullName 'commands.write-dbadatatable.timespantype' -Fallback 'TotalMilliseconds')
SizeType = (Get-DbatoolsConfigValue -FullName 'commands.write-dbadatatable.sizetype' -Fallback 'Int64')
IgnoreNull = (Get-DbatoolsConfigValue -FullName 'commands.write-dbadatatable.ignorenull' -Fallback $false)
Raw = (Get-DbatoolsConfigValue -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 Read-DbatoolsConfigFile
{
<#
.SYNOPSIS
Reads a configuration file and parses it.
.DESCRIPTION
Reads a configuration file and parses it.
.PARAMETER Path
The path to the file to parse.
.PARAMETER WebLink
The link to a website to download straight as raw json.
.PARAMETER RawJson
Raw json data to interpret.
.EXAMPLE
PS C:\> Read-DbatoolsConfigFile -Path config.json
Reads the config.json file and returns interpreted configuration objects.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ParameterSetName = 'Path')]
[string]
$Path,
[Parameter(Mandatory = $true, ParameterSetName = 'Weblink')]
[string]
$Weblink,
[Parameter(Mandatory = $true, ParameterSetName = 'RawJson')]
[string]
$RawJson
)
#region Utility Function
function New-ConfigItem
{
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
[CmdletBinding()]
param (
$FullName,
$Value,
$Type,
[switch]
$KeepPersisted,
[switch]
$Enforced,
[switch]
$Policy
)
[pscustomobject]@{
FullName = $FullName
Value = $Value
Type = $Type
KeepPersisted = $KeepPersisted
Enforced = $Enforced
Policy = $Policy
}
}
function Get-WebContent
{
[CmdletBinding()]
param (
[string]
$WebLink
)
$webClient = New-Object System.Net.WebClient
$webClient.Encoding = [System.Text.Encoding]::UTF8
$webClient.DownloadString($WebLink)
}
#endregion Utility Function
if ($Path)
{
if (-not (Test-Path $Path)) { return }
$data = Get-Content -Path $Path -Encoding UTF8 | ConvertFrom-Json -ErrorAction Stop
}
if ($Weblink)
{
$data = Get-WebContent -WebLink $Weblink | ConvertFrom-Json -ErrorAction Stop
}
if ($RawJson)
{
$data = $RawJson | ConvertFrom-Json -ErrorAction Stop
}
foreach ($item in $data)
{
#region No Version
if (-not $item.Version)
{
New-ConfigItem -FullName $item.FullName -Value ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::ConvertFromPersistedValue($item.Value, $item.Type))
}
#endregion No Version
#region Version One
if ($item.Version -eq 1)
{
if ((-not $item.Style) -or ($item.Style -eq "Simple")) { New-ConfigItem -FullName $item.FullName -Value $item.Data }
else
{
if (($item.Type -eq "Object") -or ($item.Type -eq 12))
{
New-ConfigItem -FullName $item.FullName -Value $item.Value -Type "Object" -KeepPersisted
}
else
{
New-ConfigItem -FullName $item.FullName -Value ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::ConvertFromPersistedValue($item.Value, $item.Type))
}
}
}
#endregion Version One
}
}
function Read-DbatoolsConfigPersisted
{
<#
.SYNOPSIS
Reads configurations from persisted file / registry.
.DESCRIPTION
Reads configurations from persisted file / registry.
.PARAMETER Scope
Where to read from.
.PARAMETER Module
Load module specific data.
Use this to load on-demand configuration only when the module is imported.
Useful when using the config system as cache.
.PARAMETER ModuleVersion
The configuration version of the module-settings to load.
.PARAMETER Hashtable
Rather than returning results, insert them into this hashtable.
.PARAMETER Default
When inserting into a hashtable, existing values are overwritten by default.
Enabling this setting will cause it to only insert values if the key does not exist yet.
.EXAMPLE
Read-DbatoolsConfigPersisted -Scope 127
Read all persisted default configuration items in the default mandated order.
#>
[OutputType([System.Collections.Hashtable])]
[CmdletBinding()]
Param (
[Sqlcollaborative.Dbatools.Configuration.ConfigScope]
$Scope,
[string]
$Module,
[int]
$ModuleVersion = 1,
[System.Collections.Hashtable]
$Hashtable,
[switch]
$Default
)
begin
{
#region Helper Functions
function New-ConfigItem
{
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
[CmdletBinding()]
param (
$FullName,
$Value,
$Type,
[switch]
$KeepPersisted,
[switch]
$Enforced,
[switch]
$Policy
)
[pscustomobject]@{
FullName = $FullName
Value = $Value
Type = $Type
KeepPersisted = $KeepPersisted
Enforced = $Enforced
Policy = $Policy
}
}
function Read-Registry
{
[CmdletBinding()]
param (
$Path,
[switch]
$Enforced
)
if (-not (Test-Path $Path)) { return }
$common = 'PSPath', 'PSParentPath', 'PSChildName', 'PSDrive', 'PSProvider'
foreach ($item in ((Get-ItemProperty -Path $Path -ErrorAction Ignore).PSObject.Properties | Where-Object Name -NotIn $common))
{
if ($item.Value -like "Object:*")
{
$data = $item.Value.Split(":", 2)
New-ConfigItem -FullName $item.Name -Type $data[0] -Value $data[1] -KeepPersisted -Enforced:$Enforced -Policy
}
else
{
try { New-ConfigItem -FullName $item.Name -Value ([Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::ConvertFromPersistedValue($item.Value)) -Policy }
catch
{
Write-Message -Level Warning -Message "Failed to load configuration from Registry: $($item.Name)" -ErrorRecord $_ -Target "$Path : $($item.Name)"
}
}
}
}
#endregion Helper Functions
if (-not $Hashtable) { $results = @{ } }
else { $results = $Hashtable }
if ($Module) { $filename = "$($Module.ToLower())-$($ModuleVersion).json" }
else { $filename = "psf_config.json" }
}
process
{
#region File - Computer Wide
if ($Scope -band 64)
{
foreach ($item in (Read-DbatoolsConfigFile -Path (Join-Path $script:path_FileSystem $filename)))
{
if (-not $Default) { $results[$item.FullName] = $item }
elseif (-not $results.ContainsKey($item.FullName)) { $results[$item.FullName] = $item }
}
}
#endregion File - Computer Wide
#region Registry - Computer Wide
if (($Scope -band 4) -and (-not $script:NoRegistry))
{
foreach ($item in (Read-Registry -Path $script:path_RegistryMachineDefault))
{
if (-not $Default) { $results[$item.FullName] = $item }
elseif (-not $results.ContainsKey($item.FullName)) { $results[$item.FullName] = $item }
}
}
#endregion Registry - Computer Wide
#region File - User Shared
if ($Scope -band 32)
{
foreach ($item in (Read-DbatoolsConfigFile -Path (Join-Path $script:path_FileUserShared $filename)))
{
if (-not $Default) { $results[$item.FullName] = $item }
elseif (-not $results.ContainsKey($item.FullName)) { $results[$item.FullName] = $item }
}
}
#endregion File - User Shared
#region Registry - User Shared
if (($Scope -band 1) -and (-not $script:NoRegistry))
{
foreach ($item in (Read-Registry -Path $script:path_RegistryUserDefault))
{
if (-not $Default) { $results[$item.FullName] = $item }
elseif (-not $results.ContainsKey($item.FullName)) { $results[$item.FullName] = $item }
}
}
#endregion Registry - User Shared
#region File - User Local
if ($Scope -band 16)
{
foreach ($item in (Read-DbatoolsConfigFile -Path (Join-Path $script:path_FileUserLocal $filename)))
{
if (-not $Default) { $results[$item.FullName] = $item }
elseif (-not $results.ContainsKey($item.FullName)) { $results[$item.FullName] = $item }
}
}
#endregion File - User Local
#region Registry - User Enforced
if (($Scope -band 2) -and (-not $script:NoRegistry))
{
foreach ($item in (Read-Registry -Path $script:path_RegistryUserEnforced -Enforced))
{
if (-not $Default) { $results[$item.FullName] = $item }
elseif (-not $results.ContainsKey($item.FullName)) { $results[$item.FullName] = $item }
}
}
#endregion Registry - User Enforced
#region Registry - System Enforced
if (($Scope -band 8) -and (-not $script:NoRegistry))
{
foreach ($item in (Read-Registry -Path $script:path_RegistryMachineEnforced -Enforced))
{
if (-not $Default) { $results[$item.FullName] = $item }
elseif (-not $results.ContainsKey($item.FullName)) { $results[$item.FullName] = $item }
}
}
#endregion Registry - System Enforced
}
end
{
$results
}
}
function Register-DbatoolsConfigValidation {
<#
.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-DbatoolsConfigValidation -Name IntPositive -ScriptBlock $scriptblock
Registers the scriptblock stored in $scriptblock as validation with the name IntPositive
#>
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]
$Name,
[Parameter(Mandatory)]
[ScriptBlock]
$ScriptBlock
)
[Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::Validation[$Name.ToLower()] = $ScriptBlock
}
function Write-DbatoolsConfigFile
{
<#
.SYNOPSIS
Handles config export to file.
.DESCRIPTION
Handles config export to file.
.PARAMETER Config
The configuration items to export.
.PARAMETER Path
The path to export to.
Needs to point to the specific file to export to.
Will create the folder structure if needed.
.PARAMETER Replace
Completely replaces previous file contents.
By default, it will integrate settings into one coherent configuration file.
.EXAMPLE
PS C:\> Write-DbatoolsConfigFile -Config $items -Path .\file.json
Exports all settings stored in $items to .\file.json.
If the file already exists, the new settings will be merged into the existing file.
#>
[CmdletBinding()]
Param (
[Sqlcollaborative.Dbatools.Configuration.Config[]]
$Config,
[string]
$Path,
[switch]
$Replace
)
begin
{
$parent = Split-Path -Path $Path
if (-not (Test-Path $parent))
{
$null = New-Item $parent -ItemType Directory -Force
}
$data = @{ }
if ((Test-Path $Path) -and (-not $Replace))
{
foreach ($item in (Get-Content -Path $Path -Encoding UTF8 | ConvertFrom-Json))
{
$data[$item.FullName] = $item
}
}
}
process
{
foreach ($item in $Config)
{
$datum = @{
Version = 1
FullName = $item.FullName
}
if ($item.SimpleExport)
{
$datum["Data"] = $item.Value
}
else
{
$persisted = [Sqlcollaborative.Dbatools.Configuration.ConfigurationHost]::ConvertToPersistedValue($item.Value)
$datum["Value"] = $persisted.PersistedValue
$datum["Type"] = $persisted.PersistedType
$datum["Style"] = "default"
}
$data[$item.FullName] = [pscustomobject]$datum
}
}
end
{
$data.Values | ConvertTo-Json | Set-Content -Path $Path -Encoding UTF8 -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(Position = 0, ParameterSetName = 'ScriptBlock')]
[System.Management.Automation.ScriptBlock]$ScriptBlock,
[Parameter(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 {
# save default runspace
$defaultrunspace = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace
#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)] 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()
$sessionstate.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'WarningPreference', 'SilentlyContinue', $null))
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