diff --git a/.github/workflows/integration-tests-repl.yml b/.github/workflows/integration-tests-repl.yml new file mode 100644 index 0000000000..d7371ad1aa --- /dev/null +++ b/.github/workflows/integration-tests-repl.yml @@ -0,0 +1,152 @@ +name: Run Replication Tests +on: [push] +defaults: + run: + shell: pwsh +jobs: + repl-tests-part1: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + steps: + - uses: actions/checkout@v3 + + - name: Install and cache PowerShell modules + uses: potatoqualitee/psmodulecache@v5.2 + with: + modules-to-cache: dbatools.library:2023.5.5 + + - name: Set encryption values + run: | + Import-Module ./dbatools.psd1 -Force + Set-DbatoolsConfig -FullName sql.connection.trustcert -Value $true -Register + Set-DbatoolsConfig -FullName sql.connection.encrypt -Value Optional -Register + Get-DbatoolsConfigValue -FullName sql.connection.encrypt | Write-Warning + + - name: Setup docker images + run: | + # create a shared network + docker network create localnet + # Expose engine and endpoint then setup a shared path for migrations + docker run -p 1433:1433 --volume shared:/shared:z --name mssql1 --hostname mssql1 --network localnet -d dbatools/sqlinstance + # Expose second engine and endpoint on different port + docker run -p 14333:1433 --volume shared:/shared:z --name mssql2 --hostname mssql2 --network localnet -d dbatools/sqlinstance2 + + - name: Add hostname to hosts file + run: | + echo "127.0.0.1 mssql1 mssql2" | sudo tee -a /etc/hosts + + - name: 👥 Clone appveyor repo + working-directory: /tmp + run: | + gh repo clone dataplat/appveyor-lab + + - name: Setup Replication + run: | + Import-Module ./dbatools.psd1 -Force + # need some folders for our repl stuff + docker exec mssql1 mkdir /shared/data /shared/repldata /var/opt/mssql/ReplData + + - name: Run replication tests part 1 + run: | + Import-Module ./dbatools.psd1 -Force + $null = Invoke-Pester ./tests/gh-actions-repl-1.ps1 -Output Detailed -PassThru -Verbose + + repl-tests-part2: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + steps: + - uses: actions/checkout@v3 + + - name: Install and cache PowerShell modules + uses: potatoqualitee/psmodulecache@v5.2 + with: + modules-to-cache: dbatools.library:2023.5.5 + + - name: Set encryption values + run: | + Import-Module ./dbatools.psd1 -Force + Set-DbatoolsConfig -FullName sql.connection.trustcert -Value $true -Register + Set-DbatoolsConfig -FullName sql.connection.encrypt -Value Optional -Register + Get-DbatoolsConfigValue -FullName sql.connection.encrypt | Write-Warning + + - name: Setup docker images + run: | + # create a shared network + docker network create localnet + # Expose engine and endpoint then setup a shared path for migrations + docker run -p 1433:1433 --volume shared:/shared:z --name mssql1 --hostname mssql1 --network localnet -d dbatools/sqlinstance + # Expose second engine and endpoint on different port + docker run -p 14333:1433 --volume shared:/shared:z --name mssql2 --hostname mssql2 --network localnet -d dbatools/sqlinstance2 + + - name: Add hostname to hosts file + run: | + echo "127.0.0.1 mssql1 mssql2" | sudo tee -a /etc/hosts + + - name: 👥 Clone appveyor repo + working-directory: /tmp + run: | + gh repo clone dataplat/appveyor-lab + + - name: Setup Replication + run: | + Import-Module ./dbatools.psd1 -Force + # need some folders for our repl stuff + docker exec mssql1 mkdir /shared/data /shared/repldata /var/opt/mssql/ReplData + + - name: Run replication tests part 2 + run: | + Import-Module ./dbatools.psd1 -Force + $null = Invoke-Pester ./tests/gh-actions-repl-2.ps1 -Output Detailed -PassThru -Verbose + + repl-tests-part3: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + steps: + - uses: actions/checkout@v3 + + - name: Install and cache PowerShell modules + uses: potatoqualitee/psmodulecache@v5.2 + with: + modules-to-cache: dbatools.library:2023.5.5 + + - name: Set encryption values + run: | + Import-Module ./dbatools.psd1 -Force + Set-DbatoolsConfig -FullName sql.connection.trustcert -Value $true -Register + Set-DbatoolsConfig -FullName sql.connection.encrypt -Value Optional -Register + Get-DbatoolsConfigValue -FullName sql.connection.encrypt | Write-Warning + + - name: Setup docker images + run: | + # create a shared network + docker network create localnet + # Expose engine and endpoint then setup a shared path for migrations + docker run -p 1433:1433 --volume shared:/shared:z --name mssql1 --hostname mssql1 --network localnet -d dbatools/sqlinstance + # Expose second engine and endpoint on different port + docker run -p 14333:1433 --volume shared:/shared:z --name mssql2 --hostname mssql2 --network localnet -d dbatools/sqlinstance2 + + - name: Add hostname to hosts file + run: | + echo "127.0.0.1 mssql1 mssql2" | sudo tee -a /etc/hosts + + - name: 👥 Clone appveyor repo + working-directory: /tmp + run: | + gh repo clone dataplat/appveyor-lab + + - name: Setup Replication + run: | + Import-Module ./dbatools.psd1 -Force + # need some folders for our repl stuff + docker exec mssql1 mkdir /shared/data /shared/repldata /var/opt/mssql/ReplData + + - name: Run replication tests part 3 + run: | + Import-Module ./dbatools.psd1 -Force + Invoke-Pester ./tests/gh-actions-repl-3.ps1 -Output Detailed -PassThru -Verbose diff --git a/.vscode/settings.json b/.vscode/settings.json index 7744d49149..c44f3d5962 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -57,6 +57,7 @@ "Kokkinos", "Kravtsov", "Mikey", + "mssql", "nchar", "niphlod", "ntext", diff --git a/ReplicationDemo.ps1 b/ReplicationDemo.ps1 new file mode 100644 index 0000000000..4205c4445f --- /dev/null +++ b/ReplicationDemo.ps1 @@ -0,0 +1,99 @@ +# dbatools 💜 dbatools + +############################## +# create docker environment +############################## +# create a shared network +docker network create localnet + +# Expose engines and setup shared path for migrations +docker run -p 2500:1433 --volume shared:/shared:z --name mssql1 --hostname mssql1 --network localnet -d dbatools/sqlinstance +docker run -p 2600:1433 --volume shared:/shared:z --name mssql2 --hostname mssql2 --network localnet -d dbatools/sqlinstance2 + +# create the repl folder +docker exec mssql1 mkdir /var/opt/mssql/ReplData + +# also need these folders for setting up replication +docker exec mssql1 mkdir /shared/data /shared/repldata + +############################## + +# import out version of the module +cd C:\GitHub\DMM-GitHub\dbatools +Import-Module .\dbatools.psd1 + +# lets save the password for connecting to containers because I'm lazy +$securePassword = ('dbatools.IO' | ConvertTo-SecureString -AsPlainText -Force) +$credential = New-Object System.Management.Automation.PSCredential('sqladmin', $securePassword) + +$PSDefaultParameterValues = @{ + "*:SqlCredential" = $credential + "*:DestinationCredential" = $credential + "*:DestinationSqlCredential" = $credential + "*:SourceSqlCredential" = $credential + "*:PublisherSqlCredential" = $credential +} + +# what do we have so far +Get-DbaReplServer -SqlInstance mssql1 +Get-DbaReplDistributor -SqlInstance mssql1 +Get-DbaReplPublisher -SqlInstance mssql1 + +# enable distribution +Enable-DbaReplDistributor -SqlInstance mssql1 + +# enable publishing +Enable-DbaReplPublishing -SqlInstance mssql1 + +# create a transactional publication using splat format +$pub = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'testPub' + Type = 'Transactional' +} +New-DbaReplPublication @pub + +# add an article to the publication +$article = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'testpub' + Name = 'authors' +} +Add-DbaReplArticle @article + +# create a pubs database on mssql2 to replicate to +New-DbaDatabase -SqlInstance mssql2 -Name pubs + +# if you don't the New-DbaReplSubscription command will create the database for you + +# add a subscription to the publication +$sub = @{ + SqlInstance = 'mssql2' + Database = 'pubs' + PublicationDatabase = 'pubs' + PublisherSqlInstance = 'mssql1' + PublicationName = 'testpub' + Type = 'Push' + SubscriptionSqlCredential = $credential + +} +New-DbaReplSubscription @sub + +# creates the snapshot job with a daily schedule at 8am - is that expected? good default? +# should adding a subscription kick off snapshot? should that be an param -StartSnapshotNow -- yes + # create that without a schedule by default maybe a param for a schedule + # + +# stats on the subscription - in the distribution database + # could we make a command to get stats + + + + + + ## when adding an article - we need the options + # - action if name is in use 'drop existing object and create new' + # copy nonclusterd indexes + # nuno diff --git a/dbatools.psd1 b/dbatools.psd1 index dabc275dd7..6bc77af190 100644 --- a/dbatools.psd1 +++ b/dbatools.psd1 @@ -139,7 +139,7 @@ 'Export-DbaLogin', 'Export-DbaPfDataCollectorSetTemplate', 'Export-DbaRegServer', - 'Export-DbaRepServerSetting', + 'Export-DbaReplServerSetting', 'Export-DbaScript', 'Export-DbaServerRole', 'Export-DbaSpConfigure', @@ -350,9 +350,10 @@ 'Get-DbaRegServer', 'Get-DbaRegServerGroup', 'Get-DbaRegServerStore', - 'Get-DbaRepDistributor', - 'Get-DbaRepPublication', - 'Get-DbaRepServer', + 'Get-DbaReplDistributor', + 'Get-DbaReplPublication', + 'Get-DbaReplPublisher', + 'Get-DbaReplServer', 'Get-DbaResourceGovernor', 'Get-DbaRgClassifierFunction', 'Get-DbaRgResourcePool', @@ -693,7 +694,7 @@ 'Test-DbaOptimizeForAdHoc', 'Test-DbaPath', 'Test-DbaPowerPlan', - 'Test-DbaRepLatency', + 'Test-DbaReplLatency', 'Test-DbaSpn', 'Test-DbaTempDbConfig', 'Test-DbaWindowsLogin', @@ -728,7 +729,23 @@ 'New-DbaLinkedServerLogin', 'Remove-DbaLinkedServerLogin', 'Remove-DbaCredential', - 'Remove-DbaAgentProxy' + 'Remove-DbaAgentProxy', + + # NEW REPLICATION STUFF + 'Disable-DbaReplDistributor', + 'Enable-DbaReplDistributor', + 'Disable-DbaReplPublishing', + 'Enable-DbaReplPublishing', + 'New-DbaReplPublication', + 'Get-DbaReplArticle', + 'Get-DbaReplArticleColumn', + 'Add-DbaReplArticle', + 'Remove-DbaReplArticle', + 'Remove-DbaReplPublication', + 'New-DbaReplSubscription', + 'Remove-DbaReplSubscription', + 'New-DbaReplCreationScriptOptions', + 'Get-DbaReplSubscription' ) # Cmdlets to export from this module @@ -751,9 +768,20 @@ 'Write-DbaDataTable', 'Get-DbaDbModule', 'Get-DbaBuildReference', - 'Copy-DbaSysDbUserObject' + 'Copy-DbaSysDbUserObject', + + # replication aliases - these existed before the repl overhaul in 2.0+ + 'Get-DbaRepServer', + 'Export-DbaRepServerSetting', + 'Get-DbaRepDistributor', + 'Test-DbaRepLatency', + 'Get-DbaRepDistributor', + 'Get-DbaRepPublication', + 'Get-DbaRepServer' + ) + # List of all modules packaged with this module ModuleList = @() diff --git a/dbatools.psm1 b/dbatools.psm1 index 55fd649f59..da9f7d41d4 100644 --- a/dbatools.psm1 +++ b/dbatools.psm1 @@ -295,6 +295,18 @@ $forever = @{ foreach ($_ in $forever.GetEnumerator()) { Set-Alias -Name $_.Key -Value $_.Value } + +# Replication Aliases +$replAliases = @{ + 'Get-DbaRepServer' = 'Get-DbaReplServer' + 'Export-DbaRepServerSetting' = 'Export-DbaReplServerSetting' + 'Get-DbaRepDistributor' = 'Get-DbaReplDistributor' + 'Test-DbaRepLatency' = 'Test-DbaReplLatency' + 'Get-DbaRepPublication' = 'Get-DbaReplPublication' +} +foreach ($_ in $replAliases.GetEnumerator()) { + Set-Alias -Name $_.Key -Value $_.Value +} #endregion Aliases # apparently this is no longer required? :O @@ -846,7 +858,7 @@ if ($PSVersionTable.PSVersion.Major -lt 5) { ) $script:noncoresmo = @( # SMO issues - 'Get-DbaRepDistributor', + 'Get-DbaReplDistributor', 'Copy-DbaPolicyManagement', 'Copy-DbaDataCollector', 'Get-DbaPbmCategory', @@ -855,10 +867,10 @@ if ($PSVersionTable.PSVersion.Major -lt 5) { 'Get-DbaPbmObjectSet', 'Get-DbaPbmPolicy', 'Get-DbaPbmStore', - 'Get-DbaRepPublication', - 'Test-DbaRepLatency', - 'Export-DbaRepServerSetting', - 'Get-DbaRepServer' + 'Get-DbaReplPublication', + 'Test-DbaReplLatency', + 'Export-DbaReplServerSetting', + 'Get-DbaReplServer' ) $script:windowsonly = @( # filesystem (\\ related), diff --git a/public/Add-DbaReplArticle.ps1 b/public/Add-DbaReplArticle.ps1 new file mode 100644 index 0000000000..3fb0ac4ac6 --- /dev/null +++ b/public/Add-DbaReplArticle.ps1 @@ -0,0 +1,196 @@ +function Add-DbaReplArticle { + <# + .SYNOPSIS + Add an article configuration to a publication in a database on the SQL Server instance(s). + + .DESCRIPTION + Add an article configuration to a publication in a database on the SQL Server instance(s). + + .PARAMETER SqlInstance + The SQL Server instance(s) for the publication. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Database + The publication database to apply the article configuration to be replicated. + + .PARAMETER Publication + The name of the publication. + + .PARAMETER Schema + Schema where the article to be added is found. + Default is dbo. + + .PARAMETER Name + The name of the object to add as an article. + + .PARAMETER Filter + Sets the where clause used to filter the article horizontally, e.g., DiscontinuedDate IS NULL + E.g. City = 'Seattle' + + .PARAMETER CreationScriptOptions + Options for the creation script. + Use New-DbaReplCreationScriptOptions to create this 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. + + .PARAMETER WhatIf + If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the 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: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + https://learn.microsoft.com/en-us/sql/relational-databases/replication/publish/define-an-article?view=sql-server-ver16#RMOProcedure + + .LINK + https://dbatools.io/Add-DbaReplArticle + + .EXAMPLE + PS C:\> Add-DbaReplArticle -SqlInstance mssql1 -Database Northwind -Publication PubFromPosh -Name TableToRepl + + Adds the TableToRepl table to the PubFromPosh publication from mssql1.Northwind + + .EXAMPLE + PS C:\> $article = @{ + SqlInstance = "mssql1" + Database = "pubs" + Publication = "testPub" + Name = "publishers" + Filter = "city = 'seattle'" + } + PS C:\> Add-DbaReplArticle @article -EnableException + + Adds the publishers table to the TestPub publication from mssql1.Pubs with a horizontal filter of only rows where city = 'seattle. + + .EXAMPLE + PS C:\> $cso = New-DbaReplCreationScriptOptions -Options NonClusteredIndexes, Statistics + PS C:\> $article = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + Publication = 'testPub' + Name = 'stores' + CreationScriptOptions = $cso + } + PS C:\> Add-DbaReplArticle @article -EnableException + + Adds the stores table to the testPub publication from mssql1.pubs with the NonClusteredIndexes and Statistics options set + includes default options. + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [parameter(Mandatory)] + [string]$Database, + [Parameter(Mandatory)] + [string]$Publication, + [string]$Schema = 'dbo', + [Parameter(Mandatory)] + [string]$Name, + [string]$Filter, + [PSObject]$CreationScriptOptions, + [switch]$EnableException + ) + process { + + # Check that $CreationScriptOptions is a valid object + if ($CreationScriptOptions -and ($CreationScriptOptions -isnot [Microsoft.SqlServer.Replication.CreationScriptOptions])) { + Stop-Function -Message "CreationScriptOptions should be the right type. Use New-DbaReplCreationScriptOptions to create the object" -ErrorRecord $_ -Target $instance -Continue + } + + if ($Filter -like 'WHERE*') { + Stop-Function -Message "Filter should not include the word 'WHERE'" -ErrorRecord $_ -Target $instance -Continue + } + + foreach ($instance in $SqlInstance) { + try { + $replServer = Get-DbaReplServer -SqlInstance $instance -SqlCredential $SqlCredential -EnableException:$EnableException + } catch { + Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue + } + Write-Message -Level Verbose -Message "Adding article $Name to publication $Publication on $instance" + + try { + if ($PSCmdlet.ShouldProcess($instance, "Get the publication details for $Publication")) { + + $pub = Get-DbaReplPublication -SqlInstance $instance -SqlCredential $SqlCredential -Name $Publication -EnableException:$EnableException + if (-not $pub) { + Stop-Function -Message "Publication $Publication does not exist on $instance" -ErrorRecord $_ -Target $instance -Continue + } + } + } catch { + Stop-Function -Message "Unable to get publication $Publication on $instance" -ErrorRecord $_ -Target $instance -Continue + } + + try { + if ($PSCmdlet.ShouldProcess($instance, "Create an article object for $Publication which is a $($pub.Type) publication")) { + + $articleOptions = New-Object Microsoft.SqlServer.Replication.ArticleOptions + + if ($pub.Type -in ('Transactional', 'Snapshot')) { + $article = New-Object Microsoft.SqlServer.Replication.TransArticle + $article.Type = $ArticleOptions::LogBased + } elseif ($pub.Type -eq 'Merge') { + $article = New-Object Microsoft.SqlServer.Replication.MergeArticle + $article.Type = $ArticleOptions::TableBased + } + + $article.ConnectionContext = $replServer.ConnectionContext + $article.Name = $Name + $article.DatabaseName = $Database + $article.SourceObjectName = $Name + $article.SourceObjectOwner = $Schema + $article.PublicationName = $Publication + } + } catch { + Stop-Function -Message "Unable to create article object for $Name to add to $Publication on $instance" -ErrorRecord $_ -Target $instance -Continue + } + + try { + if ($CreationScriptOptions) { + if ($PSCmdlet.ShouldProcess($instance, "Add creation options for article: $Name")) { + $article.SchemaOption = $CreationScriptOptions + } + } + + if ($Filter) { + if ($PSCmdlet.ShouldProcess($instance, "Add filter for article: $Name")) { + $article.FilterClause = $Filter + } + } + + if ($PSCmdlet.ShouldProcess($instance, "Create article: $Name")) { + if (-not ($article.IsExistingObject)) { + $article.Create() + } else { + Stop-Function -Message "Article already exists in $Publication on $instance" -ErrorRecord $_ -Target $instance -Continue + } + + if ($pub.Type -in ('Transactional', 'Snapshot')) { + $pub.RefreshSubscriptions() + } + } + } catch { + Stop-Function -Message "Unable to add article $Name to $Publication on $instance" -ErrorRecord $_ -Target $instance -Continue + } + Get-DbaReplArticle -SqlInstance $instance -SqlCredential $SqlCredential -Publication $Publication -Name $Name -EnableException:$EnableException + } + } +} \ No newline at end of file diff --git a/public/Disable-DbaReplDistributor.ps1 b/public/Disable-DbaReplDistributor.ps1 new file mode 100644 index 0000000000..f7a4e3bd4a --- /dev/null +++ b/public/Disable-DbaReplDistributor.ps1 @@ -0,0 +1,95 @@ +function Disable-DbaReplDistributor { + <# + .SYNOPSIS + Disables replication distribution for the target SQL instances. + + .DESCRIPTION + Disables replication distribution for the target SQL instances. + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Force + Specify whether or not replication objects are removed from the server, even if a remote Distributor cannot be reached. + + If true, the publishing and Distributor configuration at the current server is uninstalled regardless of whether or not dependent publishing and distribution objects are uninstalled. + + If false, the publisher and distribution databases must already be uninstalled, and no local databases are enabled for publishing. + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/Disable-DbaReplDistributor + + .EXAMPLE + PS C:\> Disable-DbaReplDistributor -SqlInstance mssql1 + + Disables replication distribution for the mssql1 instance. + + .EXAMPLE + PS C:\> $cred = Get-Credential sqladmin + PS C:\> Disable-DbaReplDistributor -SqlInstance mssql1, mssql2 -SqlCredential $cred -Force + + Disables replication distribution for the mssql1 and mssql2 instances using a sql login. Specifies force so the publishing and Distributor configuration at the current server is uninstalled regardless of whether or not dependent publishing and distribution objects are uninstalled. + + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [switch]$Force, + [switch]$EnableException + ) + process { + foreach ($instance in $SqlInstance) { + + $replServer = Get-DbaReplServer -SqlInstance $instance -SqlCredential $SqlCredential -EnableException:$EnableException + + Write-Message -Level Verbose -Message "Disabling and removing replication distribution for $instance" + + if ($replServer.IsDistributor) { + try { + if ($PSCmdlet.ShouldProcess($instance, "Disabling and removing distribution on $instance")) { + # remove any connections to the distribution database + $null = Get-DbaProcess -SqlInstance $instance -SqlCredential $SqlCredential -Database $replServer.DistributionDatabases.name -EnableException:$EnableException | Stop-DbaProcess -EnableException:$EnableException + # uninstall distribution + $replServer.UninstallDistributor($Force) + } + } catch { + Stop-Function -Message "Unable to disable replication distribution" -ErrorRecord $_ -Target $instance -Continue + } + + $replServer.Refresh() + $replServer + + } else { + Stop-Function -Message "$instance isn't currently enabled for distributing." -Target $instance -Continue + } + } + } +} \ No newline at end of file diff --git a/public/Disable-DbaReplPublishing.ps1 b/public/Disable-DbaReplPublishing.ps1 new file mode 100644 index 0000000000..1970881893 --- /dev/null +++ b/public/Disable-DbaReplPublishing.ps1 @@ -0,0 +1,92 @@ +function Disable-DbaReplPublishing { + <# + .SYNOPSIS + Disables publishing for the target SQL instances. + + .DESCRIPTION + Disables publishing for the target SQL instances. + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Force + Specifies whether the Publisher is uninstalled from the Distributor without verifying that Publisher has also uninstalled the Distributor, if the Publisher is on a separate server. + If true, all the replication objects associated with the Publisher are dropped even if the Publisher is on a remote server that cannot be reached. + If false, replication first verifies that the remote Publisher has uninstalled the Distributor. + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/Disable-DbaReplPublishing + + .EXAMPLE + PS C:\> Disable-DbaReplPublishing -SqlInstance mssql1 + + Disables replication distribution for the mssql1 instance. + + .EXAMPLE + PS C:\> $cred = Get-Credential sqladmin + PS C:\> Disable-DbaReplPublishing -SqlInstance mssql1, mssql2 -SqlCredential $cred -Force + + Disables replication distribution for the mssql1 and mssql2 instances using a sql login. + + Specifies force so all the replication objects associated with the Publisher are dropped even + if the Publisher is on a remote server that cannot be reached. + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [switch]$Force, + [switch]$EnableException + ) + process { + foreach ($instance in $SqlInstance) { + + $replServer = Get-DbaReplServer -SqlInstance $instance -SqlCredential $SqlCredential -EnableException:$EnableException + + Write-Message -Level Verbose -Message "Disabling and removing publishing for $instance" + + if ($replServer.IsPublisher) { + try { + if ($PSCmdlet.ShouldProcess($instance, "Disabling and removing publishing on $instance")) { + $replServer.DistributionPublishers.Remove($Force) + } + + $replServer.Refresh() + $replServer + + } catch { + Stop-Function -Message "Unable to disable replication publishing" -ErrorRecord $_ -Target $instance -Continue + } + } else { + Stop-Function -Message "$instance isn't currently enabled for publishing." -Continue -ContinueLabel main -Target $instance -Category ObjectNotFound + } + } + } +} \ No newline at end of file diff --git a/public/Enable-DbaReplDistributor.ps1 b/public/Enable-DbaReplDistributor.ps1 new file mode 100644 index 0000000000..902d3abbf8 --- /dev/null +++ b/public/Enable-DbaReplDistributor.ps1 @@ -0,0 +1,89 @@ +function Enable-DbaReplDistributor { + <# + .SYNOPSIS + Enables replication distribution for the target SQL instances. + + .DESCRIPTION + Enables replication distribution for the target SQL instances. + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER DistributionDatabase + Name of the distribution database that will be created. + + Default is 'distribution'. + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/Enable-DbaReplDistributor + + .EXAMPLE + PS C:\> Enable-DbaReplDistributor -SqlInstance mssql1 + + Enables distribution for the mssql1 instance. + + .EXAMPLE + PS C:\> Enable-DbaReplDistributor -SqlInstance mssql1 -DistributionDatabase repDatabase + + Enables distribution for the mssql1 instance and names the distribution database repDatabase. + + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [string]$DistributionDatabase = 'distribution', + [switch]$EnableException + ) + process { + foreach ($instance in $SqlInstance) { + + $replServer = Get-DbaReplServer -SqlInstance $instance -SqlCredential $SqlCredential -EnableException:$EnableException + + Write-Message -Level Verbose -Message "Enabling replication distribution for $instance" + + try { + if ($PSCmdlet.ShouldProcess($instance, "Enabling distributor for $instance")) { + $distributionDb = New-Object Microsoft.SqlServer.Replication.DistributionDatabase + $distributionDb.ConnectionContext = $replServer.ConnectionContext + $distributionDb.Name = $DistributionDatabase + + #TODO: lots more properties to add as params + $replServer.InstallDistributor($null, $distributionDb) + + $replServer.Refresh() + $replServer + } + } catch { + Stop-Function -Message "Unable to enable replication distributor" -ErrorRecord $_ -Target $instance -Continue + } + } + } +} \ No newline at end of file diff --git a/public/Enable-DbaReplPublishing.ps1 b/public/Enable-DbaReplPublishing.ps1 new file mode 100644 index 0000000000..5995d335d5 --- /dev/null +++ b/public/Enable-DbaReplPublishing.ps1 @@ -0,0 +1,119 @@ +function Enable-DbaReplPublishing { + <# + .SYNOPSIS + Enables replication publishing for the target SQL instances. + + .DESCRIPTION + Enables replication publishing for the target SQL instances. + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER SnapshotShare + The share used to access snapshot files. + + The default is the ReplData folder within the InstallDataDirectory for the instance. + + .PARAMETER PublisherSqlLogin + If this is used the PublisherSecurity will be set to use this. + If not specified WindowsAuthentication will be used - this is the default, and recommended method. + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/Enable-DbaReplPublishing + + .EXAMPLE + PS C:\> Enable-DbaReplPublishing -SqlInstance SqlBox1\Instance2 -StartupProcedure '[dbo].[StartUpProc1]' + + Attempts to set the procedure '[dbo].[StartUpProc1]' in the master database of SqlBox1\Instance2 for automatic execution when the instance is started. + + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [string]$SnapshotShare, + [PSCredential]$PublisherSqlLogin, + [switch]$EnableException + ) + process { + foreach ($instance in $SqlInstance) { + + $replServer = Get-DbaReplServer -SqlInstance $instance -SqlCredential $SqlCredential -EnableException:$EnableException + + Write-Message -Level Verbose -Message "Enabling replication publishing for $instance" + + if ($replServer.IsDistributor) { + try { + if ($PSCmdlet.ShouldProcess($instance, "Getting distribution information on $instance")) { + + $distPublisher = New-Object Microsoft.SqlServer.Replication.DistributionPublisher + $distPublisher.ConnectionContext = $replServer.ConnectionContext + $distPublisher.Name = $instance + $distPublisher.DistributionDatabase = $replServer.DistributionDatabases.Name + + if (Test-Bound SnapshotShare -Not) { + $SnapshotShare = Join-Path (Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential).InstallDataDirectory 'ReplData' + Write-Message -Level Verbose -Message ('No snapshot share specified, using default of {0}' -f $SnapshotShare) + } + + $distPublisher.WorkingDirectory = $SnapshotShare + } + + if ($PSCmdlet.ShouldProcess($instance, "Configuring PublisherSecurity on $instance")) { + if ($PublisherSqlLogin) { + Write-Message -Level Verbose -Message "Configuring with a SQLLogin for PublisherSecurity" + $distPublisher.PublisherSecurity.WindowsAuthentication = $false + $distPublisher.PublisherSecurity.SqlStandardLogin = $PublisherSqlLogin.UserName + $distPublisher.PublisherSecurity.SecureSqlStandardPassword = $PublisherSqlLogin.Password + + } else { + Write-Message -Level Verbose -Message "Configuring with WindowsAuth for PublisherSecurity" + $distPublisher.PublisherSecurity.WindowsAuthentication = $true + } + } + + if ($PSCmdlet.ShouldProcess($instance, "Enable publishing on $instance")) { + Write-Message -Level Debug -Message $distPublisher + # lots more properties to add as params + $distPublisher.Create() + + $replServer.Refresh() + $replServer + } + + } catch { + Stop-Function -Message "Unable to enable replication publishing" -ErrorRecord $_ -Target $instance -Continue + } + } else { + Stop-Function -Message "$instance isn't currently enabled for distributing. Please enable that first." -ErrorRecord $_ -Target $instance -Continue + } + } + } +} \ No newline at end of file diff --git a/public/Export-DbaInstance.ps1 b/public/Export-DbaInstance.ps1 index 625e272ffc..b5229e3431 100644 --- a/public/Export-DbaInstance.ps1 +++ b/public/Export-DbaInstance.ps1 @@ -385,7 +385,7 @@ function Export-DbaInstance { Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting replication settings" try { - $null = Export-DbaRepServerSetting -SqlInstance $instance -SqlCredential $SqlCredential -FilePath "$exportPath\replication.sql" -EnableException + $null = Export-DbaReplServerSetting -SqlInstance $instance -SqlCredential $SqlCredential -FilePath "$exportPath\replication.sql" -EnableException Get-ChildItem -ErrorAction Ignore -Path "$exportPath\replication.sql" } catch { Write-Message -Level Verbose -Message "Replication failed, skipping" diff --git a/public/Export-DbaRepServerSetting.ps1 b/public/Export-DbaReplServerSetting.ps1 similarity index 90% rename from public/Export-DbaRepServerSetting.ps1 rename to public/Export-DbaReplServerSetting.ps1 index 7ab3ecc52d..77f59ca3f3 100644 --- a/public/Export-DbaRepServerSetting.ps1 +++ b/public/Export-DbaReplServerSetting.ps1 @@ -1,4 +1,4 @@ -function Export-DbaRepServerSetting { +function Export-DbaReplServerSetting { <# .SYNOPSIS Exports replication server settings to file. @@ -51,7 +51,7 @@ function Export-DbaRepServerSetting { Not real sure how to use this yet .PARAMETER InputObject - Allows piping from Get-DbaRepServer + Allows piping from Get-DbaReplServer .PARAMETER EnableException By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. @@ -59,7 +59,7 @@ function Export-DbaRepServerSetting { Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch. .NOTES - Tags: Replication + Tags: Replication, Repl Author: Chrissy LeMaire (@cl), netnerds.net Website: https://dbatools.io @@ -67,15 +67,15 @@ function Export-DbaRepServerSetting { License: MIT https://opensource.org/licenses/MIT .LINK - https://dbatools.io/Export-DbaRepServerSetting + https://dbatools.io/Export-DbaReplServerSetting .EXAMPLE - PS C:\> Export-DbaRepServerSetting -SqlInstance sql2017 -Path C:\temp\replication.sql + PS C:\> Export-DbaReplServerSetting -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 + PS C:\> Get-DbaReplServer -SqlInstance sql2017 | Export-DbaReplServerSetting -Path C:\temp\replication.sql Exports the replication settings on sql2017 to the file C:\temp\replication.sql @@ -104,7 +104,7 @@ function Export-DbaRepServerSetting { process { if (Test-FunctionInterrupt) { return } foreach ($instance in $SqlInstance) { - $InputObject += Get-DbaRepServer -SqlInstance $instance -SqlCredential $SqlCredential + $InputObject += Get-DbaReplServer -SqlInstance $instance -SqlCredential $SqlCredential -EnableException:$EnableException } foreach ($repserver in $InputObject) { diff --git a/public/Get-DbaRepPublication.ps1 b/public/Get-DbaRepPublication.ps1 deleted file mode 100644 index 8a04b05640..0000000000 --- a/public/Get-DbaRepPublication.ps1 +++ /dev/null @@ -1,120 +0,0 @@ -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. - - All replication commands need SQL Server Management Studio installed and are therefore currently not supported. - Have a look at this issue to get more information: https://github.com/dataplat/dbatools/issues/7428 - - .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. Accepts PowerShell credentials (Get-Credential). - - Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. - - For MFA support, please use Connect-DbaInstance. - - .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 - ) - begin { - Add-ReplicationLibrary - } - process { - if (Test-FunctionInterrupt) { return } - foreach ($instance in $SqlInstance) { - - # Connect to Publisher - try { - $server = Connect-DbaInstance -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 - } - } - } - } - } -} \ No newline at end of file diff --git a/public/Get-DbaReplArticle.ps1 b/public/Get-DbaReplArticle.ps1 new file mode 100644 index 0000000000..21da21b046 --- /dev/null +++ b/public/Get-DbaReplArticle.ps1 @@ -0,0 +1,140 @@ +function Get-DbaReplArticle { + <# + .SYNOPSIS + Gets the information about publication articles. + + .DESCRIPTION + This function locates and enumerates articles' information. + + Can specify a database, publication or article name. + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Database + Specifies one or more database(s) to process. If unspecified, all databases will be processed. + + .PARAMETER Publication + Specifies one or more publication(s) to process. If unspecified, all publications will be processed. + + .PARAMETER Schema + Specifies one or more schema(s) to process. If unspecified, all schemas will be processed. + + .PARAMETER Name + Specify the name of one or more article(s) to process. If unspecified, all articles 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: repl, Replication + Author: Cláudio Silva (@claudioessilva), claudioessilva.eu + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/Get-DbaReplArticle + + .EXAMPLE + PS C:\> Get-DbaReplArticle -SqlInstance mssql1 + + Retrieve information of all articles from all publications on all databases for server mssql1. + + .EXAMPLE + PS C:\> Get-DbaReplArticle -SqlInstance mssql1 -Database pubs + + Retrieve information of all articles from all publications on 'pubs' database for server mssql1. + + .EXAMPLE + PS C:\> Get-DbaReplArticle -SqlInstance mssql1 -Database pubs -Publication PubName + + Retrieve information of all articles from 'PubName' on 'pubs' database for server mssql1. + + .EXAMPLE + PS C:\> Get-DbaReplArticle -SqlInstance mssql1 -Database pubs -Schema sales + + Retrieve information of articles in the 'sales' schema on 'pubs' database for server mssql1. + + .EXAMPLE + PS C:\> Get-DbaReplArticle -SqlInstance mssql1 -Database pubs -Publication PubName -Name sales + + Retrieve information of 'sales' article from 'PubName' on 'pubs' database for server mssql1. + + #> + [CmdletBinding()] + param ( + [parameter(ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [object[]]$Database, + [object[]]$Publication, + [string[]]$Schema, + [string[]]$Name, + [switch]$EnableException + ) + begin { + Add-ReplicationLibrary + } + process { + if (Test-FunctionInterrupt) { return } + + foreach ($instance in $SqlInstance) { + try { + $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential + } catch { + Stop-Function -Message "Error occurred while establishing connection to $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue + } + + try { + $databases = $server.Databases | Where-Object IsAccessible -eq $true + if ($Database) { + $databases = $databases | Where-Object Name -in $Database + } + } catch { + Stop-Function -Message "Error occurred while getting databases from $instance" -ErrorRecord $_ -Target $instance -Continue + } + + try { + foreach ($db in $databases) { + Write-Message -Level Verbose -Message ('Working on {0}' -f $db.Name) + + $publications = Get-DbaReplPublication -SqlInstance $server -Database $db.Name -EnableException:$EnableException + + if ($Publication) { + $publications = $publications | Where-Object Name -in $Publication + } + + $articles = $publications.Articles + + if ($Schema) { + $articles = $articles | Where-Object SourceObjectOwner -in $Schema + } + if ($Name) { + $articles = $articles | Where-Object Name -in $Name + } + + foreach ($art in $articles) { + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name SqlInstance -Value $server + + Select-DefaultView -InputObject $art -Property ComputerName, InstanceName, SqlInstance, DatabaseName, PublicationName, Name, Type, VerticalPartition, SourceObjectOwner, SourceObjectName #, DestinationObjectOwner, DestinationObjectName + } + } + } catch { + Stop-Function -Message "Error occurred while getting articles from $instance" -ErrorRecord $_ -Target $instance -Continue + } + } + } +} \ No newline at end of file diff --git a/public/Get-DbaReplArticleColumn.ps1 b/public/Get-DbaReplArticleColumn.ps1 new file mode 100644 index 0000000000..9f2814ee6e --- /dev/null +++ b/public/Get-DbaReplArticleColumn.ps1 @@ -0,0 +1,122 @@ +function Get-DbaReplArticleColumn { + <# + .SYNOPSIS + Gets the information about replicated article columns. + + .DESCRIPTION + This function enumerates column information for given articles. + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Database + Specifies one or more database(s) to process. If unspecified, all databases will be processed. + + .PARAMETER Publication + Specifies one or more publication(s) to process. If unspecified, all publications will be processed. + + .PARAMETER Article + Specifies one or more article(s) to process. If unspecified, all articles will be processed. + + .PARAMETER Column + Specifies one or more column(s) to process. If unspecified, all columns 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: repl, Replication + Author: Cláudio Silva (@claudioessilva), claudioessilva.eu + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/Get-DbaReplArticleColumn + + .EXAMPLE + PS C:\> Get-DbaReplArticleColumn -SqlInstance sqlserver2019 + + Retrieve information of all replicated columns in any publications on server sqlserver2019. + + .EXAMPLE + PS C:\> Get-DbaReplArticleColumn -SqlInstance sqlserver2019 -Database pubs + + Retrieve information of all replicated columns in any publications from the pubs database on server sqlserver2019. + + .EXAMPLE + PS C:\> Get-DbaReplArticleColumn -SqlInstance sqlserver2019 -Publication test + + Retrieve information of all replicated columns in the test publication on server sqlserver2019. + + .EXAMPLE + PS C:\> Get-DbaReplArticleColumn -SqlInstance sqlserver2019 -Database pubs -Publication PubName -Article sales + + Retrieve information of 'sales' article from 'PubName' on 'pubs' database for server sqlserver2019. + + .EXAMPLE + PS C:\> Get-DbaReplArticleColumn -SqlInstance sqlserver2019 -Column state + + Retrieve information for the state column in any publication from any database on server sqlserver2019. + + #> + [CmdletBinding()] + param ( + [parameter(ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [object[]]$Database, + [parameter(ValueFromPipeline)] + [object[]]$Publication, + [string[]]$Article, + [string[]]$Column, + [switch]$EnableException + ) + process { + if (Test-FunctionInterrupt) { return } + + $articles = Get-DbaReplArticle -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Database $Database -Publication $Publication -Name $Article -EnableException:$EnableException + + foreach ($art in $articles) { + try { + + $columns = $art.ListReplicatedColumns() + + if ($Column) { + $columns = $columns | Where-Object { $_ -In $Column } + } + + foreach ($col in $columns) { + + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name ComputerName -Value $art.ComputerName + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name InstanceName -Value $art.InstanceName + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name SqlInstance -Value $art.SqlInstance + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name DatabaseName -Value $art.DatabaseName + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name PublicationName -Value $art.PublicationName + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name ArticleName -Value $art.Name + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name ArticleId -Value $art.ArticleId + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name Description -Value $art.Description + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name Type -Value $art.Type + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name VerticalPartition -Value $art.VerticalPartition + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name SourceObjectOwner -Value $art.SourceObjectOwner + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name SourceObjectName -Value $art.SourceObjectName + Add-Member -Force -InputObject $art -MemberType NoteProperty -Name ColumnName -Value $col + + Select-DefaultView -InputObject $art -Property ComputerName, InstanceName, SqlInstance, DatabaseName, PublicationName, ArticleName, ArticleId, ColumnName #, DestinationObjectOwner, DestinationObjectName + } + } catch { + Stop-Function -Message "Error occurred while getting article columns from $instance" -ErrorRecord $_ -Target $instance -Continue + } + } + } +} \ No newline at end of file diff --git a/public/Get-DbaRepDistributor.ps1 b/public/Get-DbaReplDistributor.ps1 similarity index 63% rename from public/Get-DbaRepDistributor.ps1 rename to public/Get-DbaReplDistributor.ps1 index c5657c688e..16f1fd9a96 100644 --- a/public/Get-DbaRepDistributor.ps1 +++ b/public/Get-DbaReplDistributor.ps1 @@ -1,4 +1,4 @@ -function Get-DbaRepDistributor { +function Get-DbaReplDistributor { <# .SYNOPSIS Gets the information about a replication distributor for a given SQL Server instance. @@ -6,9 +6,6 @@ function Get-DbaRepDistributor { .DESCRIPTION This function locates and enumerates distributor information for a given SQL Server instance. - All replication commands need SQL Server Management Studio installed and are therefore currently not supported. - Have a look at this issue to get more information: https://github.com/dataplat/dbatools/issues/7428 - .PARAMETER SqlInstance The target SQL Server instance or instances. @@ -33,13 +30,17 @@ function Get-DbaRepDistributor { License: MIT https://opensource.org/licenses/MIT .LINK - https://dbatools.io/Get-DbaRepDistributor + https://dbatools.io/Get-DbaReplDistributor .EXAMPLE - PS C:\> Get-DbaRepDistributor -SqlInstance sql2008, sqlserver2012 + PS C:\> Get-DbaReplDistributor -SqlInstance sql2008, sqlserver2012 Retrieve distributor information for servers sql2008 and sqlserver2012. + .EXAMPLE + PS C:\> Connect-DbaInstance -SqlInstance mssql1 | Get-DbaReplDistributor + + Pipe a SQL Server instance to Get-DbaReplDistributor to retrieve distributor information. #> [CmdletBinding()] param ( @@ -48,9 +49,6 @@ function Get-DbaRepDistributor { [PSCredential]$SqlCredential, [switch]$EnableException ) - begin { - Add-ReplicationLibrary - } process { if (Test-FunctionInterrupt) { return } foreach ($instance in $SqlInstance) { @@ -58,17 +56,11 @@ function Get-DbaRepDistributor { # Connect to the distributor of the instance try { - $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential - $sqlconn = New-SqlConnection -SqlInstance $instance -SqlCredential $SqlCredential - - $distributor = New-Object Microsoft.SqlServer.Replication.ReplicationServer $sqlconn + $distributor = Get-DbaReplServer -SqlInstance $instance -SqlCredential $SqlCredential -EnableException:$EnableException } catch { - Stop-Function -Message "Error occurred while establishing connection to $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue + Stop-Function -Message "Error occurred getting information about $instance" -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 + Write-Message -Level Verbose -Message "Getting publisher for $server" Select-DefaultView -InputObject $distributor -Property ComputerName, InstanceName, SqlInstance, IsPublisher, IsDistributor, DistributionServer, DistributionDatabase, DistributorInstalled, DistributorAvailable, HasRemotePublisher } diff --git a/public/Get-DbaReplPublication.ps1 b/public/Get-DbaReplPublication.ps1 new file mode 100644 index 0000000000..434c444a62 --- /dev/null +++ b/public/Get-DbaReplPublication.ps1 @@ -0,0 +1,152 @@ +function Get-DbaReplPublication { + <# + .SYNOPSIS + Displays all publications on a server. + + .DESCRIPTION + Quickly find all transactional, merge, and snapshot publications on a server or filter by database, name or type. + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Database + The database(s) to process. If unspecified, all databases will be processed. + + .PARAMETER Name + The name of the publication. + + .PARAMETER Type + Limit by specific type of publication. Valid choices include: Transactional, Merge, Snapshot. + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. + Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch. + + .NOTES + Tags: repl, 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-DbaReplPublication + + .EXAMPLE + PS C:\> Get-DbaReplPublication -SqlInstance sql2008, sqlserver2012 + + Return all publications for servers sql2008 and sqlserver2012. + + .EXAMPLE + PS C:\> Get-DbaReplPublication -SqlInstance sql2008 -Database TestDB + + Return all publications on server sql2008 for only the TestDB database + + .EXAMPLE + PS C:\> Get-DbaReplPublication -SqlInstance sql2008 -Type Transactional + + Return all transactional publications on server sql2008. + + .EXAMPLE + PS C:\> Get-DbaReplPublication -SqlInstance mssql1 -Name Mergey + + Returns the Mergey publications on server mssql1 + + .EXAMPLE + PS C:\> Connect-DbaInstance -SqlInstance mssql1 | Get-DbaReplPublication + + Returns all publications on server mssql1 using the pipeline. + #> + [CmdletBinding()] + param ( + [parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [object[]]$Database, + [String]$Name, + [Alias("PublicationType")] + [ValidateSet("Transactional", "Merge", "Snapshot")] + [object[]]$Type, + [switch]$EnableException + ) + begin { + Add-ReplicationLibrary + } + process { + if (Test-FunctionInterrupt) { return } + foreach ($instance in $SqlInstance) { + # Connect to Publisher + try { + $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9 + } catch { + Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue + } + + try { + $databases = $server.Databases | Where-Object { $_.IsAccessible -eq $true -and (-not $_.IsSystemObject) } + if ($Database) { + $databases = $databases | Where-Object Name -In $Database + } + } catch { + Stop-Function -Message "Unable to get databases for" -ErrorRecord $_ -Target $server -Continue + } + + try { + foreach ($db in $databases) { + + #test if the database published + if ((($db.ReplicationOptions -band [Microsoft.SqlServer.Management.Smo.ReplicationOptions]::Published) -ne [Microsoft.SqlServer.Management.Smo.ReplicationOptions]::Published) -and + (($db.ReplicationOptions -band [Microsoft.SqlServer.Management.Smo.ReplicationOptions]::MergePublished) -ne [Microsoft.SqlServer.Management.Smo.ReplicationOptions]::MergePublished)) { + # The database is not published + Write-Message -Level Verbose -Message "Skipping $($db.name). Database is not published." + continue + } + + + $repDB = Connect-ReplicationDB -Server $server -Database $db -EnableException:$EnableException + + $pubTypes = $repDB.TransPublications + $repDB.MergePublications + + if ($Type) { + $pubTypes = $pubTypes | Where-Object Type -in $Type + } + + if ($Name) { + $pubTypes = $pubTypes | Where-Object Name -in $Name + } + + foreach ($pub in $pubTypes) { + if ($pub.Type -eq 'Merge') { + $articles = $pub.MergeArticles + $subscriptions = $pub.MergeSubscriptions + } else { + $articles = $pub.TransArticles + $subscriptions = $pub.TransSubscriptions + } + + + Add-Member -Force -InputObject $pub -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName + Add-Member -Force -InputObject $pub -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName + Add-Member -Force -InputObject $pub -MemberType NoteProperty -Name SQLInstance -Value $server + Add-Member -Force -InputObject $pub -MemberType NoteProperty -Name Articles -Value $articles + Add-Member -Force -InputObject $pub -MemberType NoteProperty -Name Subscriptions -Value $subscriptions + + Select-DefaultView -InputObject $pub -Property ComputerName, InstanceName, SQLInstance, DatabaseName, Name, Type, Articles, Subscriptions + + } + } + } catch { + Stop-Function -Message "Unable to get publications from " -ErrorRecord $_ -Target $server -Continue + } + } + } +} \ No newline at end of file diff --git a/public/Get-DbaReplPublisher.ps1 b/public/Get-DbaReplPublisher.ps1 new file mode 100644 index 0000000000..3db1b106af --- /dev/null +++ b/public/Get-DbaReplPublisher.ps1 @@ -0,0 +1,79 @@ +function Get-DbaReplPublisher { + <# + .SYNOPSIS + Gets publisher information for the target SQL instances. + + .DESCRIPTION + Gets publisher information for the target SQL instances. + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. + Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch. + + .NOTES + Tags: repl, Replication + Author: Mikey Bronowski (@MikeyBronowski), bronowski.it + + Website: https://dbatools.io + Copyright: (c) 2022 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/Get-DbaReplPublisher + + .EXAMPLE + PS C:\> Get-DbaReplPublisher -SqlInstance mssql1 + + Gets publisher for the mssql1 instance. + + .EXAMPLE + PS C:\> Connect-DbaInstance -SqlInstance mssql1 | Get-DbaReplPublisher + + Pipes a SQL Server object to get publisher information for the mssql1 instance. + #> + [CmdletBinding()] + param ( + [parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [switch]$EnableException + ) + process { + foreach ($instance in $SqlInstance) { + try { + $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential + $replServer = Get-DbaReplServer -SqlInstance $server -EnableException:$EnableException + } catch { + Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $server -Continue + } + Write-Message -Level Verbose -Message "Getting publisher for $server" + + try { + $publisher = $replServer.DistributionPublishers + } catch { + Stop-Function -Message "Unable to get publisher for" -ErrorRecord $_ -Target $server -Continue + } + + # fails if there isn't any + if ($publisher) { + $publisher | Add-Member -Type NoteProperty -Name ComputerName -Value $server.ComputerName -Force + $publisher | Add-Member -Type NoteProperty -Name InstanceName -Value $server.ServiceName -Force + $publisher | Add-Member -Type NoteProperty -Name SqlInstance -Value $server.DomainInstanceName -Force + } + + Select-DefaultView -InputObject $publisher -Property ComputerName, InstanceName, SqlInstance, Status, WorkingDirectory, DistributionDatabase, DistributionPublications, PublisherType, Name + + } + } +} \ No newline at end of file diff --git a/public/Get-DbaRepServer.ps1 b/public/Get-DbaReplServer.ps1 similarity index 56% rename from public/Get-DbaRepServer.ps1 rename to public/Get-DbaReplServer.ps1 index 9a9d692110..89c6ef0a6d 100644 --- a/public/Get-DbaRepServer.ps1 +++ b/public/Get-DbaReplServer.ps1 @@ -1,13 +1,13 @@ -function Get-DbaRepServer { +function Get-DbaReplServer { <# .SYNOPSIS Gets a replication server object .DESCRIPTION - Gets a replication server object + Gets a replication server object. - All replication commands need SQL Server Management Studio installed and are therefore currently not supported. - Have a look at this issue to get more information: https://github.com/dataplat/dbatools/issues/7428 + Note: The ReplicationDatabases property gets the databases enabled for replication in the connected instance of Microsoft SQL Server/. + Not necessarily the databases that are actually replicated. .PARAMETER SqlInstance The target SQL Server instance or instances @@ -33,15 +33,15 @@ function Get-DbaRepServer { License: MIT https://opensource.org/licenses/MIT .LINK - https://dbatools.io/Get-DbaRepServer + https://dbatools.io/Get-DbaReplServer .EXAMPLE - PS C:\> Get-DbaRepServer -SqlInstance sql2016 + PS C:\> Get-DbaReplServer -SqlInstance sql2016 Gets the replication server object for sql2016 using Windows authentication .EXAMPLE - PS C:\> Get-DbaRepServer -SqlInstance sql2016 -SqlCredential repadmin + PS C:\> Get-DbaReplServer -SqlInstance sql2016 -SqlCredential repadmin Gets the replication server object for sql2016 using SQL authentication @@ -60,11 +60,16 @@ function Get-DbaRepServer { if (Test-FunctionInterrupt) { return } foreach ($instance in $SqlInstance) { try { - # use System.Data instead of Microsoft.Data - $sqlconn = New-SqlConnection -SqlInstance $instance -SqlCredential $SqlCredential - New-Object Microsoft.SqlServer.Replication.ReplicationServer $sqlconn + $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential + $replServer = New-Object Microsoft.SqlServer.Replication.ReplicationServer + $replServer.ConnectionContext = $Server.ConnectionContext.SqlConnectionObject + $replServer | Add-Member -Type NoteProperty -Name ComputerName -Value $server.ComputerName -Force + $replServer | Add-Member -Type NoteProperty -Name InstanceName -Value $server.ServiceName -Force + $replServer | Add-Member -Type NoteProperty -Name SqlInstance -Value $server.DomainInstanceName -Force + + Select-DefaultView -InputObject $replServer -Property ComputerName, InstanceName, SqlInstance, IsDistributor, IsPublisher, DistributionServer, DistributionDatabase } catch { - Stop-Function -Message "Error occurred while establishing connection to $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue + Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue } } } diff --git a/public/Get-DbaReplSubscription.ps1 b/public/Get-DbaReplSubscription.ps1 new file mode 100644 index 0000000000..3169ffe18e --- /dev/null +++ b/public/Get-DbaReplSubscription.ps1 @@ -0,0 +1,149 @@ +function Get-DbaReplSubscription { + <# + .SYNOPSIS + Displays all subscriptions for a publication. + + .DESCRIPTION + Displays all subscriptions for a publication + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Database + The database(s) to process. If unspecified, all databases will be processed. + + .PARAMETER PublicationName + The name of the publication. + + .PARAMETER SubscriberName + The subscriber SQL Server instance name. + + .PARAMETER SubscriptionDatabase + The name of the subscription database. + + .PARAMETER Type + Limit by specific type of publication. Valid choices include: Transactional, Merge, Snapshot + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. + Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch. + + .NOTES + Tags: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/Get-DbaReplSubscription + + .EXAMPLE + PS C:\> Get-DbaReplSubscription -SqlInstance mssql1 + + Return all subscriptions for all publications on server mssql1. + + .EXAMPLE + PS C:\> Get-DbaReplSubscription -SqlInstance mssql1 -Database TestDB + + Return all subscriptions for all publications on server mssql1 for only the TestDB database. + + .EXAMPLE + PS C:\> Get-DbaReplSubscription -SqlInstance mssql1 -PublicationName Mergey + + Return all subscriptions for the publication Mergey on server mssql1. + + .EXAMPLE + PS C:\> Get-DbaReplSubscription -SqlInstance mssql1 -Type Push + + Return all subscriptions for all transactional publications on server mssql1. + + .EXAMPLE + PS C:\> Get-DbaReplSubscription -SqlInstance mssql1 -SubscriberName mssql2 + + Return all subscriptions for all publications on server mssql1 where the subscriber is mssql2. + + .EXAMPLE + PS C:\> Get-DbaReplSubscription -SqlInstance mssql1 -SubscriptionDatabase TestDB + + Return all subscriptions for all publications on server mssql1 where the subscription database is TestDB. + + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [Object[]]$Database, + [String[]]$PublicationName, + [DbaInstanceParameter[]]$SubscriberName, + [Object[]]$SubscriptionDatabase, + [Alias("PublicationType")] + [ValidateSet("Push", "Pull")] + [Object[]]$Type, + [Switch]$EnableException + ) + process { + if (Test-FunctionInterrupt) { return } + + foreach ($instance in $SqlInstance) { + try { + $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential + } catch { + Stop-Function -Message "Error occurred while establishing connection to $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue + } + + try { + $publications = Get-DbaReplPublication -SqlInstance $server -EnableException:$EnableException + + if ($Database) { + $publications = $publications | Where-Object DatabaseName -in $Database + } + + if ($PublicationName) { + $publications = $publications | Where-Object Name -in $PublicationName + } + + } catch { + Stop-Function -Message "Error occurred while getting publications from $instance" -ErrorRecord $_ -Target $instance -Continue + } + + try { + foreach ($subs in $publications.Subscriptions) { + Write-Message -Level Verbose -Message ('Get subscriptions for {0}' -f $sub.PublicationName) + + if ($SubscriberName) { + $subs = $subs | Where-Object SubscriberName -eq $SubscriberName + } + + if ($SubscriptionDatabase) { + $subs = $subs | Where-Object SubscriptionDBName -eq $SubscriptionDatabase + } + + if ($Type) { + $subs = $subs | Where-Object SubscriptionType -eq $Type + } + + foreach ($sub in $subs) { + Add-Member -Force -InputObject $sub -MemberType NoteProperty -Name ComputerName -Value $server.ComputerName + Add-Member -Force -InputObject $sub -MemberType NoteProperty -Name InstanceName -Value $server.ServiceName + Add-Member -Force -InputObject $sub -MemberType NoteProperty -Name SqlInstance -Value $server.DomainInstanceName + + Select-DefaultView -InputObject $sub -Property ComputerName, InstanceName, SqlInstance, DatabaseName, PublicationName, Name, SubscriberName, SubscriptionDBName, SubscriptionType + } + } + } catch { + Stop-Function -Message "Error occurred while getting subscriptions from $instance" -ErrorRecord $_ -Target $instance -Continue + } + } + } +} \ No newline at end of file diff --git a/public/New-DbaReplCreationScriptOptions.ps1 b/public/New-DbaReplCreationScriptOptions.ps1 new file mode 100644 index 0000000000..20edf0629c --- /dev/null +++ b/public/New-DbaReplCreationScriptOptions.ps1 @@ -0,0 +1,80 @@ +function New-DbaReplCreationScriptOptions { + <# + .SYNOPSIS + Creates a new Microsoft.SqlServer.Replication.CreationScriptOptions enumeration object. + + .DESCRIPTION + Creates a new Microsoft.SqlServer.Replication.CreationScriptOptions enumeration object that allows you to specify article options. + + See https://learn.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.replication.creationscriptoptions for more information + + .PARAMETER Options + The options to set on published articles. + See https://docs.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.replication.creationscriptoptions for a list of available options + + .PARAMETER NoDefaults + If specified, no default options will be set on the object + + Defaults are copied from when you add an article in SQL Server Management Studio and include: + PrimaryObject, CustomProcedures, Identity, KeepTimestamp, + ClusteredIndexes, DriPrimaryKey, Collation, DriUniqueKeys, + MarkReplicatedCheckConstraintsAsNotForReplication, + MarkReplicatedForeignKeyConstraintsAsNotForReplication, and Schema + + .NOTES + Tags: repl, Replication, Script + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/New-DbaReplCreationScriptOptions + + .EXAMPLE + PS C:\> $cso = New-DbaReplCreationScriptOptions -Options NonClusteredIndexes, Statistics + PS C:\> $article = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'testPub' + Name = 'stores' + CreationScriptOptions = $cso + } + Add-DbaReplArticle @article -EnableException + + Adds the stores table to the testPub publication from mssql1.pubs with the NonClusteredIndexes and Statistics options set + includes default options. + + + .EXAMPLE + PS C:\> $cso = New-DbaReplCreationScriptOptions -Options ClusteredIndexes, Identity -NoDefaults + PS C:\> $article = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'testPub' + Name = 'stores' + CreationScriptOptions = $cso + } + Add-DbaReplArticle @article -EnableException + + Adds the stores table to the testPub publication from mssql1.pubs with the ClusteredIndexes and Identity options set, excludes default options. + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] + param ( + [String[]]$Options, + [switch]$NoDefaults + ) + + $cso = New-Object Microsoft.SqlServer.Replication.CreationScriptOptions + + if (-not $NoDefaults) { + 'PrimaryObject', 'CustomProcedures', 'Identity', 'KeepTimestamp', 'ClusteredIndexes', 'DriPrimaryKey', 'Collation', 'DriUniqueKeys', 'MarkReplicatedCheckConstraintsAsNotForReplication', 'MarkReplicatedForeignKeyConstraintsAsNotForReplication', 'Schema' | ForEach-Object { $cso += $_ } + } + + foreach ($opt in $options) { + $cso += $opt + } + + $cso +} \ No newline at end of file diff --git a/public/New-DbaReplPublication.ps1 b/public/New-DbaReplPublication.ps1 new file mode 100644 index 0000000000..7c538626fc --- /dev/null +++ b/public/New-DbaReplPublication.ps1 @@ -0,0 +1,200 @@ +function New-DbaReplPublication { + <# + .SYNOPSIS + Creates a publication for the database on the target SQL instances. + + .DESCRIPTION + Creates a publication for the database on the target SQL instances. + + https://learn.microsoft.com/en-us/sql/relational-databases/replication/publish/create-a-publication?view=sql-server-ver16 + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Database + The database that contains the articles to be replicated. + + .PARAMETER Name + The name of the replication publication. + + .PARAMETER Type + The flavour of replication. + Options are Transactional, Snapshot, Merge + + .PARAMETER LogReaderAgentCredential + Used to provide the credentials for the Microsoft Windows account under which the Log Reader Agent runs + + Setting LogReaderAgentProcessSecurity is not required when the publication is created by a member of the sysadmin fixed server role. + In this case, the agent will impersonate the SQL Server Agent account. For more information, see Replication Agent Security Model. + + TODO: test LogReaderAgentCredential parameters + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/New-DbaReplPublication + + .EXAMPLE + PS C:\> New-DbaReplPublication -SqlInstance mssql1 -Database Northwind -Name PubFromPosh -Type Transactional + + Creates a transactional publication called PubFromPosh for the Northwind database on mssql1 + + .EXAMPLE + PS C:\> New-DbaReplPublication -SqlInstance mssql1 -Database pubs -Name snapPub -Type Snapshot + + Creates a snapshot publication called snapPub for the pubs database on mssql1 + + .EXAMPLE + PS C:\> New-DbaReplPublication -SqlInstance mssql1 -Database pubs -Name mergePub -Type Merge + + Creates a merge publication called mergePub for the pubs database on mssql1 + #> + [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess, ConfirmImpact = 'Medium')] + param ( + [parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [parameter(Mandatory)] + [String]$Database, + [parameter(Mandatory)] + [String]$Name, + [parameter(Mandatory)] + [ValidateSet("Snapshot", "Transactional", "Merge")] + [String]$Type, + [PSCredential]$LogReaderAgentCredential, + [Switch]$EnableException + ) + process { + foreach ($instance in $SqlInstance) { + try { + $replServer = Get-DbaReplServer -SqlInstance $instance -SqlCredential $SqlCredential -EnableException:$EnableException + + if (-not $replServer.IsPublisher) { + Stop-Function -Message "Instance $instance is not a publisher, run Enable-DbaReplPublishing to set this up" -Target $instance -Continue + } + + } catch { + Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue + } + Write-Message -Level Verbose -Message "Creating publication on $instance" + + try { + if ($PSCmdlet.ShouldProcess($instance, "Creating publication on $instance")) { + + + + $pubDatabase = New-Object Microsoft.SqlServer.Replication.ReplicationDatabase + $pubDatabase.ConnectionContext = $replServer.ConnectionContext + $pubDatabase.Name = $Database + if (-not $pubDatabase.LoadProperties()) { + throw "Database $Database not found on $instance" + } + + if ($Type -in ('Transactional', 'Snapshot')) { + Write-Message -Level Verbose -Message "Enable trans publishing publication on $instance.$Database" + $pubDatabase.EnabledTransPublishing = $true + $pubDatabase.CommitPropertyChanges() + # log reader agent is only needed for transactional and snapshot replication. + if (-not $pubDatabase.LogReaderAgentExists) { + Write-Message -Level Verbose -Message "Create log reader agent job for $Database on $instance" + if ($LogReaderAgentCredential) { + $pubDatabase.LogReaderAgentProcessSecurity.Login = $LogReaderAgentCredential.UserName + $pubDatabase.LogReaderAgentProcessSecurity.Password = $LogReaderAgentCredential.Password + } + + #(Optional) Set the SqlStandardLogin and SqlStandardPassword or + # SecureSqlStandardPassword fields of LogReaderAgentPublisherSecurity when using SQL Server Authentication to connect to the Publisher. + + $pubDatabase.CreateLogReaderAgent() + } else { + Write-Message -Level Verbose -Message "Log reader agent job already exists for $Database on $instance" + } + + } elseif ($Type -eq 'Merge') { + Write-Message -Level Verbose -Message "Enable merge publishing publication on $instance.$Database" + $pubDatabase.EnabledMergePublishing = $true + $pubDatabase.CommitPropertyChanges() + } + + if ($Type -in ('Transactional', 'Snapshot')) { + + $transPub = New-Object Microsoft.SqlServer.Replication.TransPublication + $transPub.ConnectionContext = $replServer.ConnectionContext + $transPub.DatabaseName = $Database + $transPub.Name = $Name + $transPub.Type = $Type + $transPub.Create() + + # create the Snapshot Agent job + $transPub.CreateSnapshotAgent() + + <# + TODO: add SnapshotGenerationAgentProcessSecurity creds in? + + The Login and Password fields of SnapshotGenerationAgentProcessSecurity to provide the credentials for the Windows account under which the Snapshot Agent runs. + This account is also used when the Snapshot Agent makes connections to the local Distributor and for any remote connections when using Windows Authentication. + + Note + Setting SnapshotGenerationAgentProcessSecurity is not required when the publication is created by a member of the sysadmin fixed server role. + In this case, the agent will impersonate the SQL Server Agent account. For more information, see Replication Agent Security Model. + + (Optional) The SqlStandardLogin and SqlStandardPassword or + SecureSqlStandardPassword fields of SnapshotGenerationAgentPublisherSecurity when using SQL Server Authentication to connect to the Publisher. + #> + } elseif ($Type -eq 'Merge') { + $mergePub = New-Object Microsoft.SqlServer.Replication.MergePublication + $mergePub.ConnectionContext = $replServer.ConnectionContext + $mergePub.DatabaseName = $Database + $mergePub.Name = $Name + $mergePub.Create() + + # create the Snapshot Agent job + $mergePub.CreateSnapshotAgent() + + <# + TODO: add SnapshotGenerationAgentProcessSecurity creds in? + + The Login and Password fields of SnapshotGenerationAgentProcessSecurity to provide the credentials for the Windows account under which the Snapshot Agent runs. + This account is also used when the Snapshot Agent makes connections to the local Distributor and for any remote connections when using Windows Authentication. + + Note + Setting SnapshotGenerationAgentProcessSecurity is not required when the publication is created by a member of the sysadmin fixed server role. + For more information, see Replication Agent Security Model. + + (Optional) Use the inclusive logical OR operator (| in Visual C# and Or in Visual Basic) and the exclusive logical OR operator (^ in Visual C# and Xor in Visual Basic) + to set the PublicationAttributes values for the Attributes property. + + #> + } + } + } catch { + Stop-Function -Message ("Unable to create publication - {0}" -f $_) -ErrorRecord $_ -Target $instance -Continue + } + Get-DbaRepPublication -SqlInstance $instance -SqlCredential $SqlCredential -Database $Database -Name $Name + } + } +} \ No newline at end of file diff --git a/public/New-DbaReplSubscription.ps1 b/public/New-DbaReplSubscription.ps1 new file mode 100644 index 0000000000..37ffff9b25 --- /dev/null +++ b/public/New-DbaReplSubscription.ps1 @@ -0,0 +1,300 @@ +function New-DbaReplSubscription { + <# + .SYNOPSIS + Creates a subscription for the database on the target SQL instances. + + .DESCRIPTION + Creates a subscription for the database on the target SQL instances. + + .PARAMETER SqlInstance + The target publishing SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target publishing instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Database + The database on the publisher that will be replicated. + + .PARAMETER SubscriberSqlInstance + The subscriber SQL instance. + + .PARAMETER SubscriberSqlCredential + Login to the subscriber instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER SubscriptionDatabase + The database on the subscriber that will be the target of the replicated data. + + .PARAMETER PublicationName + The name of the replication publication + + .PARAMETER SubscriptionSqlCredential + Credential object that will be saved as the 'subscriber credential' in the subscription properties. + + .PARAMETER Type + The flavour of the subscription. Push or Pull. + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/New-DbaReplSubscription + + .EXAMPLE + PS C:\> New-DbaReplSubscription -SqlInstance sql2017 -Database pubs -SubscriberSqlInstance sql2019 -SubscriptionDatabase pubs -PublicationName testPub -Type Push + + Creates a push subscription from sql2017 to sql2019 for the pubs database. + + .EXAMPLE + PS C:\> New-DbaReplSubscription -SqlInstance sql2017 -Database pubs -SubscriberSqlInstance sql2019 -SubscriptionDatabase pubs -PublicationName testPub -Type Pull + + Creates a pull subscription from sql2017 to sql2019 for the pubs database. + #> + [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess, ConfirmImpact = 'Medium')] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter]$SqlInstance, + [PSCredential]$SqlCredential, + [String]$Database, + [Parameter(Mandatory)] + [DbaInstanceParameter[]]$SubscriberSqlInstance, + [PSCredential]$SubscriberSqlCredential, + [String]$SubscriptionDatabase, + [Parameter(Mandatory)] + [String]$PublicationName, + [PSCredential] + $SubscriptionSqlCredential, + [Parameter(Mandatory)] + [ValidateSet("Push", "Pull")] + [String]$Type, + [Switch]$EnableException + ) + begin { + Write-Message -Level Verbose -Message "Connecting to publisher: $SqlInstance" + + # connect to publisher and get the publication + try { + $pubReplServer = Get-DbaReplServer -SqlInstance $SqlInstance -SqlCredential $SqlCredential -EnableException:$EnableException + } catch { + Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $SqlInstance -Continue + } + + try { + $pub = Get-DbaReplPublication -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Name $PublicationName -EnableException:$EnableException + } catch { + Stop-Function -Message ("Publication {0} not found on {1}" -f $PublicationName, $SqlInstance) -ErrorRecord $_ -Target $SqlInstance -Continue + } + } + + process { + + # for each subscription SqlInstance we need to create a subscription + foreach ($instance in $SubscriberSqlInstance) { + + try { + $subReplServer = Get-DbaReplServer -SqlInstance $instance -SqlCredential $SubscriberSqlCredential -EnableException:$EnableException + + if (-not (Get-DbaDatabase -SqlInstance $instance -SqlCredential $SubscriberSqlCredential -Database $SubscriptionDatabase -EnableException:$EnableException)) { + + Write-Message -Level Verbose -Message "Subscription database $SubscriptionDatabase not found on $instance - will create it - but you should check the settings!" + + if ($PSCmdlet.ShouldProcess($instance, "Creating subscription database")) { + + $newSubDb = @{ + SqlInstance = $instance + SqlCredential = $SubscriberSqlCredential + Name = $SubscriptionDatabase + EnableException = $EnableException + } + $null = New-DbaDatabase @newSubDb + } + } + } catch { + Stop-Function -Message ("Couldn't create the subscription database {0}.{1}" -f $instance, $SubscriptionDatabase) -ErrorRecord $_ -Target $instance -Continue + } + + try { + Write-Message -Level Verbose -Message "Creating subscription on $instance" + if ($PSCmdlet.ShouldProcess($instance, "Creating subscription on $instance")) { + + # check if needed schemas exist + foreach ($schema in $pub.articles.DestinationObjectOwner) { + if ($schema -ne 'dbo' -and -not (Get-DbaDbSchema -SqlInstance $instance -SqlCredential $SubscriberSqlCredential -Database $SubscriptionDatabase -Schema $schema)) { + Write-Message -Level Verbose -Message "Subscription database $SubscriptionDatabase does not contain the $schema schema on $instance - will create it!" + $null = New-DbaDbSchema -SqlInstance $instance -SqlCredential $SubscriberSqlCredential -Database $SubscriptionDatabase -Schema $schema -EnableException + } + } + + if ($pub.Type -in ('Transactional', 'Snapshot')) { + + $transPub = New-Object Microsoft.SqlServer.Replication.TransPublication + $transPub.ConnectionContext = $pubReplServer.ConnectionContext + $transPub.DatabaseName = $Database + $transPub.Name = $PublicationName + + # if LoadProperties returns then the publication was found + if ( $transPub.LoadProperties() ) { + + if ($type -eq 'Push') { + + # Perform a bitwise logical AND (& in Visual C# and And in Visual Basic) between the Attributes property and AllowPush. + if (($transPub.Attributes -band [Microsoft.SqlServer.Replication.PublicationAttributes]::AllowPush) -ne [Microsoft.SqlServer.Replication.PublicationAttributes]::AllowPush) { + + # # Perform a bitwise logical AND (& in Visual C# and And in Visual Basic) between the Attributes property and AllowPush. + # if ($transPub.Attributes -band 'AllowPush' -eq 'None' ) { + + # If the result is None, set Attributes to the result of a bitwise logical OR (| in Visual C# and Or in Visual Basic) between Attributes and AllowPush. + $transPub.Attributes = $transPub.Attributes -bor 'AllowPush' + + # Then, call CommitPropertyChanges to enable push subscriptions. + $transPub.CommitPropertyChanges() + } + } else { + #TODO: Fix pull subscriptions in New-DbaReplSubscription command - this still creates a PUSH + + # Perform a bitwise logical AND (& in Visual C# and And in Visual Basic) between the Attributes property and AllowPull. + if (($transPub.Attributes -band [Microsoft.SqlServer.Replication.PublicationAttributes]::AllowPull) -ne [Microsoft.SqlServer.Replication.PublicationAttributes]::AllowPull) { + # If the result is None, set Attributes to the result of a bitwise logical OR (| in Visual C# and Or in Visual Basic) between Attributes and AllowPull. + $transPub.Attributes = $transPub.Attributes -bor 'AllowPull' + + # Then, call CommitPropertyChanges to enable pull subscriptions. + $transPub.CommitPropertyChanges() + } + } + + # create the subscription + $transSub = New-Object Microsoft.SqlServer.Replication.TransSubscription + $transSub.ConnectionContext = $pubReplServer.ConnectionContext + $transSub.SubscriptionDBName = $SubscriptionDatabase + $transSub.SubscriberName = $instance + $transSub.DatabaseName = $Database + $transSub.PublicationName = $PublicationName + + #TODO: + + <# + The Login and Password fields of SynchronizationAgentProcessSecurity to provide the credentials for the + Microsoft Windows account under which the Distribution Agent runs at the Distributor. This account is used to make local connections to the Distributor and to make + remote connections by using Windows Authentication. + + Note + Setting SynchronizationAgentProcessSecurity is not required when the subscription is created by a member of the sysadmin fixed server role, but we recommend it. + In this case, the agent will impersonate the SQL Server Agent account. For more information, see Replication Agent security model. + + (Optional) A value of true (the default) for CreateSyncAgentByDefault to create an agent job that is used to synchronize the subscription. + If you specify false, the subscription can only be synchronized programmatically. + + #> + + if ($SubscriptionSqlCredential) { + $transSub.SubscriberSecurity.WindowsAuthentication = $false + $transSub.SubscriberSecurity.SqlStandardLogin = $SubscriptionSqlCredential.UserName + $transSub.SubscriberSecurity.SecureSqlStandardPassword = $SubscriptionSqlCredential.Password + } + + $transSub.Create() + } else { + Stop-Function -Message ("Publication {0} not found on {1}" -f $PublicationName, $instance) -ErrorRecord $_ -Target $instance -Continue + } + + } elseif ($pub.Type -eq 'Merge') { + + $mergePub = New-Object Microsoft.SqlServer.Replication.MergePublication + $mergePub.ConnectionContext = $pubReplServer.ConnectionContext + $mergePub.DatabaseName = $Database + $mergePub.Name = $PublicationName + + if ( $mergePub.LoadProperties() ) { + + if ($type = 'Push') { + # Perform a bitwise logical AND (& in Visual C# and And in Visual Basic) between the Attributes property and AllowPush. + if ($mergePub.Attributes -band 'AllowPush' -eq 'None' ) { + # If the result is None, set Attributes to the result of a bitwise logical OR (| in Visual C# and Or in Visual Basic) between Attributes and AllowPush. + $mergePub.Attributes = $mergePub.Attributes -bor 'AllowPush' + + # Then, call CommitPropertyChanges to enable push subscriptions. + $mergePub.CommitPropertyChanges() + } + + } else { + # Perform a bitwise logical AND (& in Visual C# and And in Visual Basic) between the Attributes property and AllowPull. + if ($mergePub.Attributes -band 'AllowPull' -eq 'None' ) { + # If the result is None, set Attributes to the result of a bitwise logical OR (| in Visual C# and Or in Visual Basic) between Attributes and AllowPull. + $mergePub.Attributes = $mergePub.Attributes -bor 'AllowPull' + + # Then, call CommitPropertyChanges to enable pull subscriptions. + $mergePub.CommitPropertyChanges() + } + } + + # create the subscription + if ($type = 'Push') { + $mergeSub = New-Object Microsoft.SqlServer.Replication.MergeSubscription + } else { + $mergeSub = New-Object Microsoft.SqlServer.Replication.MergePullSubscription + } + + $mergeSub.ConnectionContext = $pubReplServer.ConnectionContext + $mergeSub.SubscriptionDBName = $SubscriptionDatabase + $mergeSub.SubscriberName = $instance + $mergeSub.DatabaseName = $Database + $mergeSub.PublicationName = $PublicationName + + #TODO: + + <# + The Login and Password fields of SynchronizationAgentProcessSecurity to provide the credentials for the + Microsoft Windows account under which the Distribution Agent runs at the Distributor. This account is used to make local connections to the Distributor and to make + remote connections by using Windows Authentication. + + Note + Setting SynchronizationAgentProcessSecurity is not required when the subscription is created by a member of the sysadmin fixed server role, but we recommend it. + In this case, the agent will impersonate the SQL Server Agent account. For more information, see Replication Agent security model. + + (Optional) A value of true (the default) for CreateSyncAgentByDefault to create an agent job that is used to synchronize the subscription. + If you specify false, the subscription can only be synchronized programmatically. + + #> + if ($SubscriptionSqlCredential) { + $mergeSub.SubscriberSecurity.WindowsAuthentication = $false + $mergeSub.SubscriberSecurity.SqlStandardLogin = $SubscriptionSqlCredential.UserName + $mergeSub.SubscriberSecurity.SecureSqlStandardPassword = $SubscriptionSqlCredential.Password + } + + $mergeSub.Create() + } + + } else { + Stop-Function -Message ("Publication {0} not found on {1}" -f $PublicationName, $instance) -ErrorRecord $_ -Target $instance -Continue + } + } + } catch { + Stop-Function -Message ("Unable to create subscription - {0}" -f $_) -ErrorRecord $_ -Target $instance -Continue + } + #TODO: call Get-DbaReplSubscription when it's done + } + } +} \ No newline at end of file diff --git a/public/Remove-DbaReplArticle.ps1 b/public/Remove-DbaReplArticle.ps1 new file mode 100644 index 0000000000..a4354240a4 --- /dev/null +++ b/public/Remove-DbaReplArticle.ps1 @@ -0,0 +1,152 @@ +function Remove-DbaReplArticle { + <# + .SYNOPSIS + Removes an article from a publication for the database on the target SQL instances. + + .DESCRIPTION + Removes an article from a publication for the database on the target SQL instances. + + Dropping an article from a publication does not remove the object from the publication database or the corresponding object from the subscription database. + Use DROP to remove these objects if necessary. + #TODO: add a param for this DropObjectOnSubscriber + + Dropping an article invalidates the current snapshot; therefore a new snapshot must be created. + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Database + The database on the publisher that contains the article to be removed from replication. + + .PARAMETER Publication + The name of the replication publication. + + .PARAMETER Schema + Source schema of the replicated object to remove from the publication. + + .PARAMETER Name + The name of the article to remove. + + .PARAMETER DropObjectOnSubscriber + If this switch is enabled, the object will be dropped from the subscriber database. + + .PARAMETER InputObject + Enables piping from Get-DbaReplArticle + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + https://learn.microsoft.com/en-us/sql/relational-databases/replication/publish/delete-an-article + + .LINK + https://dbatools.io/Remove-DbaReplArticle + + .EXAMPLE + PS C:\> Remove-DbaReplArticle -SqlInstance mssql1 -Database Pubs -Publication PubFromPosh -Name 'publishers' + + Removes the publishers article from a publication called PubFromPosh on mssql1 + + .EXAMPLE + + PS C:\> Get-DbaReplArticle -SqlInstance mssql1 -Database Pubs -Publication TestPub | Remove-DbaReplArticle + + Removes all articles from a publication called TestPub on mssql1 + #> + [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [String]$Database, + [String]$Publication, + [String]$Schema = 'dbo', + [String]$Name, + #[Switch]$DropObjectOnSubscriber, + [Parameter(ValueFromPipeline)] + [Microsoft.SqlServer.Replication.Article[]]$InputObject, + [Switch]$EnableException + ) + + begin { + $articles = @( ) + } + + process { + if (-not $PSBoundParameters.SqlInstance -and -not $PSBoundParameters.InputObject) { + Stop-Function -Message "You must specify either SqlInstance or InputObject" + return + } + + if ($InputObject) { + $articles += $InputObject + } else { + $params = $PSBoundParameters + $null = $params.Remove('InputObject') + $null = $params.Remove('WhatIf') + $null = $params.Remove('Confirm') + $articles = Get-DbaReplArticle @params + } + } + + end { + # We have to delete in the end block to prevent "Collection was modified; enumeration operation may not execute." if directly piped from Get-DbaReplArticle. + foreach ($art in $articles) { + if ($PSCmdlet.ShouldProcess($art.Name, "Removing the article $($art.SourceObjectOwner).$($art.SourceObjectName) from the $($art.PublicationName) publication on $($art.SqlInstance)")) { + $output = [pscustomobject]@{ + ComputerName = $art.ComputerName + InstanceName = $art.InstanceName + SqlInstance = $art.SqlInstance + Database = $art.DatabaseName + ObjectName = $art.SourceObjectName + ObjectSchema = $art.SourceObjectOwner + Status = $null + IsRemoved = $false + } + try { + + $pub = Get-DbaReplPublication -SqlInstance $art.SqlInstance -SqlCredential $SqlCredential -Database $art.DatabaseName -Name $art.PublicationName -EnableException:$EnableException + + if (($pub.Subscriptions | Measure-Object).count -gt 0 ) { + Write-Message -Level Verbose -Message ("There is a subscription so remove article {0} from subscription on {1}" -f $art.Name, $pub.Subscriptions.SubscriberName) + $query = "exec sp_dropsubscription @publication = '{0}', @article= '{1}',@subscriber = '{2}'" -f $art.PublicationName, $art.Name, $pub.Subscriptions.SubscriberName + Invoke-DbaQuery -SqlInstance $art.SqlInstance -SqlCredential $SqlCredential -Database $art.DatabaseName -query $query -EnableException:$EnableException + } + if (($art.IsExistingObject)) { + $art.Remove() + } else { + Stop-Function -Message "Article doesn't exist in $PublicationName on $instance" -ErrorRecord $_ -Target $instance -Continue + } + $output.Status = "Removed" + $output.IsRemoved = $true + } catch { + Stop-Function -Message "Failed to remove the article from publication" -ErrorRecord $_ + $output.Status = (Get-ErrorMessage -Record $_) + $output.IsRemoved = $false + } + $output + } + } + } +} \ No newline at end of file diff --git a/public/Remove-DbaReplPublication.ps1 b/public/Remove-DbaReplPublication.ps1 new file mode 100644 index 0000000000..4959879e67 --- /dev/null +++ b/public/Remove-DbaReplPublication.ps1 @@ -0,0 +1,186 @@ +function Remove-DbaReplPublication { + <# + .SYNOPSIS + Removes a publication from the database on the target SQL instances. + + .DESCRIPTION + Removes a publication from the database on the target SQL instances. + + https://learn.microsoft.com/en-us/sql/relational-databases/replication/publish/delete-a-publication?view=sql-server-ver16#RMOProcedure + + .PARAMETER SqlInstance + The target SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Database + The database that will be replicated. + + .PARAMETER Name + The name of the replication publication + + .PARAMETER InputObject + A publication object retrieved from Get-DbaReplPublication. Enables piping from Get-DbaReplPublication. + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/Remove-DbaReplPublication + + .EXAMPLE + PS C:\> Remove-DbaReplPublication -SqlInstance mssql1 -Database Northwind -Name PubFromPosh + + Removes a publication called PubFromPosh from the Northwind database on mssql1 + + #> + [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [String]$Database, + [String]$Name, + [parameter(ValueFromPipeline)] + [Microsoft.SqlServer.Replication.Publication[]]$InputObject, + [Switch]$EnableException + ) + begin { + $publications = @( ) + } + process { + if (-not $PSBoundParameters.SqlInstance -and -not $PSBoundParameters.InputObject) { + Stop-Function -Message "You must specify either SqlInstance or InputObject" + return + } + + if ($InputObject) { + $publications += $InputObject + } else { + $params = $PSBoundParameters + $null = $params.Remove('InputObject') + $null = $params.Remove('WhatIf') + $null = $params.Remove('Confirm') + $publications = Get-DbaReplPublication @params + } + } + + end { + # We have to delete in the end block to prevent "Collection was modified; enumeration operation may not execute." if directly piped from Get-DbaReplArticle. + foreach ($pub in $publications) { + if ($PSCmdlet.ShouldProcess($pub.Name, "Removing the publication $($pub.Name) on $($pub.SqlInstance)")) { + $output = [PSCustomObject]@{ + ComputerName = $pub.ComputerName + InstanceName = $pub.InstanceName + SqlInstance = $pub.SqlInstance + Database = $pub.DatabaseName + Name = $pub.Name + Type = $pub.Type + Status = $null + IsRemoved = $false + } + + if ($pub.Type -in ('Transactional', 'Snapshot')) { + try { + if ($pub.IsExistingObject) { + Write-Message -Level Verbose -Message "Removing $($pub.Name) from $($pub.SqlInstance).$($pub.DatabaseName)" + + if ($PSCmdlet.ShouldProcess($pub.Name, "Stopping the REPL-LogReader job for the database $($pub.DatabaseName) on $($pub.SqlInstance)")) { + $null = Get-DbaAgentJob -SqlInstance $pub.SqlInstance -SqlCredential $SqlCredential -Category REPL-LogReader | Where-Object { $_.Name -like ('*{0}*' -f $pub.DatabaseName) } | Stop-DbaAgentJob + } + $pub.Remove() + + $output.Status = "Removed" + $output.IsRemoved = $true + } + } catch { + Stop-Function -Message "Failed to remove the publication from $($pub.SqlInstance)" -ErrorRecord $_ + $output.Status = (Get-ErrorMessage -Record $_) + $output.IsRemoved = $false + } + + try { + # If no other transactional publications exist for this database, the database can be disabled for transactional publishing + if (-not (Get-DbaReplPublication -SqlInstance $pub.SqlInstance -SqlCredential $SqlCredential -Database $pub.DatabaseName -Type Transactional, Snapshot -EnableException:$EnableException)) { + $pubDatabase = New-Object Microsoft.SqlServer.Replication.ReplicationDatabase + $pubDatabase.ConnectionContext = $pub.ConnectionContext + $pubDatabase.Name = $pub.DatabaseName + if (-not $pubDatabase.LoadProperties()) { + throw "Database $Database not found on $($pub.SqlInstance)" + } + + if ($pubDatabase.EnabledTransPublishing) { + Write-Message -Level Verbose -Message "No transactional publications on $Instance.$Database so disabling transactional publishing" + $pubDatabase.EnabledTransPublishing = $false + } + } + } catch { + Stop-Function -Message "Failed to disable transactional publishing on $($pub.SqlInstance)" -ErrorRecord $_ + } + + } elseif ($pub.Type -eq 'Merge') { + try { + if ($pub.IsExistingObject) { + Write-Message -Level Verbose -Message "Removing $($pub.Name) from $($pub.SqlInstance).$($pub.DatabaseName)" + if ($PSCmdlet.ShouldProcess($pub.Name, "Stopping the REPL-LogReader job for the database $($pub.DatabaseName) on $($pub.SqlInstance)")) { + $null = Get-DbaAgentJob -SqlInstance $pub.SqlInstance -SqlCredential $SqlCredential -Category REPL-LogReader | Where-Object { $_.Name -like ('*{0}*' -f $pub.DatabaseName) } | Stop-DbaAgentJob + } + $pub.Remove() + + $output.Status = "Removed" + $output.IsRemoved = $true + } else { + Write-Warning "Didn't find $($pub.Name) on $($pub.SqlInstance).$($pub.DatabaseName)" + } + } catch { + Stop-Function -Message "Failed to remove the publication from $($pub.SqlInstance)" -ErrorRecord $_ + $output.Status = (Get-ErrorMessage -Record $_) + $output.IsRemoved = $false + } + + try { + # If no other merge publications exist for this database, the database can be disabled for merge publishing + if (-not (Get-DbaReplPublication -SqlInstance $pub.SqlInstance -SqlCredential $SqlCredential -Database $pub.DatabaseName -Type Merge -EnableException:$EnableException)) { + $pubDatabase = New-Object Microsoft.SqlServer.Replication.ReplicationDatabase + $pubDatabase.ConnectionContext = $pub.ConnectionContext + $pubDatabase.Name = $pub.DatabaseName + + if (-not $pubDatabase.LoadProperties()) { + throw "Database $Database not found on $instance" + } + + if ($pubDatabase.EnabledTransPublishing) { + Write-Message -Level Verbose -Message "No merge publications on $Instance.$Database so disabling merge publishing" + $pubDatabase.EnabledMergePublishing = $false + } + } + } catch { + Stop-Function -Message "Failed to disable transactional publishing on $($pub.SqlInstance)" -ErrorRecord $_ + } + } + + $output + } + } + } +} \ No newline at end of file diff --git a/public/Remove-DbaReplSubscription.ps1 b/public/Remove-DbaReplSubscription.ps1 new file mode 100644 index 0000000000..b9cd288961 --- /dev/null +++ b/public/Remove-DbaReplSubscription.ps1 @@ -0,0 +1,144 @@ +function Remove-DbaReplSubscription { + <# + .SYNOPSIS + Removes a subscription \for the target SQL instances. + + .DESCRIPTION + Removes a subscription for the target SQL instances. + + https://learn.microsoft.com/en-us/sql/relational-databases/replication/delete-a-push-subscription?view=sql-server-ver16 + https://learn.microsoft.com/en-us/sql/relational-databases/replication/delete-a-pull-subscription?view=sql-server-ver16 + + .PARAMETER SqlInstance + The target publisher SQL Server instance or instances. + + .PARAMETER SqlCredential + Login to the target publisher instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER Database + The database where the publication is located. + + .PARAMETER SubscriberSqlInstance + The subscriber SQL Server instance. + + .PARAMETER SubscriberSqlCredential + Login to the subscriber instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). + + Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. + + For MFA support, please use Connect-DbaInstance. + + .PARAMETER PublicationName + The name of the publication. + + .PARAMETER SubscriptionDatabase + The database where the subscription is located. + + .PARAMETER EnableException + By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. + This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically 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: repl, Replication + Author: Jess Pomfret (@jpomfret), jesspomfret.com + + Website: https://dbatools.io + Copyright: (c) 2023 by dbatools, licensed under MIT + License: MIT https://opensource.org/licenses/MIT + + .LINK + https://dbatools.io/Remove-DbaReplSubscription + + .EXAMPLE + PS C:\> $sub = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'testPub' + SubscriberSqlInstance = 'mssql2' + SubscriptionDatabase = 'pubs' + } + PS C:\> Remove-DbaReplSubscription @sub + + Removes a subscription for the testPub publication on mssql2.pubs. + + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [DbaInstanceParameter[]]$SqlInstance, + [PSCredential]$SqlCredential, + [Parameter(Mandatory)] + [String]$Database, + [Parameter(Mandatory)] + [String]$PublicationName, + [Parameter(Mandatory)] + [DbaInstanceParameter]$SubscriberSqlInstance, + [PSCredential]$SubscriberSqlCredential, + [Parameter(Mandatory)] + [String]$SubscriptionDatabase, + [Switch]$EnableException + ) + begin { + + $pub = Get-DbaReplPublication -SqlInstance $SqlInstance -SqlCredential $SqlCredential -Name $PublicationName -EnableException:$EnableException + + if (-not $pub) { + Write-Warning "Didn't find a subscription to the $PublicationName publication on $SqlInstance.$Database" + } + + } + process { + foreach ($instance in $SubscriberSqlInstance) { + + try { + if ($PSCmdlet.ShouldProcess($instance, "Removing subscription to $PublicationName from $SqlInstance.$SubscriptionDatabase")) { + + if ($pub.Type -in ('Transactional', 'Snapshot')) { + + #TODO: Only handles push subscriptions at the moment - need to add pull subscriptions + # https://learn.microsoft.com/en-us/sql/relational-databases/replication/delete-a-pull-subscription?view=sql-server-ver16 + $transSub = New-Object Microsoft.SqlServer.Replication.TransSubscription + $transSub.ConnectionContext = $pub.ConnectionContext + $transSub.DatabaseName = $Database + $transSub.PublicationName = $PublicationName + $transSub.SubscriptionDBName = $SubscriptionDatabase + $transSub.SubscriberName = $instance + + if ($transSub.IsExistingObject) { + Write-Message -Level Verbose -Message "Removing the subscription" + $transSub.Remove() + } + + } elseif ($pub.Type -eq 'Merge') { + $mergeSub = New-Object Microsoft.SqlServer.Replication.MergeSubscription + $mergeSub.ConnectionContext = $pub.ConnectionContext + $mergeSub.DatabaseName = $Database + $mergeSub.PublicationName = $PublicationName + $mergeSub.SubscriptionDBName = $SubscriptionDatabase + $mergeSub.SubscriberName = $instance + + if ($mergeSub.IsExistingObject) { + Write-Message -Level Verbose -Message "Removing the merge subscription" + $mergeSub.Remove() + } else { + Write-Warning "Didn't find a subscription to $PublicationName on $($instance).$SubscriptionDatabase" + } + } + } + } catch { + Stop-Function -Message ("Unable to remove subscription - {0}" -f $_) -ErrorRecord $_ -Target $instance -Continue + } + } + } +} \ No newline at end of file diff --git a/public/Test-DbaRepLatency.ps1 b/public/Test-DbaReplLatency.ps1 similarity index 95% rename from public/Test-DbaRepLatency.ps1 rename to public/Test-DbaReplLatency.ps1 index 6224e0041a..aff54d33a1 100644 --- a/public/Test-DbaRepLatency.ps1 +++ b/public/Test-DbaReplLatency.ps1 @@ -1,4 +1,4 @@ -function Test-DbaRepLatency { +function Test-DbaReplLatency { <# .SYNOPSIS Displays replication latency for all transactional publications for a server or database. @@ -50,20 +50,20 @@ function Test-DbaRepLatency { License: MIT https://opensource.org/licenses/MIT .LINK - https://dbatools.io/Test-DbaRepLatency + https://dbatools.io/Test-DbaReplLatency .EXAMPLE - PS C:\> Test-DbaRepLatency -SqlInstance sql2008, sqlserver2012 + PS C:\> Test-DbaReplLatency -SqlInstance sql2008, sqlserver2012 Return replication latency for all transactional publications for servers sql2008 and sqlserver2012. .EXAMPLE - PS C:\> Test-DbaRepLatency -SqlInstance sql2008 -Database TestDB + PS C:\> Test-DbaReplLatency -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 + PS C:\> Test-DbaReplLatency -SqlInstance sql2008 -Database TestDB -PublicationName TestDB_Pub Return replication latency for the TestDB_Pub publication for the TestDB database located on the server sql2008. @@ -94,7 +94,7 @@ function Test-DbaRepLatency { Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue } - $publicationNames = Get-DbaRepPublication -SqlInstance $server -Database $Database -SqlCredential $SqlCredentials -PublicationType "Transactional" + $publicationNames = Get-DbaReplPublication -SqlInstance $server -Database $Database -SqlCredential $SqlCredentials -Type "Transactional" if ($PublicationName) { $publicationNames = $publicationNames | Where-Object PublicationName -in $PublicationName @@ -205,7 +205,7 @@ function Test-DbaRepLatency { PublicationServer = $publication.Server PublicationDB = $publication.Database PublicationName = $publication.PublicationName - PublicationType = $publication.PublicationType + PublicationType = $publication.Type DistributionServer = $distributionServer DistributionDB = $distributionDatabase SubscriberServer = $info.subscriber @@ -213,7 +213,7 @@ function Test-DbaRepLatency { PublisherToDistributorLatency = $info.distributor_latency DistributorToSubscriberLatency = $info.subscriber_latency TotalLatency = $totalLatency - } | Select-DefaultView -ExcludeProperty PublicationType + } | Select-DefaultView -ExcludeProperty Type if (!$RetainToken) { diff --git a/testing.ps1 b/testing.ps1 new file mode 100644 index 0000000000..842d9de1e1 --- /dev/null +++ b/testing.ps1 @@ -0,0 +1,271 @@ +############################## +# create docker environment +############################## +# create a shared network +docker network create localnet + +# Expose engines and setup shared path for migrations +docker run -p 2500:1433 --volume shared:/shared:z --name mssql1 --hostname mssql1 --network localnet -d dbatools/sqlinstance +docker run -p 2600:1433 --volume shared:/shared:z --name mssql2 --hostname mssql2 --network localnet -d dbatools/sqlinstance2 + +# create the repl folder +docker exec mssql1 mkdir /var/opt/mssql/ReplData + +# also need these folders for setting up replication +docker exec mssql1 mkdir /shared/data /shared/repldata + +############################## +# import our working module +############################## +Import-Module .\dbatools.psd1 -Force +Get-module dbatools* + +############################## +# create alias +############################## +New-DbaClientAlias -ServerName 'localhost,2500' -Alias mssql1 +New-DbaClientAlias -ServerName 'localhost,2600' -Alias mssql2 + + +############################## +# save the password for ease +############################## +$securePassword = ('dbatools.IO' | ConvertTo-SecureString -AsPlainText -Force) +$credential = New-Object System.Management.Automation.PSCredential('sqladmin', $securePassword) + +$PSDefaultParameterValues = @{ + "*:SqlCredential" = $credential + "*:DestinationCredential" = $credential + "*:DestinationSqlCredential" = $credential + "*:SourceSqlCredential" = $credential + "*:PublisherSqlCredential" = $credential +} + + +############################## +# change silly defaults +############################## +Set-DbatoolsConfig -FullName sql.connection.trustcert -Value $true -PassThru | Register-DbatoolsConfig #-Scope SystemMandatory +Set-DbatoolsConfig -FullName sql.connection.EncryptConnection -Value optional -PassThru | Register-DbatoolsConfig #-Scope SystemMandatory + +############################## +# test things :) +############################## + + +## already existing commands +<# +Get-DbaReplServer +Get-DbaReplDistributor +Get-DbaReplPublication +Test-DbaReplLatency + +Export-DbaReplServerSetting +#> + +$sqlInstance = Connect-DbaInstance -SqlInstance mssql1 + +Get-DbaReplServer -SqlInstance mssql1 +Get-DbaReplDistributor -SqlInstance mssql1 +Get-DbaReplPublication -SqlInstance mssql1 + +# enable\disable distribution +Enable-DbaReplDistributor -SqlInstance mssql1 +Disable-DbaReplDistributor -SqlInstance mssql1 + +Get-DbaReplDistributor -SqlInstance + +# enable publishing +Enable-DbaReplPublishing -SqlInstance mssql1 +Disable-DbaReplPublishing -SqlInstance mssql1 + +# add a transactional publication +$pub = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'testPub' + Type = 'Transactional' + +} +New-DbaReplPublication @pub -verbose + + + +# add a merge publication +$pub = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'mergey' + Type = 'Merge' + +} +New-DbaReplPublication @pub + +# add a snapshot publication +$pub = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'snappy' + Type = 'Snapshot' + +} +New-DbaReplPublication @pub + + +# add an article + +$article = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'testpub' + Name = 'publishers' + Filter = "city = 'seattle'" +} +Add-DbaReplArticle @article + +$article = @{ + SqlInstance = 'mssql1' + Database = 'ReplDb' + PublicationName = 'testtrans' + Name = 'ReplicateMe' +} +Add-DbaReplArticle @article -EnableException + +# mergey +$article = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'Mergey' + Name = 'publishers' + #Filter = "city = 'seattle'" ## not working? +} +Add-DbaReplArticle @article + +# snappy +$article = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'snappy' + Name = 'publishers' +} +Add-DbaReplArticle @article + + +# remove an article +$article = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'testpub' + Name = 'publishers' +} +Remove-DbaReplArticle @article + +# remove an article +$article = @{ + SqlInstance = 'mssql1' + Database = 'pubs' + PublicationName = 'Mergey' + Name = 'publishers' +} +Remove-DbaReplArticle @article + + + +## remove pubs +$pub = @{ + SqlInstance = 'mssql1' + Database = 'ReplDb' + Name = 'Snappy' +} +Remove-DbaReplPublication @pub +## remove pubs +$pub = @{ + SqlInstance = 'mssql1' + Database = 'ReplDb' + Name = 'TestPub' +} +Remove-DbaReplPublication @pub +## remove pubs +$pub = @{ + SqlInstance = 'mssql1' + Database = 'ReplDb' + Name = 'Mergey' +} +Remove-DbaReplPublication @pub + + + +# add subscriptions. + +#transactional +$sub = @{ + SqlInstance = 'mssql2' + Database = 'pubs' + PublicationDatabase = 'pubs' + PublisherSqlInstance = 'mssql1' + PublicationName = 'testpub' + Type = 'Push' + SubscriptionSqlCredential = $credential + +} +New-DbaReplSubscription @sub -enableexception + +#merge +$sub = @{ + SqlInstance = 'mssql2' + Database = 'Mergeypubs' + PublicationDatabase = 'pubs' + PublisherSqlInstance = 'mssql1' + PublicationName = 'Mergey' + Type = 'Push' + SubscriptionSqlCredential = $credential + +} +New-DbaReplSubscription @sub + +#snapshot +$sub = @{ + SqlInstance = 'mssql2' + Database = 'Snappypubs' + PublicationDatabase = 'pubs' + PublisherSqlInstance = 'mssql1' + PublicationName = 'Snappy' + Type = 'Push' + SubscriptionSqlCredential = $credential +} +New-DbaReplSubscription @sub + + +# remove subscriptions +$sub = @{ + SqlInstance = 'mssql2' + SubscriptionDatabase = 'pubs' + PublisherSqlInstance = 'mssql1' + PublicationDatabase = 'pubs' + PublicationName = 'testPub' +} +Remove-DbaReplSubscription @sub + +$sub = @{ + SqlInstance = 'mssql2' + SubscriptionDatabase = 'Mergeypubs' + PublisherSqlInstance = 'mssql1' + PublicationDatabase = 'pubs' + PublicationName = 'Mergey' +} +Remove-DbaReplSubscription @sub + +$sub = @{ + SqlInstance = 'mssql2' + PublisherSqlInstance = 'mssql1' + PublicationName = 'snappy' + PublicationDatabase = 'pubs' + SubscriptionDatabase = 'Snappypubs' +} +Remove-DbaReplSubscription @sub + +# TODO: Does the schema exist on the subscriber? +<# +// Ensure that we create the schema owner at the Subscriber. +article.SchemaOption |= CreationScriptOptions.Schema; +#> \ No newline at end of file diff --git a/tests/Add-DbaReplArticle.Tests.ps1 b/tests/Add-DbaReplArticle.Tests.ps1 new file mode 100644 index 0000000000..78d16d32ff --- /dev/null +++ b/tests/Add-DbaReplArticle.Tests.ps1 @@ -0,0 +1,17 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'Publication', 'Schema', 'Name', 'Filter', 'CreationScriptOptions', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/Disable-DbaReplDistributor.Tests.ps1 b/tests/Disable-DbaReplDistributor.Tests.ps1 new file mode 100644 index 0000000000..8707c37e5a --- /dev/null +++ b/tests/Disable-DbaReplDistributor.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Force', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/Disable-DbaReplPublishing.Tests.ps1 b/tests/Disable-DbaReplPublishing.Tests.ps1 new file mode 100644 index 0000000000..db1d3ce2fd --- /dev/null +++ b/tests/Disable-DbaReplPublishing.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Force', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> diff --git a/tests/Enable-DbaReplDistributor.Tests.ps1 b/tests/Enable-DbaReplDistributor.Tests.ps1 new file mode 100644 index 0000000000..c1f08019a0 --- /dev/null +++ b/tests/Enable-DbaReplDistributor.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'DistributionDatabase', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/Enable-DbaReplPublishing.Tests.ps1 b/tests/Enable-DbaReplPublishing.Tests.ps1 new file mode 100644 index 0000000000..08143c6619 --- /dev/null +++ b/tests/Enable-DbaReplPublishing.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'SnapshotShare', 'PublisherSqlLogin', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/Export-DbaRepServerSetting.Tests.ps1 b/tests/Export-DbaReplServerSetting.Tests.ps1 similarity index 100% rename from tests/Export-DbaRepServerSetting.Tests.ps1 rename to tests/Export-DbaReplServerSetting.Tests.ps1 diff --git a/tests/Get-DbaReplArticle.Tests.ps1 b/tests/Get-DbaReplArticle.Tests.ps1 new file mode 100644 index 0000000000..11bdb49fd2 --- /dev/null +++ b/tests/Get-DbaReplArticle.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'Publication', 'Schema', 'Name', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/Get-DbaReplArticleColumn.Tests.ps1 b/tests/Get-DbaReplArticleColumn.Tests.ps1 new file mode 100644 index 0000000000..3a5f07cf69 --- /dev/null +++ b/tests/Get-DbaReplArticleColumn.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'Publication', 'Article', 'Column', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/Get-DbaRepDistributor.Tests.ps1 b/tests/Get-DbaReplDistributor.Tests.ps1 similarity index 93% rename from tests/Get-DbaRepDistributor.Tests.ps1 rename to tests/Get-DbaReplDistributor.Tests.ps1 index 2d4231cb8a..dded7c7e9d 100644 --- a/tests/Get-DbaRepDistributor.Tests.ps1 +++ b/tests/Get-DbaReplDistributor.Tests.ps1 @@ -15,7 +15,7 @@ Describe "$CommandName Unit Tests" -Tag 'UnitTests' { Describe "$CommandName Integration Tests" -Tags "IntegrationTests" { Context "ensuring accuracy of results" { - $results = Get-DbaRepDistributor -SqlInstance $script:instance1 + $results = Get-DbaReplDistributor -SqlInstance $script:instance1 It "accurately reports that the distributor is not installed" { $results.DistributorInstalled | Should Be $false } diff --git a/tests/Get-DbaRepPublication.Tests.ps1 b/tests/Get-DbaReplPublication.Tests.ps1 similarity index 55% rename from tests/Get-DbaRepPublication.Tests.ps1 rename to tests/Get-DbaReplPublication.Tests.ps1 index 57e1f5c9e4..fc06284fa7 100644 --- a/tests/Get-DbaRepPublication.Tests.ps1 +++ b/tests/Get-DbaReplPublication.Tests.ps1 @@ -4,11 +4,11 @@ Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan Describe "$commandname Unit Tests" -Tag 'UnitTests' { Context "Validate parameters" { - [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} - [object[]]$knownParameters = 'SqlInstance', 'Database', 'SqlCredential', 'PublicationType', 'EnableException' + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object { $_ -notin ('whatif', 'confirm') } + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'Name', 'Type', 'EnableException' $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters It "Should only contain our specific parameters" { - (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object { $_ }) -DifferenceObject $params).Count ) | Should Be 0 } } @@ -19,8 +19,9 @@ Describe "$commandname Unit Tests" -Tag 'UnitTests' { [object]@{ Name = 'TestDB' TransPublications = @{ - Name = 'TestDB_pub' - Type = 'Transactional' + Name = 'TestDB_pub' + Type = 'Transactional' + DatabaseName = 'TestDB' } MergePublications = @{} } @@ -28,32 +29,36 @@ Describe "$commandname Unit Tests" -Tag 'UnitTests' { Mock Connect-DbaInstance -MockWith { [object]@{ - Name = "MockServerName" - ComputerName = 'MockComputerName' - Databases = @{ + Name = "MockServerName" + ServiceName = 'MSSQLSERVER' + DomainInstanceName = 'MockServerName' + ComputerName = 'MockComputerName' + Databases = @{ Name = 'TestDB' #state #status ID = 5 ReplicationOptions = 'Published' + IsAccessible = $true + IsSystemObject = $false } - ConnectionContext = @{ + ConnectionContext = @{ SqlConnectionObject = 'FakeConnectionContext' } } } It "Honors the SQLInstance parameter" { - $Results = Get-DbaRepPublication -SqlInstance MockServerName - $Results.Server | Should Be "MockServerName" + $Results = Get-DbaReplPublication -SqlInstance MockServerName + $Results.SqlInstance.Name | Should Be "MockServerName" } It "Honors the Database parameter" { - $Results = Get-DbaRepPublication -SqlInstance MockServerName -Database TestDB - $Results.Database | Should Be "TestDB" + $Results = Get-DbaReplPublication -SqlInstance MockServerName -Database TestDB + $Results.DatabaseName | Should Be "TestDB" } - It "Honors the PublicationType parameter" { + It "Honors the Type parameter" { Mock Connect-ReplicationDB -MockWith { [object]@{ @@ -66,13 +71,13 @@ Describe "$commandname Unit Tests" -Tag 'UnitTests' { } } - $Results = Get-DbaRepPublication -SqlInstance MockServerName -Database TestDB -PublicationType Snapshot - $Results.PublicationType | Should Be "Snapshot" + $Results = Get-DbaReplPublication -SqlInstance MockServerName -Database TestDB -Type Snapshot + $Results.Type | Should Be "Snapshot" } - It "Stops if validate set for PublicationType is not met" { + It "Stops if validate set for Type is not met" { - { Get-DbaRepPublication -SqlInstance MockServerName -PublicationType NotAPubType } | should Throw + { Get-DbaReplPublication -SqlInstance MockServerName -Type NotAPubType } | should Throw } } diff --git a/tests/Get-DbaReplPublisher.Tests.ps1 b/tests/Get-DbaReplPublisher.Tests.ps1 new file mode 100644 index 0000000000..dde5fa4f51 --- /dev/null +++ b/tests/Get-DbaReplPublisher.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/Get-DbaRepServer.Tests.ps1 b/tests/Get-DbaReplServer.Tests.ps1 similarity index 100% rename from tests/Get-DbaRepServer.Tests.ps1 rename to tests/Get-DbaReplServer.Tests.ps1 diff --git a/tests/Get-DbaReplSubscription.Tests.ps1 b/tests/Get-DbaReplSubscription.Tests.ps1 new file mode 100644 index 0000000000..bee78c532f --- /dev/null +++ b/tests/Get-DbaReplSubscription.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm') } + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'PublicationName', 'SubscriberName', 'SubscriptionDatabase', 'Type', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/InModule.Help.Tests.ps1 b/tests/InModule.Help.Tests.ps1 index a9df2bc916..277981cc47 100644 --- a/tests/InModule.Help.Tests.ps1 +++ b/tests/InModule.Help.Tests.ps1 @@ -18,7 +18,9 @@ if ($env:appveyor) { 'Microsoft.SqlServer.Management.XEvent', 'Microsoft.SqlServer.Management.XEventDbScoped', 'Microsoft.SqlServer.Management.XEventDbScopedEnum', - 'Microsoft.SqlServer.Management.XEventEnum' + 'Microsoft.SqlServer.Management.XEventEnum', + 'Microsoft.SqlServer.Replication', + 'Microsoft.SqlServer.Rmo' ) foreach ($name in $names) { diff --git a/tests/New-DbaReplCreationScriptOptions.Tests.ps1 b/tests/New-DbaReplCreationScriptOptions.Tests.ps1 new file mode 100644 index 0000000000..c7e06f9e13 --- /dev/null +++ b/tests/New-DbaReplCreationScriptOptions.Tests.ps1 @@ -0,0 +1,19 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'Options', 'NoDefaults' + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/New-DbaReplPublication.Tests.ps1 b/tests/New-DbaReplPublication.Tests.ps1 new file mode 100644 index 0000000000..7306059d09 --- /dev/null +++ b/tests/New-DbaReplPublication.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'Name', 'Type', 'LogReaderAgentCredential', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> diff --git a/tests/New-DbaReplSubscription.Tests.ps1 b/tests/New-DbaReplSubscription.Tests.ps1 new file mode 100644 index 0000000000..64176e3048 --- /dev/null +++ b/tests/New-DbaReplSubscription.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'SubscriberSqlInstance', 'SubscriberSqlCredential', 'SubscriptionDatabase', 'PublicationName', 'SubscriptionSqlCredential', 'Type', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> diff --git a/tests/Remove-DbaReplArticle.Tests.ps1 b/tests/Remove-DbaReplArticle.Tests.ps1 new file mode 100644 index 0000000000..3df7559a0f --- /dev/null +++ b/tests/Remove-DbaReplArticle.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'Publication', 'Schema', 'Name', 'InputObject', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/Remove-DbaReplPublication.Tests.ps1 b/tests/Remove-DbaReplPublication.Tests.ps1 new file mode 100644 index 0000000000..7914ec229d --- /dev/null +++ b/tests/Remove-DbaReplPublication.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'Name', 'InputObject', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/Remove-DbaReplSubscription.Tests.ps1 b/tests/Remove-DbaReplSubscription.Tests.ps1 new file mode 100644 index 0000000000..730d6870a6 --- /dev/null +++ b/tests/Remove-DbaReplSubscription.Tests.ps1 @@ -0,0 +1,20 @@ +$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "") +Write-Host -Object "Running $PSCommandPath" -ForegroundColor Cyan +. "$PSScriptRoot\constants.ps1" + + +Add-ReplicationLibrary + +Describe "$CommandName Unit Tests" -Tag 'UnitTests' { + Context "Validate parameters" { + [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'Database', 'PublicationName', 'SubscriberSqlInstance', 'SubscriberSqlCredential', 'SubscriptionDatabase', 'EnableException' + $knownParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + It "Should only contain our specific parameters" { + (@(Compare-Object -ReferenceObject ($knownParameters | Where-Object {$_}) -DifferenceObject $params).Count ) | Should Be 0 + } + } +} +<# + Integration tests for replication are in GitHub Actions and run from \tests\gh-actions-repl-*.ps1.ps1 +#> \ No newline at end of file diff --git a/tests/Test-DbaRepLatency.Tests.ps1 b/tests/Test-DbaReplLatency.Tests.ps1 similarity index 100% rename from tests/Test-DbaRepLatency.Tests.ps1 rename to tests/Test-DbaReplLatency.Tests.ps1 diff --git a/tests/gh-actions-repl-1.ps1 b/tests/gh-actions-repl-1.ps1 new file mode 100644 index 0000000000..e925b6a295 --- /dev/null +++ b/tests/gh-actions-repl-1.ps1 @@ -0,0 +1,315 @@ +Describe "Integration Tests" -Tag "IntegrationTests" { + BeforeAll { + $password = ConvertTo-SecureString "dbatools.IO" -AsPlainText -Force + $cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "sqladmin", $password + + $PSDefaultParameterValues["*:SqlInstance"] = "mssql1" + $PSDefaultParameterValues["*:SqlCredential"] = $cred + $PSDefaultParameterValues["*:SubscriberSqlCredential"] = $cred + $PSDefaultParameterValues["*:Confirm"] = $false + $PSDefaultParameterValues["*:SharedPath"] = "/shared" + $PSDefaultParameterValues["*:WarningAction"] = "SilentlyContinue" + $global:ProgressPreference = "SilentlyContinue" + + Import-Module ./dbatools.psd1 -Force + + $null = New-DbaDatabase -Name ReplDb + $null = Invoke-DbaQuery -Database ReplDb -Query 'CREATE TABLE ReplicateMe ( id int identity (1,1) PRIMARY KEY, col1 varchar(10) ); CREATE TABLE ReplicateMeToo ( id int identity (1,1) PRIMARY KEY, col1 varchar(10) );' + } + + Describe "General commands" -Tag general { + + Context "Get-DbaReplServer works" { + + It "Doesn't throw errors" { + { Get-DbaReplServer -EnableException } | Should -Not -Throw + } + + It "Returns a ReplicationObject" { + (Get-DbaReplServer).GetType().BaseType | Should -Be "Microsoft.SqlServer.Replication.ReplicationObject" + } + + It "Gets a replication server" { + (Get-DbaReplServer).SqlInstance | Should -Be 'mssql1' + (Get-DbaReplServer).DistributorInstalled | Should -Not -BeNullOrEmpty + (Get-DbaReplServer).DistributorAvailable | Should -Not -BeNullOrEmpty + (Get-DbaReplServer).IsDistributor | Should -Not -BeNullOrEmpty + (Get-DbaReplServer).IsPublisher | Should -Not -BeNullOrEmpty + } + } + } + + Describe "Publishing\Distribution commands" -tag pub { + + Context "Get-DbaReplDistributor works" { + BeforeAll { + + # if distribution is enabled, disable it & enable it with defaults + if ((Get-DbaReplDistributor).IsDistributor) { + Disable-DbaReplDistributor + } + Enable-DbaReplDistributor + } + + It "gets a distributor without error" { + { Get-DbaReplDistributor -EnableException } | Should -Not -Throw + } + + It "gets a distributor" { + (Get-DbaReplDistributor).IsDistributor | Should -Be $true + } + + It "distribution database name is correct" { + (Get-DbaReplDistributor).DistributionDatabases.Name | Should -Be 'distribution' + } + + + } + + Context "Get-DbaReplPublisher works" { + BeforeAll { + # if distribution is disabled - enable it + if (-not (Get-DbaReplDistributor).IsDistributor) { + Enable-DbaReplDistributor + } + + # if publishing is disabled - enable it + if (-not (Get-DbaReplServer).IsPublisher) { + Enable-DbaReplPublishing -PublisherSqlLogin $cred -EnableException + } + } + + It "gets a publisher" { + (Get-DbaReplPublisher).PublisherType | Should -Be "MSSQLSERVER" + } + + + } + + Context "Enable-DbaReplDistributor works" { + BeforeAll { + # if distribution is enabled - disable it + if ((Get-DbaReplDistributor).IsDistributor) { + Disable-DbaReplDistributor + } + } + + It "distribution starts disabled" { + (Get-DbaReplDistributor).IsDistributor | Should -Be $false + } + + It "distribution is enabled" { + Enable-DbaReplDistributor + (Get-DbaReplDistributor).IsDistributor | Should -Be $true + } + } + + Context "Enable-DbaReplDistributor works with specified database name" { + BeforeAll { + # if distribution is enabled - disable it + if ((Get-DbaReplDistributor).IsDistributor) { + Disable-DbaReplDistributor + } + } + AfterAll { + if ((Get-DbaReplDistributor).IsDistributor) { + Disable-DbaReplDistributor + } + } + + It "distribution starts disabled" { + (Get-DbaReplDistributor).IsDistributor | Should -Be $false + } + + It "distribution is enabled with specific database" { + $distDb = ('distdb-{0}' -f (Get-Random)) + Enable-DbaReplDistributor -DistributionDatabase $distDb + (Get-DbaReplDistributor).DistributionDatabases.Name | Should -Be $distDb + } + } + + Context "Disable-DbaReplDistributor works" { + BeforeAll { + # if replication is disabled - enable it + if (-not (Get-DbaReplDistributor).IsDistributor) { + Enable-DbaReplDistributor + } + } + + It "distribution starts enabled" { + (Get-DbaReplDistributor).IsDistributor | Should -Be $true + } + + It "distribution is disabled" { + Disable-DbaReplDistributor + (Get-DbaReplDistributor).IsDistributor | Should -Be $false + } + } + + Context "Enable-DbaReplPublishing works" { + BeforeAll { + # if Publishing is enabled - disable it + if ((Get-DbaReplServer).IsPublisher) { + Disable-DbaReplPublishing + } + # if distribution is disabled - enable it + if (-not (Get-DbaReplDistributor).IsDistributor) { + Enable-DbaReplDistributor + } + } + + It "publishing starts disabled" { + (Get-DbaReplServer).IsPublisher | Should -Be $false + } + + It "publishing is enabled" { + { Enable-DbaReplPublishing -EnableException } | Should -Not -Throw + (Get-DbaReplServer).IsPublisher | Should -Be $true + } + } + + Context "Disable-DbaReplPublishing works" { + BeforeAll { + # if publishing is disabled - enable it + if (-not (Get-DbaReplServer).IsPublisher) { + write-output -message 'I should enable publishing' + Enable-DbaReplPublishing -EnableException + } + + # if distribution is disabled - enable it + if (-not (Get-DbaReplDistributor).IsDistributor) { + write-output -message 'I should enable distribution' + Enable-DbaReplDistributor -EnableException + } + } + + It "publishing starts enabled" { + (Get-DbaReplServer).IsPublisher | Should -Be $true + } + + It "publishing is disabled" { + { Disable-DbaReplPublishing -EnableException } | Should -Not -Throw + (Get-DbaReplServer).IsPublisher | Should -Be $false + } + } + } + + Describe "Publication commands" -tag pub { + + Context "Get-DbaReplPublication works" { + BeforeAll { + # if distribution is disabled - enable it + if (-not (Get-DbaReplDistributor).IsDistributor) { + Enable-DbaReplDistributor + } + + # if publishing is disabled - enable it + if (-not (Get-DbaReplServer).IsPublisher) { + Enable-DbaReplPublishing -PublisherSqlLogin $cred -EnableException + } + + # create a publication + $name = 'TestPub' + New-DbaReplPublication -Database ReplDb -Type Transactional -Name ('{0}-Trans' -f $Name) + New-DbaReplPublication -Database ReplDb -Type Merge -Name ('{0}-Merge' -f $Name) + $null = New-DbaDatabase -Name Test + New-DbaReplPublication -Database Test -Type Snapshot -Name ('{0}-Snapshot' -f $Name) + } + + It "gets all publications" { + Get-DbaReplPublication | Should -Not -BeNullOrEmpty + } + + It "gets publications for a specific database" { + Get-DbaReplPublication -Database ReplDb | Should -Not -BeNullOrEmpty + (Get-DbaRepPublication -Database ReplDb).DatabaseName | ForEach-Object { $_ | Should -Be 'ReplDb' } + } + + It "gets publications for a specific type" { + Get-DbaReplPublication -Type Transactional | Should -Not -BeNullOrEmpty + (Get-DbaRepPublication -Type Transactional).Type | ForEach-Object { $_ | Should -Be 'Transactional' } + } + + } + + Context "New-DbaReplPublication works" { + BeforeAll { + # if replication is disabled - enable it + if (-not (Get-DbaReplDistributor).IsDistributor) { + Enable-DbaReplDistributor + } + # if publishing is disabled - enable it + if (-not (Get-DbaReplServer).IsPublisher) { + Enable-DbaReplPublishing -PublisherSqlLogin $cred -EnableException + } + } + + It "New-DbaReplPublication creates a Transactional publication" { + $name = 'TestPub' + { New-DbaReplPublication -Database ReplDb -Type Transactional -Name $Name -EnableException } | Should -Not -Throw + (Get-DbaReplPublication -Name $Name) | Should -Not -BeNullOrEmpty + (Get-DbaReplPublication -Name $Name).DatabaseName | Should -Be 'ReplDb' + (Get-DbaReplPublication -Name $Name).Type | Should -Be 'Transactional' + } + It "New-DbaReplPublication creates a Snapshot publication" { + $name = 'Snappy' + { New-DbaReplPublication -Database ReplDb -Type Snapshot -Name $name -EnableException } | Should -Not -Throw + (Get-DbaReplPublication -Name $name) | Should -Not -BeNullOrEmpty + (Get-DbaReplPublication -Name $name).DatabaseName | Should -Be 'ReplDb' + (Get-DbaReplPublication -Name $name).Type | Should -Be 'Snapshot' + } + It "New-DbaReplPublication creates a Merge publication" { + $name = 'Mergey' + { New-DbaReplPublication -Database ReplDb -Type Merge -Name $name -EnableException } | Should -Not -Throw + (Get-DbaReplPublication -Name $name) | Should -Not -BeNullOrEmpty + (Get-DbaReplPublication -Name $name).DatabaseName | Should -Be 'ReplDb' + (Get-DbaReplPublication -Name $name).Type | Should -Be 'Merge' + } + } + + Context "Remove-DbaReplPublication works" { + BeforeAll { + # if replication is disabled - enable it + if (-not (Get-DbaReplDistributor).IsDistributor) { + Enable-DbaReplDistributor + } + # if publishing is disabled - enable it + if (-not (Get-DbaReplServer).IsPublisher) { + Enable-DbaReplPublishing -PublisherSqlLogin $cred -EnableException + } + $articleName = 'ReplicateMe' + + # we need some publications too + $pubName = 'TestTrans' + if (-not (Get-DbaReplPublication -Name $pubName -Type Transactional)) { + $null = New-DbaReplPublication -Database ReplDb -Type Transactional -Name $pubName + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubName -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubName -Name $articleName + } + + $pubName = 'TestSnap' + if (-not (Get-DbaReplPublication -Name $pubName -Type Snapshot)) { + $null = New-DbaReplPublication -Database ReplDb -Type Snapshot -Name $pubName + } + $pubName = 'TestMerge' + if (-not (Get-DbaReplPublication -Name $pubName -Type Merge)) { + $null = New-DbaReplPublication -Database ReplDb -Type Merge -Name $pubName + } + } + + It "Remove-DbaReplPublication removes a publication that has articles" { + $name = 'TestTrans' + { Remove-DbaReplPublication -Name $name -EnableException } | Should -Not -Throw + (Get-DbaReplPublication -Name $name) | Should -BeNullOrEmpty + } + + It "Remove-DbaReplPublication removes a publication that has no articles" { + $name = 'TestSnap' + { Remove-DbaReplPublication -Name $name -EnableException } | Should -Not -Throw + (Get-DbaReplPublication -Name $name) | Should -BeNullOrEmpty + } + + } + } +} diff --git a/tests/gh-actions-repl-2.ps1 b/tests/gh-actions-repl-2.ps1 new file mode 100644 index 0000000000..d80d258350 --- /dev/null +++ b/tests/gh-actions-repl-2.ps1 @@ -0,0 +1,337 @@ +Describe "Integration Tests" -Tag "IntegrationTests" { + BeforeAll { + $password = ConvertTo-SecureString "dbatools.IO" -AsPlainText -Force + $cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "sqladmin", $password + + $PSDefaultParameterValues["*:SqlInstance"] = "mssql1" + $PSDefaultParameterValues["*:SqlCredential"] = $cred + $PSDefaultParameterValues["*:SubscriberSqlCredential"] = $cred + $PSDefaultParameterValues["*:Confirm"] = $false + $PSDefaultParameterValues["*:SharedPath"] = "/shared" + $PSDefaultParameterValues["*:WarningAction"] = "SilentlyContinue" + $global:ProgressPreference = "SilentlyContinue" + + Import-Module ./dbatools.psd1 -Force + + $null = New-DbaDatabase -Name ReplDb + $null = Invoke-DbaQuery -Database ReplDb -Query 'CREATE TABLE ReplicateMe ( id int identity (1,1) PRIMARY KEY, col1 varchar(10) ); CREATE TABLE ReplicateMeToo ( id int identity (1,1) PRIMARY KEY, col1 varchar(10) );' + } + + Describe "Article commands" -tag art { + BeforeAll { + # if replication is disabled - enable it + if (-not (Get-DbaReplDistributor).IsDistributor) { + Enable-DbaReplDistributor + } + # if publishing is disabled - enable it + if (-not (Get-DbaReplServer).IsPublisher) { + Enable-DbaReplPublishing -PublisherSqlLogin $cred -EnableException + } + # we need some publications too + $name = 'TestTrans' + if (-not (Get-DbaReplPublication -Name $name -Type Transactional )) { + $null = New-DbaReplPublication -Database ReplDb -Type Transactional -Name $Name + } + $name = 'TestSnap' + if (-not (Get-DbaReplPublication -Name $name -Type Snapshot)) { + $null = New-DbaReplPublication -Database ReplDb -Type Snapshot -Name $Name + } + $name = 'TestMerge' + if (-not (Get-DbaReplPublication -Name $name -Type Merge)) { + $null = New-DbaReplPublication -Database ReplDb -Type Merge -Name $Name + } + $articleName = 'ReplicateMe' + $articleName2 = 'ReplicateMeToo' + } + + Context "New-DbaReplCreationScriptOptions works" { + It "New-DbaReplCreationScriptOptions creates a Microsoft.SqlServer.Replication.CreationScriptOptions" { + $options = New-DbaReplCreationScriptOptions + $options | Should -BeOfType Microsoft.SqlServer.Replication.CreationScriptOptions + } + It "Microsoft.SqlServer.Replication.CreationScriptOptions should include the 11 defaults by default" { + ((New-DbaReplCreationScriptOptions).ToString().Split(',').Trim() | Measure-Object).Count | Should -Be 11 + } + It "Microsoft.SqlServer.Replication.CreationScriptOptions should include ClusteredIndexes" { + ((New-DbaReplCreationScriptOptions).ToString().Split(',').Trim() | Should -Contain 'ClusteredIndexes') + } + It "Microsoft.SqlServer.Replication.CreationScriptOptions with option of NonClusteredIndexes should add NonClusteredIndexes" { + ((New-DbaReplCreationScriptOptions -Option NonClusteredIndexes).ToString().Split(',').Trim() | Measure-Object).Count | Should -Be 12 + ((New-DbaReplCreationScriptOptions -Option NonClusteredIndexes).ToString().Split(',').Trim() | Should -Contain 'NonClusteredIndexes') + } + + It "NoDefaults should mean Microsoft.SqlServer.Replication.CreationScriptOptions only contains DisableScripting" { + ((New-DbaReplCreationScriptOptions -NoDefaults).ToString().Split(',').Trim() | Measure-Object).Count | Should -Be 1 + ((New-DbaReplCreationScriptOptions -NoDefaults).ToString().Split(',').Trim() | Should -Contain 'DisableScripting') + } + It "NoDefaults plus option of NonClusteredIndexes should mean Microsoft.SqlServer.Replication.CreationScriptOptions only contains NonClusteredIndexes" { + ((New-DbaReplCreationScriptOptions -NoDefaults -Option NonClusteredIndexes).ToString().Split(',').Trim() | Measure-Object).Count | Should -Be 1 + ((New-DbaReplCreationScriptOptions -NoDefaults -Option NonClusteredIndexes).ToString().Split(',').Trim() | Should -Contain 'NonClusteredIndexes') + } + } + + Context "Add-DbaReplArticle works" { + BeforeAll { + # remove all articles + $null = Get-DbaReplArticle -Database ReplDb | Remove-DbaReplArticle -Confirm:$false + } + + It "Add-DbaReplArticle adds an article to a Transactional publication" { + $pubName = 'TestTrans' + { Add-DbaReplArticle -Database ReplDb -Name $articleName -Publication $pubName -EnableException } | Should -not -throw + $art = Get-DbaReplArticle -Database ReplDb -Name $articleName -Publication $pubName + $art | Should -Not -BeNullOrEmpty + $art.PublicationName | Should -Be $pubName + $art.Name | Should -Be $articleName + } + + It "Add-DbaReplArticle adds an article to a Snapshot publication and specifies create script options" { + $pubname = 'TestTrans' + $cso = New-DbaReplCreationScriptOptions -Options NonClusteredIndexes, Statistics + { Add-DbaReplArticle -Database ReplDb -Name $articleName2 -Publication $pubname -CreationScriptOptions $cso -EnableException } | Should -not -throw + $art = Get-DbaReplArticle -Database ReplDb -Name $articleName2 -Publication $pubName + $art | Should -Not -BeNullOrEmpty + $art.PublicationName | Should -Be $pubName + $art.Name | Should -Be $articleName2 + } + + It "Add-DbaReplArticle adds an article to a Snapshot publication" { + $pubname = 'TestSnap' + { Add-DbaReplArticle -Database ReplDb -Name $articleName -Publication $pubname -EnableException } | Should -not -throw + $art = Get-DbaReplArticle -Database ReplDb -Name $articleName -Publication $pubName + $art | Should -Not -BeNullOrEmpty + $art.PublicationName | Should -Be $pubName + $art.Name | Should -Be $articleName + } + + It "Add-DbaReplArticle adds an article to a Snapshot publication with a filter" { + $pubName = 'TestSnap' + { Add-DbaReplArticle -Database ReplDb -Name $articleName2 -Publication $pubName -Filter "col1 = 'test'" -EnableException } | Should -not -throw + $art = Get-DbaReplArticle -Database ReplDb -Name $articleName2 -Publication $pubName + $art | Should -Not -BeNullOrEmpty + $art.PublicationName | Should -Be $pubName + $art.Name | Should -Be $articleName2 + $art.FilterClause | Should -Be "col1 = 'test'" + } + + It "Add-DbaReplArticle adds an article to a Merge publication" { + $pubname = 'TestMerge' + { Add-DbaReplArticle -Database ReplDb -Name $articleName -Publication $pubname -EnableException } | Should -not -throw + $art = Get-DbaReplArticle -Database ReplDb -Name $articleName -Publication $pubName + $art | Should -Not -BeNullOrEmpty + $art.PublicationName | Should -Be $pubName + $art.Name | Should -Be $articleName + } + } + + Context "Get-DbaReplArticle works" { + BeforeAll { + # we need some articles too get + $articleName = 'ReplicateMe' + $articleName2 = 'ReplicateMeToo' + + # we need some publications too + $pubName = 'TestTrans' + if (-not (Get-DbaReplPublication -Name $pubName -Type Transactional)) { + $null = New-DbaReplPublication -Database ReplDb -Type Transactional -Name $pubName + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubName -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubName -Name $articleName + } + + $pubName = 'TestSnap' + if (-not (Get-DbaReplPublication -Name $pubName -Type Snapshot)) { + $null = New-DbaReplPublication -Database ReplDb -Type Snapshot -Name $pubName + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName2 + } + + $pubName = 'TestMerge' + if (-not (Get-DbaReplPublication -Name $pubName -Type Merge)) { + $null = New-DbaReplPublication -Database ReplDb -Type Merge -Name $pubName + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + } + + It "Get-DbaReplArticle gets all the articles from a server" { + $getArt = Get-DbaReplArticle + $getArt | Should -Not -BeNullOrEmpty + $getArt | ForEach-Object { $_.SqlInstance.name | Should -Be 'mssql1' } + } + + It "Get-DbaReplArticle gets all the articles from a particular database on a server" { + $getArt = Get-DbaReplArticle -Database ReplDb + $getArt | Should -Not -BeNullOrEmpty + $getArt | ForEach-Object { $_.SqlInstance.Name | Should -Be 'mssql1' } + $getArt | ForEach-Object { $_.DatabaseName | Should -Be 'ReplDb' } + } + + It "Get-DbaReplArticle gets all the articles from a specific publication" { + $pubName = 'TestSnap' + $arts = $articleName, $articleName2 + + $getArt = Get-DbaReplArticle -Database ReplDb -Publication $pubName + $getArt.Count | Should -Be 2 + $getArt.Name | Should -Be $arts + $getArt | Foreach-Object { $_.PublicationName | Should -Be $pubName } + } + + It "Get-DbaReplArticle gets a certain article from a specific publication" { + $pubName = 'TestTrans' + + $getArt = Get-DbaReplArticle -Database ReplDb -Publication $pubName -Name $articleName + $getArt.Count | Should -Be 1 + $getArt.Name | Should -Be $ArticleName + $getArt.PublicationName | Should -Be $pubName + } + } + + Context "Remove-DbaReplArticle works" { + BeforeAll { + # we need some articles too remove + $articleName = 'ReplicateMe' + + # we need some publications with articles too + $pubname = 'TestTrans' + if (-not (Get-DbaReplPublication -Name $pubname -Type Transactional)) { + $null = New-DbaReplPublication -Database ReplDb -Type Transactional -Name $pubname + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + + $pubname = 'TestSnap' + if (-not (Get-DbaReplPublication -Name $pubname -Type Snapshot)) { + $null = New-DbaReplPublication -Database ReplDb -Type Snapshot -Name $pubname + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + + $pubname = 'TestMerge' + if (-not (Get-DbaReplPublication -Name $pubname -Type Merge)) { + $null = New-DbaReplPublication -Database ReplDb -Type Merge -Name $pubname + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + + } + + It "Remove-DbaReplArticle removes an article from a Transactional publication" { + $pubname = 'TestTrans' + $Name = "ReplicateMe" + $rm = Remove-DbaReplArticle -Database ReplDb -Publication $pubname -Name $Name + $rm.IsRemoved | Should -Be $true + $rm.Status | Should -Be 'Removed' + $articleName = Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $Name + $articleName | Should -BeNullOrEmpty + } + + It "Remove-DbaReplArticle removes an article from a Snapshot publication" { + $pubname = 'TestSnap' + $Name = "ReplicateMe" + $rm = Remove-DbaReplArticle -Database ReplDb -Publication $pubname -Name $Name + $rm.IsRemoved | Should -Be $true + $rm.Status | Should -Be 'Removed' + $articleName = Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $Name + $articleName | Should -BeNullOrEmpty + } + + It "Remove-DbaReplArticle removes an article from a Merge publication" { + $pubname = 'TestMerge' + $Name = "ReplicateMe" + $rm = Remove-DbaReplArticle -Database ReplDb -Publication $pubname -Name $Name + $rm.IsRemoved | Should -Be $true + $rm.Status | Should -Be 'Removed' + $articleName = Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $Name + $articleName | Should -BeNullOrEmpty + } + } + + + } + + Describe "Article Column commands" -tag art { + BeforeAll { + # if replication is disabled - enable it + if (-not (Get-DbaReplDistributor).IsDistributor) { + Enable-DbaReplDistributor + } + # if publishing is disabled - enable it + if (-not (Get-DbaReplServer).IsPublisher) { + Enable-DbaReplPublishing -PublisherSqlLogin $cred -EnableException + } + $articleName = 'ReplicateMe' + + # we need some publications with articles too + $pubname = 'TestTrans' + if (-not (Get-DbaReplPublication -Name $pubname -Type Transactional)) { + $null = New-DbaReplPublication -Database ReplDb -Type Transactional -Name $pubname + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + + $pubname = 'TestSnap' + if (-not (Get-DbaReplPublication -Name $pubname -Type Snapshot)) { + $null = New-DbaReplPublication -Database ReplDb -Type Snapshot -Name $pubname + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + + $pubname = 'TestMerge' + if (-not (Get-DbaReplPublication -Name $pubname -Type Merge)) { + $null = New-DbaReplPublication -Database ReplDb -Type Merge -Name $pubname + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + } + + Context "Get-DbaReplArticleColumn works" { + It "Gets all column information for a server" { + $cols = Get-DbaReplArticleColumn + $cols | Should -Not -BeNullOrEmpty + $cols.SqlInstance | ForEach-Object { $_.Name | Should -Be 'mssql1' } + } + + It "Gets all column information for specific database on a server" { + $cols = Get-DbaReplArticleColumn -Database ReplDb + $cols | Should -Not -BeNullOrEmpty + $cols.SqlInstance | ForEach-Object { $_.Name | Should -Be 'mssql1' } + $cols.DatabaseName | ForEach-Object { $_ | Should -Be 'ReplDb' } + } + + It "Gets all column information for specific publication on a server" { + $pubname = 'TestTrans' + $cols = Get-DbaReplArticleColumn -Publication $pubname + $cols | Should -Not -BeNullOrEmpty + $cols.SqlInstance | ForEach-Object { $_.Name | Should -Be 'mssql1' } + $cols.PublicationName | ForEach-Object { $_ | Should -Be $pubname } + } + + It "Gets all column information for specific article on a server" { + $pubname = 'TestTrans' + $cols = Get-DbaReplArticleColumn -Publication $pubname -Article $articleName + $cols | Should -Not -BeNullOrEmpty + $cols.SqlInstance | ForEach-Object { $_.Name | Should -Be 'mssql1' } + $cols.ArticleName | ForEach-Object { $_ | Should -Be $articleName } + } + + It "Gets all column information for specific column on a server" { + $pubname = 'TestTrans' + $cols = Get-DbaReplArticleColumn -Publication $pubname -Column 'col1' + $cols | Should -Not -BeNullOrEmpty + $cols.SqlInstance | ForEach-Object { $_.Name | Should -Be 'mssql1' } + $cols.ColumnName | ForEach-Object { $_ | Should -Be 'col1' } + } + } + } + +} diff --git a/tests/gh-actions-repl-3.ps1 b/tests/gh-actions-repl-3.ps1 new file mode 100644 index 0000000000..870d55485c --- /dev/null +++ b/tests/gh-actions-repl-3.ps1 @@ -0,0 +1,282 @@ +Describe "Integration Tests" -Tag "IntegrationTests" { + BeforeAll { + $password = ConvertTo-SecureString "dbatools.IO" -AsPlainText -Force + $cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "sqladmin", $password + + $PSDefaultParameterValues["*:SqlInstance"] = "mssql1" + $PSDefaultParameterValues["*:SqlCredential"] = $cred + $PSDefaultParameterValues["*:SubscriberSqlCredential"] = $cred + $PSDefaultParameterValues["*:Confirm"] = $false + $PSDefaultParameterValues["*:SharedPath"] = "/shared" + $PSDefaultParameterValues["*:WarningAction"] = "SilentlyContinue" + $global:ProgressPreference = "SilentlyContinue" + + Import-Module ./dbatools.psd1 -Force + + $null = New-DbaDatabase -Name ReplDb + $null = Invoke-DbaQuery -Database ReplDb -Query 'CREATE TABLE ReplicateMe ( id int identity (1,1) PRIMARY KEY, col1 varchar(10) ); CREATE TABLE ReplicateMeToo ( id int identity (1,1) PRIMARY KEY, col1 varchar(10) );' + } + + + Describe "Subscription commands" -tag sub { + BeforeAll { + # if replication is disabled - enable it + #if (-not (Get-DbaReplDistributor).IsDistributor) { + # Enable-DbaReplDistributor + #} + ## if publishing is disabled - enable it + #if (-not (Get-DbaReplServer).IsPublisher) { + # Enable-DbaReplPublishing -PublisherSqlLogin $cred -EnableException + #} + #$articleName = 'ReplicateMe' + + <# + # we need some publications with articles too + $pubname = 'TestTrans' + if (-not (Get-DbaReplPublication -Name $pubname -Type Transactional)) { + $null = New-DbaReplPublication -Database ReplDb -Type Transactional -Name $pubname + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + + $pubname = 'TestSnap' + if (-not (Get-DbaReplPublication -Name $pubname -Type Snapshot)) { + $null = New-DbaReplPublication -Database ReplDb -Type Snapshot -Name $pubname + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + + $pubname = 'TestMerge' + if (-not (Get-DbaReplPublication -Name $pubname -Type Merge)) { + $null = New-DbaReplPublication -Database ReplDb -Type Merge -Name $pubname + } + #> + } + + Context "New-DbaReplSubscription works" -skip { + BeforeAll { + if (Get-DbaReplSubscription -SqlInstance mssql1 -PublicationName TestTrans) { + (Get-DbaReplSubscription -SqlInstance mssql1 -PublicationName TestTrans).foreach{ + Remove-DbaReplSubscription -SqlInstance $psitem.SqlInstance -SubscriptionDatabase $psitem.SubscriptionDBName -SubscriberSqlInstance $psitem.SubscriberName -Database $psitem.DatabaseName -PublicationName $psitem.PublicationName -Confirm:$false -EnableException + } + } + if (Get-DbaReplSubscription -SqlInstance mssql1 -PublicationName TestSnap) { + (Get-DbaReplSubscription -SqlInstance mssql1 -PublicationName TestSnap).foreach{ + Remove-DbaReplSubscription -SqlInstance $psitem.SqlInstance -SubscriptionDatabase $psitem.SubscriptionDBName -SubscriberSqlInstance $psitem.SubscriberName -Database $psitem.DatabaseName -PublicationName $psitem.PublicationName -Confirm:$false -EnableException + } + } + Get-DbaReplArticle -Publication TestSnap | Remove-DbaReplArticle -Confirm:$false + } + It "Adds a subscription" { + $pubName = 'TestTrans' + { New-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriberSqlInstance mssql2 -SubscriptionDatabase ReplDbTrans -PublicationName $pubName -Type Push -EnableException } | Should -Not -Throw + + $sub = Get-DbaReplSubscription -SqlInstance mssql1 -PublicationName $pubname + $sub | Should -Not -BeNullOrEmpty + $sub.SqlInstance | ForEach-Object { $_ | Should -Be 'mssql1' } + $sub.SubscriberName | ForEach-Object { $_ | Should -Be 'mssql2' } + $sub.PublicationName | ForEach-Object { $_ | Should -Be $pubname } + $sub.SubscriptionType | ForEach-Object { $_ | Should -Be 'Push' } + } + + It "Adds a pull subscription" { + #TODO: Fix pull subscriptions in New-DbaReplSubscription command + $pubName = 'TestMerge' + { New-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriberSqlInstance mssql2 -SubscriptionDatabase ReplDbSnap -PublicationName $pubName -Type Pull -EnableException } | Should -Not -Throw + + $sub = Get-DbaReplSubscription -SqlInstance mssql1 -PublicationName $pubname + $sub | Should -Not -BeNullOrEmpty + $sub.SqlInstance | ForEach-Object { $_ | Should -Be 'mssql1' } + $sub.SubscriberName | ForEach-Object { $_ | Should -Be 'mssql2' } + $sub.PublicationName | ForEach-Object { $_ | Should -Be $pubname } + $sub.SubscriptionType | ForEach-Object { $_ | Should -Be 'Pull' } + } + + It "Throws an error if there are no articles in the publication" { + $pubName = 'TestSnap' + { New-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriberSqlInstance mssql2 -SubscriptionDatabase ReplDb -PublicationName $pubName -Type Pull -EnableException } | Should -Throw + } + } + + Context "Remove-DbaReplSubscription works" -skip { + BeforeEach { + $pubName = 'TestTrans' + if (-not (Get-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriptionDatabase ReplDb -PublicationName $pubname -Type Push | Where-Object SubscriberName -eq mssql2)) { + New-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriberSqlInstance mssql2 -SubscriptionDatabase ReplDb -PublicationName $pubname -Type Push + } + } + It "Removes a push subscription" { + Get-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriptionDatabase ReplDb -PublicationName $pubname -Type Push | Should -Not -BeNullOrEmpty + { Remove-DbaReplSubscription -SqlInstance mssql1 -SubscriptionDatabase ReplDb -SubscriberSqlInstance mssql2 -Database ReplDb -PublicationName $pubname -EnableException } | Should -Not -Throw + Get-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriptionDatabase ReplDb -PublicationName $pubname -Type Push | Where-Object SubscriberName -eq mssql2 | Should -BeNullOrEmpty + } + It "Removes a pull subscription" -skip { + #TODO: Fix pull subscriptions in New-DbaReplSubscription command + Get-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriptionDatabase ReplDb -PublicationName $pubname -Type Pull | Should -Not -BeNullOrEmpty + { Remove-DbaReplSubscription -SqlInstance mssql1 -SubscriptionDatabase ReplDb -SubscriberSqlInstance mssql2 -Database ReplDb -PublicationName $pubname -EnableException } | Should -Not -Throw + Get-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriptionDatabase ReplDb -PublicationName $pubname -Type Pull | Where-Object SubscriberName -eq mssql2 | Should -BeNullOrEmpty + } + } + + + Context "Get-DbaReplSubscription works" { + BeforeAll { + $pubname = 'TestTrans' + $articleName = 'ReplicateMe' + if (-not (Get-DbaReplPublication -Name $pubname -Type Transactional)) { + $null = New-DbaReplPublication -Database ReplDb -Type Transactional -Name $pubname + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + if (-not (Get-DbaReplSubscription -PublicationName $pubname -Type Push | Where-Object SubscriberName -eq mssql2)) { + New-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriberSqlInstance mssql2 -SubscriptionDatabase ReplDb -PublicationName $pubname -Type Push -enableException + } + + $pubName = 'TestSnap' + if (-not (Get-DbaReplPublication -Name $pubname -Type Snapshot)) { + $null = New-DbaReplPublication -Database ReplDb -Type Snapshot -Name $pubname + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName + } + if (-not (Get-DbaReplSubscription -PublicationName $pubname -Type Push | Where-Object SubscriberName -eq mssql2)) { + New-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb -SubscriberSqlInstance mssql2 -SubscriptionDatabase ReplDb -PublicationName $pubname -Type Push -enableException + } + } + + It "Gets subscriptions" { + $sub = Get-DbaReplSubscription -SqlInstance mssql1 + $sub | Should -Not -BeNullOrEmpty + $sub.SqlInstance | ForEach-Object { $_ | Should -Be 'mssql1' } + } + + It "Gets subscriptions for a particular database" { + $sub = Get-DbaReplSubscription -SqlInstance mssql1 -Database ReplDb + $sub | Should -Not -BeNullOrEmpty + $sub.DatabaseName | ForEach-Object { $_ | Should -Be 'ReplDb' } + } + + It "Gets subscriptions by publication name" { + $sub = Get-DbaReplSubscription -SqlInstance mssql1 -PublicationName TestTrans + $sub | Should -Not -BeNullOrEmpty + $sub.PublicationName | ForEach-Object { $_ | Should -Be 'TestTrans' } + } + + It "Gets subscriptions by type" { + $sub = Get-DbaReplSubscription -SqlInstance mssql1 -Type Push + $sub | Should -Not -BeNullOrEmpty + $sub.SubscriptionType | ForEach-Object { $_ | Should -Be 'Push' } + + $sub = Get-DbaReplSubscription -SqlInstance mssql1 -Type Pull + if($sub) { + $sub.SubscriptionType | ForEach-Object { $_ | Should -Be 'Pull' } + } + } + + It "Gets subscriptions by subscriber name" { + $sub = Get-DbaReplSubscription -SqlInstance mssql1 -SubscriberName mssql2 + $sub | Should -Not -BeNullOrEmpty + $sub.SubscriberName | ForEach-Object { $_ | Should -Be 'mssql2' } + } + + It "Gets subscriptions by subscription database name" { + $sub = Get-DbaReplSubscription -SqlInstance mssql1 -SubscriptionDatabase ReplDbTrans + $sub | Should -Not -BeNullOrEmpty + $sub.SubscriptionDBName | ForEach-Object { $_ | Should -Be 'ReplDbTrans' } + } + } + } + + Describe "Piping" -tag pipe { + BeforeAll { + # if replication is disabled - enable it + if (-not (Get-DbaReplDistributor).IsDistributor) { + Enable-DbaReplDistributor + } + # if publishing is disabled - enable it + if (-not (Get-DbaReplServer).IsPublisher) { + Enable-DbaReplPublishing -PublisherSqlLogin $cred -EnableException + } + + # we need some articles too get + $articleName = 'ReplicateMe' + $articleName2 = 'ReplicateMeToo' + + # we need some publications too + $pubName = 'TestTrans' + if (-not (Get-DbaReplPublication -Name $pubName -Type Transactional)) { + $null = New-DbaReplPublication -Database ReplDb -Type Transactional -Name $pubName -EnableException + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubName -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubName -Name $articleName -EnableException + } + + $pubName = 'TestSnap' + if (-not (Get-DbaReplPublication -Name $pubName -Type Snapshot)) { + $null = New-DbaReplPublication -Database ReplDb -Type Snapshot -Name $pubName -EnableException + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName -EnableException + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName2 -EnableException + } + + $pubName = 'TestMerge' + if (-not (Get-DbaReplPublication -Name $pubName -Type Merge)) { + $null = New-DbaReplPublication -Database ReplDb -Type Merge -Name $pubName -EnableException + } + if (-not (Get-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName)) { + $null = Add-DbaReplArticle -Database ReplDb -Publication $pubname -Name $articleName -EnableException + } + # piping doesn't work well if there are PSDefaultParameterValues set + $PSDefaultParameterValues = $null + } + + Context "Get-DbaReplPublisher works with piping" { + It "gets a publisher using piping" { + (Connect-DbaInstance -SqlInstance 'mssql1' -SqlCredential $cred | Get-DbaReplPublisher).PublisherType | Should -Be "MSSQLSERVER" + } + } + + Context "Get-DbaReplPublication works with piping" { + It "works with piping" { + Connect-DbaInstance -SqlInstance 'mssql1' -SqlCredential $cred | Get-DbaReplPublication | Should -Not -BeNullOrEmpty + } + } + + Context "Get-DbaReplDistributor works with piping" { + It "can pipe a sql server object to it" { + Connect-DbaInstance -SqlInstance 'mssql1' -SqlCredential $cred | Get-DbaReplDistributor | Should -Not -BeNullOrEmpty + } + } + + Context "Get-DbaReplArticle works with piping" { + It "Piping from Connect-DbaInstance to works" { + Connect-DbaInstance -SqlInstance 'mssql1' -SqlCredential $cred -Database ReplDb | Get-DbaReplArticle | Should -Not -BeNullOrEmpty + } + } + + Context "Remove-DbaReplArticle works with piping" { + It "Remove-DbaReplArticle removes an article from a Transactional publication" { + $pubName = 'TestTrans' + $Name = "ReplicateMe" + + $rm = Get-DbaReplArticle -SqlInstance 'mssql1' -SqlCredential $cred -Database ReplDb -Publication $pubName -Name $Name | Remove-DbaReplArticle -Confirm:$false + $rm.IsRemoved | ForEach-Object { $_ | Should -Be $true } + $rm.Status | ForEach-Object { $_ | Should -Be 'Removed' } + $articleName = Get-DbaReplArticle -SqlInstance 'mssql1' -SqlCredential $cred -Database ReplDb -Publication $pubName -Name $Name + $articleName | Should -BeNullOrEmpty + } + } + + Context "Remove-DbaReplPublication works with piping" { + It "Remove-DbaReplPublication removes a publication using piping" { + $name = 'TestMerge' + { Get-DbaReplPublication -SqlInstance 'mssql1' -SqlCredential $cred -Name $name -EnableException | Remove-DbaReplPublication -Confirm:$false -EnableException } | Should -Not -Throw + (Get-DbaReplPublication -SqlInstance 'mssql1' -SqlCredential $cred -Name $name -EnableException) | Should -BeNullOrEmpty + } + } + } +}