From ac6fc4857fad3658a39946c461ac4f6e3a7a905c Mon Sep 17 00:00:00 2001 From: Chrissy LeMaire Date: Wed, 12 Jun 2019 20:16:08 +0000 Subject: [PATCH] Add additional support for Local Server Groups (#5734) * adjust for local * add support for local reg * works for local and remote # ADD IN ability to pipe a server! * works locally and remotely * support local instances * updated docs and examples * use that insteasd, need to do it for all commands * cleanup * remove tab wtf * add proper params * this may fix Collection was modified issue --- functions/Add-DbaRegServer.ps1 | 128 +++++++++++++++++++------ functions/Add-DbaRegServerGroup.ps1 | 33 ++++--- functions/Get-DbaRegServerGroup.ps1 | 28 ++++-- functions/Get-DbaRegServerStore.ps1 | 14 ++- functions/Move-DbaRegServer.ps1 | 4 +- functions/Move-DbaRegServerGroup.ps1 | 4 +- functions/Remove-DbaRegServer.ps1 | 27 +++++- functions/Remove-DbaRegServerGroup.ps1 | 36 +++++-- tests/Add-DbaRegServer.Tests.ps1 | 2 +- 9 files changed, 209 insertions(+), 67 deletions(-) diff --git a/functions/Add-DbaRegServer.ps1 b/functions/Add-DbaRegServer.ps1 index 52a7502cb3..8abf862ea1 100644 --- a/functions/Add-DbaRegServer.ps1 +++ b/functions/Add-DbaRegServer.ps1 @@ -1,14 +1,14 @@ function Add-DbaRegServer { <# .SYNOPSIS - Adds registered servers to SQL Server Central Management Server (CMS) + Adds registered servers to SQL Server Central Management Server (CMS) or Local Server Groups .DESCRIPTION - Adds registered servers to SQL Server Central Management Server (CMS). If you need more flexiblity, look into Import-DbaRegServer which + Adds registered servers to SQL Server Central Management Server (CMS) or Local Server Groups. If you need more flexiblity, look into Import-DbaRegServer which accepts multiple kinds of input and allows you to add reg servers from different CMSes. .PARAMETER SqlInstance - The target SQL Server instance + The target SQL Server instance if a CMS is used .PARAMETER SqlCredential Login to the target instance using alternative credentials. Windows and SQL Authentication supported. Accepts credential objects (Get-Credential) @@ -17,7 +17,7 @@ function Add-DbaRegServer { Server Name is the actual SQL instance name (labeled Server Name) .PARAMETER Name - Name is basically the nickname in SSMS CMS interface (labeled Registered Server Name) + Name is basically the nickname in SSMS Registered Server interface (labeled Registered Server Name) .PARAMETER Description Adds a description for the registered server @@ -25,6 +25,21 @@ function Add-DbaRegServer { .PARAMETER Group Adds the registered server to a specific group. + .PARAMETER ActiveDirectoryTenant + Active Directory Tenant + + .PARAMETER ActiveDirectoryUserId + Active Directory User id + + .PARAMETER ConnectionString + SQL Server connection string + + .PARAMETER OtherParams + Additional parameters to append to the connection string + + .PARAMETER ServerObject + SMO Server Objects (from Connect-DbaInstance) + .PARAMETER InputObject Allows the piping of a registered server group @@ -57,6 +72,11 @@ function Add-DbaRegServer { Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, the name "sql01" will be visible. + .EXAMPLE + PS C:\> Add-DbaRegServer -ServerName sql01 + + Creates a registered server in Local Server Groups which points to the SQL Server, sql01. When scrolling in Registered Servers, the name "sql01" will be visible. + .EXAMPLE PS C:\> Add-DbaRegServer -SqlInstance sql2008 -ServerName sql01 -Name "The 2008 Clustered Instance" -Description "HR's Dedicated SharePoint instance" @@ -69,35 +89,53 @@ function Add-DbaRegServer { Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, the name "sql01" will be visible within the Seattle group which is in the hr group. .EXAMPLE - PS C:\> Get-DbaRegServerGroup -SqlInstance sql2008 -Group hr\Seattle | Add-DbaRegServer -ServerName sql01111 - - Creates a registered server on sql2008's CMS which points to the SQL Server, sql01. When scrolling in CMS, the name "sql01" will be visible within the Seattle group which is in the hr group. + PS C:\> Connect-DbaInstance -SqlInstance dockersql1 -SqlCredential sqladmin | Add-DbaRegServer -ServerName mydockerjam + Creates a registered server called "mydockerjam" in Local Server Groups that uses SQL authentication and points to the server dockersql1. #> [CmdletBinding(SupportsShouldProcess)] param ( [DbaInstanceParameter[]]$SqlInstance, [PSCredential]$SqlCredential, - [parameter(Mandatory)] [string]$ServerName, [string]$Name = $ServerName, [string]$Description, [object]$Group, + [string]$ActiveDirectoryTenant, + [string]$ActiveDirectoryUserId, + [string]$ConnectionString, + [string]$OtherParams, [parameter(ValueFromPipeline)] [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup[]]$InputObject, + [parameter(ValueFromPipeline)] + [Microsoft.SqlServer.Management.Smo.Server[]]$ServerObject, [switch]$EnableException ) process { - if (-not $InputObject -and -not $SqlInstance) { - Stop-Function -Message "You must either pipe in a registered server group or specify a sqlinstance" + # double check in case a null name was bound + if (-not $PSBoundParameters.ServerName -and -not $PSBoundParameters.ServerObject) { + Stop-Function -Message "You must specify either ServerName or ServerObject" return } - - # double check in case a null name was bound if (-not $Name) { $Name = $ServerName } + if (-not $SqlInstance -and -not $InputObject) { + Write-Message -Level Verbose -Message "Parsing local" + if (($Group)) { + if ($Group -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) { + $InputObject += Get-DbaRegServerGroup -Group $Group.Name + } else { + Write-Message -Level Verbose -Message "String group provided" + $InputObject += Get-DbaRegServerGroup -Group $Group + } + } else { + Write-Message -Level Verbose -Message "No group passed, getting root" + $InputObject += Get-DbaRegServerGroup -Id 1 + } + } + foreach ($instance in $SqlInstance) { if (($Group)) { if ($Group -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) { @@ -115,24 +153,56 @@ function Add-DbaRegServer { } foreach ($reggroup in $InputObject) { - $parentserver = Get-RegServerParent -InputObject $reggroup - - if ($null -eq $parentserver) { - Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue + if ($reggroup.Source -eq "Azure Data Studio") { + Stop-Function -Message "You cannot use dbatools to remove or add registered servers in Azure Data Studio" -Continue } - - $server = $reggroup.ParentServer - - if ($Pscmdlet.ShouldProcess($parentserver.SqlInstance, "Adding $ServerName")) { - try { - $newserver = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($reggroup, $Name) - $newserver.ServerName = $ServerName - $newserver.Description = $Description - $newserver.Create() - - Get-DbaRegServer -SqlInstance $server -Name $Name -ServerName $ServerName - } catch { - Stop-Function -Message "Failed to add $ServerName on $($parentserver.SqlInstance)" -ErrorRecord $_ -Continue + if ($reggroup.ID) { + $target = $reggroup.ParentServer.SqlInstance + } else { + $target = "Local Registered Servers" + } + if ($Pscmdlet.ShouldProcess($target, "Adding $ServerName")) { + + if ($ServerObject) { + foreach ($server in $ServerObject) { + if (-not $PSBoundParameters.Name) { + $Name = $server.Name + } + if (-not $PSBoundParameters.ServerName) { + $ServerName = $server.Name + } + try { + $newserver = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($reggroup, $Name) + $newserver.ServerName = $ServerName + $newserver.Description = $Description + $newserver.ConnectionString = $server.ConnectionContext.ConnectionString + $newserver.SecureConnectionString = $server.ConnectionContext.SecureConnectionString + $newserver.ActiveDirectoryTenant = $ActiveDirectoryTenant + $newserver.ActiveDirectoryUserId = $ActiveDirectoryUserId + $newserver.OtherParams = $OtherParams + $newserver.CredentialPersistenceType = "PersistLoginNameAndPassword" + $newserver.Create() + + Get-DbaRegServer -SqlInstance $reggroup.ParentServer -Name $Name -ServerName $ServerName + } catch { + Stop-Function -Message "Failed to add $ServerName on $target" -ErrorRecord $_ -Continue + } + } + } else { + try { + $newserver = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServer($reggroup, $Name) + $newserver.ServerName = $ServerName + $newserver.Description = $Description + $newserver.ConnectionString = $ConnectionString + $newserver.ActiveDirectoryTenant = $ActiveDirectoryTenant + $newserver.ActiveDirectoryUserId = $ActiveDirectoryUserId + $newserver.OtherParams = $OtherParams + $newserver.Create() + + Get-DbaRegServer -SqlInstance $reggroup.ParentServer -Name $Name -ServerName $ServerName + } catch { + Stop-Function -Message "Failed to add $ServerName on $target" -ErrorRecord $_ -Continue + } } } } diff --git a/functions/Add-DbaRegServerGroup.ps1 b/functions/Add-DbaRegServerGroup.ps1 index 4639620d82..245c5d5b40 100644 --- a/functions/Add-DbaRegServerGroup.ps1 +++ b/functions/Add-DbaRegServerGroup.ps1 @@ -77,10 +77,6 @@ function Add-DbaRegServerGroup { [switch]$EnableException ) process { - if (-not $InputObject -and -not $SqlInstance) { - Stop-Function -Message "You must either pipe in a registered server group or specify a sqlinstance" - return - } foreach ($instance in $SqlInstance) { if ((Test-Bound -ParameterName Group)) { $InputObject += Get-DbaRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group @@ -89,24 +85,37 @@ function Add-DbaRegServerGroup { } } + if (-not $SqlInstance -and -not $InputObject) { + if ((Test-Bound -ParameterName Group)) { + $InputObject += Get-DbaRegServerGroup -Group $Group + } else { + $InputObject += Get-DbaRegServerGroup -Id 1 + } + } + foreach ($reggroup in $InputObject) { - $parentserver = Get-RegServerParent -InputObject $reggroup - $server = $reggroup.ParentServer + if ($reggroup.Source -eq "Azure Data Studio") { + Stop-Function -Message "You cannot use dbatools to remove or add registered server groups in Azure Data Studio" -Continue + } - if ($null -eq $parentserver) { - Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue + if ($reggroup.ID) { + $target = $reggroup.Parent + } else { + $target = "Local Registered Server Groups" } - if ($Pscmdlet.ShouldProcess($parentserver.SqlInstance, "Adding $Name")) { + if ($Pscmdlet.ShouldProcess($target, "Adding $Name")) { try { $newgroup = New-Object Microsoft.SqlServer.Management.RegisteredServers.ServerGroup($reggroup, $Name) $newgroup.Description = $Description $newgroup.Create() - Get-DbaRegServerGroup -SqlInstance $server -Group (Get-RegServerGroupReverseParse -object $newgroup) - $parentserver.ServerConnection.Disconnect() + Get-DbaRegServerGroup -SqlInstance $reggroup.ParentServer -Group (Get-RegServerGroupReverseParse -object $newgroup) + if ($parentserver.ServerConnection) { + $parentserver.ServerConnection.Disconnect() + } } catch { - Stop-Function -Message "Failed to add $reggroup on $server" -ErrorRecord $_ -Continue + Stop-Function -Message "Failed to add $reggroup" -ErrorRecord $_ -Continue } } } diff --git a/functions/Get-DbaRegServerGroup.ps1 b/functions/Get-DbaRegServerGroup.ps1 index e50eb66346..974044205a 100644 --- a/functions/Get-DbaRegServerGroup.ps1 +++ b/functions/Get-DbaRegServerGroup.ps1 @@ -62,7 +62,7 @@ function Get-DbaRegServerGroup { #> [CmdletBinding()] param ( - [parameter(Mandatory, ValueFromPipeline)] + [parameter(ValueFromPipeline)] [DbaInstanceParameter[]]$SqlInstance, [PSCredential]$SqlCredential, [object[]]$Group, @@ -70,17 +70,24 @@ function Get-DbaRegServerGroup { [int[]]$Id, [switch]$EnableException ) + begin { + $serverstores = $groups = @() + } process { foreach ($instance in $SqlInstance) { try { - $serverstore = Get-DbaRegServerStore -SqlInstance $instance -SqlCredential $SqlCredential -EnableException + $serverstores += Get-DbaRegServerStore -SqlInstance $instance -SqlCredential $SqlCredential -EnableException } catch { Stop-Function -Message "Cannot access Central Management Server '$instance'" -ErrorRecord $_ -Continue } + } - $groups = @() + if (-not $SqlInstance) { + $serverstores += Get-DbaRegServerStore + } - if ($group) { + foreach ($serverstore in $serverstores) { + if ($Group) { foreach ($currentgroup in $Group) { Write-Message -Level Verbose -Message "Processing $currentgroup" if ($currentgroup -is [Microsoft.SqlServer.Management.RegisteredServers.ServerGroup]) { @@ -137,19 +144,26 @@ function Get-DbaRegServerGroup { if ($Id) { Write-Message -Level Verbose -Message "Filtering for id $Id. Id 1 = default." if ($Id -eq 1) { - $groups = $serverstore.DatabaseEngineServerGroup | Where-Object Id -in $Id + $groups = $serverstore.DatabaseEngineServerGroup } else { $groups = $serverstore.DatabaseEngineServerGroup.GetDescendantRegisteredServers().Parent | Where-Object Id -in $Id } } - $serverstore.ServerConnection.Disconnect() + if ($serverstore.ServerConnection) { + $serverstore.ServerConnection.Disconnect() + } + foreach ($groupobject in $groups) { Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name ComputerName -value $serverstore.ComputerName Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name InstanceName -value $serverstore.InstanceName Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name SqlInstance -value $serverstore.SqlInstance Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name ParentServer -value $serverstore.ParentServer - Select-DefaultView -InputObject $groupobject -Property ComputerName, InstanceName, SqlInstance, Name, DisplayName, Description, ServerGroups, RegisteredServers + if ($groupobject.ComputerName) { + Select-DefaultView -InputObject $groupobject -Property ComputerName, InstanceName, SqlInstance, Name, DisplayName, Description, ServerGroups, RegisteredServers + } else { + Select-DefaultView -InputObject $groupobject -Property Name, DisplayName, Description, ServerGroups, RegisteredServers + } } } } diff --git a/functions/Get-DbaRegServerStore.ps1 b/functions/Get-DbaRegServerStore.ps1 index 832404712d..ece18600e8 100644 --- a/functions/Get-DbaRegServerStore.ps1 +++ b/functions/Get-DbaRegServerStore.ps1 @@ -42,7 +42,7 @@ function Get-DbaRegServerStore { #> [CmdletBinding()] param ( - [parameter(Mandatory, ValueFromPipeline)] + [parameter(ValueFromPipeline)] [DbaInstanceParameter[]]$SqlInstance, [PSCredential]$SqlCredential, [switch]$EnableException @@ -67,5 +67,17 @@ function Get-DbaRegServerStore { Add-Member -Force -InputObject $store -MemberType NoteProperty -Name ParentServer -value $server Select-DefaultView -InputObject $store -ExcludeProperty ServerConnection, DomainInstanceName, DomainName, Urn, Properties, Metadata, Parent, ConnectionContext, PropertyMetadataChanged, PropertyChanged, ParentServer } + + # Magic courtesy of Mathias Jessen and David Shifflet + if (-not $PSBoundParameters.SqlInstance) { + $file = [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore]::LocalFileStore.DomainInstanceName + if ($file) { + if ((Test-Path -Path $file)) { + $class = [Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore] + $initMethod = $class.GetMethod('InitChildObjects', [Reflection.BindingFlags]'Static,NonPublic') + $initMethod.Invoke($null, @($file)) + } + } + } } } \ No newline at end of file diff --git a/functions/Move-DbaRegServer.ps1 b/functions/Move-DbaRegServer.ps1 index d1835d0924..9c5e794f05 100644 --- a/functions/Move-DbaRegServer.ps1 +++ b/functions/Move-DbaRegServer.ps1 @@ -1,10 +1,10 @@ function Move-DbaRegServer { <# .SYNOPSIS - Moves registered servers around SQL Server Central Management Server (CMS) + Moves registered servers around SQL Server Central Management Server (CMS). Local Registered Servers not currently supported. .DESCRIPTION - Moves registered servers around SQL Server Central Management Server (CMS) + Moves registered servers around SQL Server Central Management Server (CMS). Local Registered Servers not currently supported. .PARAMETER SqlInstance The target SQL Server instance or instances. diff --git a/functions/Move-DbaRegServerGroup.ps1 b/functions/Move-DbaRegServerGroup.ps1 index 54ae447e11..6e3b159566 100644 --- a/functions/Move-DbaRegServerGroup.ps1 +++ b/functions/Move-DbaRegServerGroup.ps1 @@ -1,10 +1,10 @@ function Move-DbaRegServerGroup { <# .SYNOPSIS - Moves registered server groups around SQL Server Central Management Server (CMS). + Moves registered server groups around SQL Server Central Management Server (CMS). Local Registered Server Groups not currently supported. .DESCRIPTION - Moves registered server groups around SQL Server Central Management Server (CMS). + Moves registered server groups around SQL Server Central Management Server (CMS). Local Registered Server Groups not currently supported. .PARAMETER SqlInstance The target SQL Server instance or instances. diff --git a/functions/Remove-DbaRegServer.ps1 b/functions/Remove-DbaRegServer.ps1 index 14308fcbcb..9014dcf6cb 100644 --- a/functions/Remove-DbaRegServer.ps1 +++ b/functions/Remove-DbaRegServer.ps1 @@ -82,12 +82,29 @@ function Remove-DbaRegServer { $InputObject += Get-DbaRegServer -SqlInstance $instance -SqlCredential $SqlCredential -Group $Group -ExcludeGroup $ExcludeGroup -Name $Name -ServerName $ServerName } + if (-not $SqlInstance -and -not $InputObject) { + $InputObject += Get-DbaRegServer -Group $Group -ExcludeGroup $ExcludeGroup -Name $Name -ServerName $ServerName + } + foreach ($regserver in $InputObject) { - $server = $regserver.Parent + if ($regserver.Source -eq "Azure Data Studio") { + Stop-Function -Message "You cannot use dbatools to remove or add registered servers in Azure Data Studio" -Continue + } - if ($Pscmdlet.ShouldProcess($regserver.Parent, "Removing $regserver")) { + if ($regserver.ID) { + $defaults = "ComputerName", "InstanceName", "SqlInstance", "Name", "ServerName", "Status" + $target = $regserver.Parent + } else { + $defaults = "Name", "ServerName", "Status" + $target = "Local Registered Server Groups" + } + + if ($Pscmdlet.ShouldProcess($target, "Removing $regserver")) { $null = $regserver.Drop() - Disconnect-RegServer -Server $server + + if ($regserver.ID) { + Disconnect-RegServer -Server $regserver.Parent + } try { [pscustomobject]@{ @@ -97,9 +114,9 @@ function Remove-DbaRegServer { Name = $regserver.Name ServerName = $regserver.ServerName Status = "Dropped" - } + } | Select-DefaultView -Property $defaults } catch { - Stop-Function -Message "Failed to drop $regserver on $server" -ErrorRecord $_ -Continue + Stop-Function -Message "Failed to drop $regserver on $target" -ErrorRecord $_ -Continue } } } diff --git a/functions/Remove-DbaRegServerGroup.ps1 b/functions/Remove-DbaRegServerGroup.ps1 index a74e14bd99..a3cf447619 100644 --- a/functions/Remove-DbaRegServerGroup.ps1 +++ b/functions/Remove-DbaRegServerGroup.ps1 @@ -68,16 +68,36 @@ function Remove-DbaRegServerGroup { $InputObject += Get-DbaRegServerGroup -SqlInstance $instance -SqlCredential $SqlCredential -Group $Name } - foreach ($regservergroup in $InputObject) { - $parentserver = Get-RegServerParent -InputObject $regservergroup + if (-not $SqlInstance -and -not $InputObject) { + $InputObject += Get-DbaRegServerGroup -Group $Name + } - if ($null -eq $parentserver) { - Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue + foreach ($regservergroup in $InputObject) { + if ($regservergroup.ID) { + $parentserver = Get-RegServerParent -InputObject $regservergroup + $target = $parentserver.DomainInstanceName + if ($null -eq $parentserver) { + Stop-Function -Message "Something went wrong and it's hard to explain, sorry. This basically shouldn't happen." -Continue + } + $defaults = "ComputerName", "InstanceName", "SqlInstance", "Name", "Status" + } else { + $target = "Local Registered Servers" + $defaults = "Name", "Status" } - if ($Pscmdlet.ShouldProcess($parentserver.DomainInstanceName, "Removing $($regservergroup.Name) CMS Group")) { - $null = $parentserver.ServerConnection.ExecuteNonQuery($regservergroup.ScriptDrop().GetScript()) - $parentserver.ServerConnection.Disconnect() + if ($Pscmdlet.ShouldProcess($target, "Removing $($regservergroup.Name) Group")) { + if ($regservergroup.Source -eq "Azure Data Studio") { + Stop-Function -Message "You cannot use dbatools to remove or add registered server groups in Azure Data Studio" -Continue + } + + # try to avoid 'Collection was modified after the enumerator was instantiated' issue + if ($regservergroup.ID) { + $null = $parentserver.ServerConnection.ExecuteNonQuery($regservergroup.ScriptDrop().GetScript()) + $parentserver.ServerConnection.Disconnect() + } else { + $regservergroup.Drop() + } + try { [pscustomobject]@{ ComputerName = $parentserver.ComputerName @@ -85,7 +105,7 @@ function Remove-DbaRegServerGroup { SqlInstance = $parentserver.SqlInstance Name = $regservergroup.Name Status = "Dropped" - } + } | Select-DefaultView -Property $defaults } catch { Stop-Function -Message "Failed to drop $regservergroup on $parentserver" -ErrorRecord $_ -Continue } diff --git a/tests/Add-DbaRegServer.Tests.ps1 b/tests/Add-DbaRegServer.Tests.ps1 index aac0fb7b10..367fc9149d 100644 --- a/tests/Add-DbaRegServer.Tests.ps1 +++ b/tests/Add-DbaRegServer.Tests.ps1 @@ -5,7 +5,7 @@ Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan Describe "$CommandName Unit Tests" -Tags "UnitTests" { Context "Validate parameters" { [object[]]$params = (Get-Command $CommandName).Parameters.Keys | Where-Object {$_ -notin ('whatif', 'confirm')} - [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'ServerName', 'Name', 'Description', 'Group', 'InputObject', 'EnableException' + [object[]]$knownParameters = 'SqlInstance', 'SqlCredential', 'ServerName', 'Name', 'Description', 'Group', 'ActiveDirectoryTenant', 'ActiveDirectoryUserId', 'ConnectionString', 'OtherParams', 'InputObject', 'ServerObject', '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