3

I'm experiencing odd behavior with the PowerShell SQLSERVER provider and the SQLRegistration\Central Management Server Group directory. Below are the behaviors. I would like to know how the SQLSERVER provider knows what CMS's are registered so that I can understand why it doesn't seem to notice the registration when done in SSMS 2014 (see below for detailed behavior). Also, why my connection fails when I know the instance is online.

Setup
Machine

  • localhost

Instances

  • localhost\SQL2012
  • localhost\SQL2014
  • localhost\SQL2014_1

Behavior 1
Using SSMS 2014, I registered localhost\SQL2012 as a CMS. Executing this code returns no items.

PS SQLSERVER:\SQLRegistration\Central Management Server Group> dir

Open and closing the PowerShell console and SSMS does not change the results. If I open SSMS 2012 and register localhost\SQL2012 as a CMS and re-run the above command, I see localhost\SQL2012 registered as expected.

Behavior 2
After getting the server to list successfully...

Directory: Microsoft.SqlServer.Management.PSProvider\SqlServer::SQLSERVER:\SQLRegistration\Central Management Server Group

Mode Name


  • localhost\SQL2012

And running the below command to try and navigate to my registered server groups fails with the below error, even though the instance is online and available.

PS SQLSERVER:\SQLRegistration\Central Management Server Group> Set-Location "localhost\SQL2012\"  

Set-Location : Cannot find path 'SQLSERVER:\SQLRegistration\Central Management Server Group\localhost\SQL2012\' because it does not exist. At line:1 char:1 + Set-Location "localhost\SQL2012\" + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (SQLSERVER:\SQLR...alhost\SQL2012:String) [Set-Location], ItemNotFoundE xception + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand

Update 1
SMO seems to work fine. The below code successfully returns my server groups.

#Load SMO assemblies
$CentralManagementServer = "localhost\sql2012"
$MS='Microsoft.SQLServer'
@('.SMO', '.Management.RegisteredServers', '.ConnectionInfo') |
     foreach-object {if ([System.Reflection.Assembly]::LoadWithPartialName("$MS$_") -eq $null) {"missing SMO component $MS$_"}}

$connectionString = "Data Source=$CentralManagementServer;Initial Catalog=master;Integrated Security=SSPI;"
$sqlConnection = new-object System.Data.SqlClient.SqlConnection($connectionString)
$conn = new-object Microsoft.SqlServer.Management.Common.ServerConnection($sqlConnection)
$CentralManagementServerStore = new-object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($conn)

$CentralManagementServerStore.ServerGroups[ "DatabaseEngineServerGroup" ].ServerGroups
1

Behavior 1 I can expect because there are some features that may or may not work as expected when using higher version of SSMS on lower version of instance. There could be things in the background that changed just slightly from 2014 to 2012 that you will probably never find any documentation on. I would actually create a connect item on this if you can recreate this scenario on another machine every time.

Now, Behavior 2 failed the first time I tried, but then succeeded every time after that, even after opening/closing windows. However if you are trying this using just PowerShell.exe it is to be expected that some things will not function the exact same way when you are dealing with the SQLPS provider. If you tried doing the same command within SQLPS.exe I would expect it will work every time, it does for me at least. Now I am not using SQL Server 2014, only 2012 is loaded on my machine right now.

You can see the fact of how different PowerShell.exe and SQLPS.exe are by comparing the assemblies loaded in each one:

enter image description here

  • Unfortunately SQLPS.exe yielded the same result. – SQL Hammer Sep 30 '14 at 0:31
  • I might try a repair of management tools, or see if it is patched. Just to refresh everything. Does it happen on another machine? – Shawn Melton Sep 30 '14 at 0:35
2

Shawn's explanation about discrepancies between SQL Server version seems on target. For this reason, I believe the SMO is a more stable method. I'm going forward using the below code.

Import-Module SQLPS -DisableNameChecking

Function Parse-ServerGroup($serverGroup)
{
    $results = $serverGroup.RegisteredServers;
    foreach($group in $serverGroup.ServerGroups)
    {
        $results += Parse-ServerGroup -serverGroup $group;
    }
    return $results;
}

Function Get-ServerList ([string]$cmsName, [string]$serverGroup, [switch]$recurse)
{

    $connectionString = "data source=$cmsName;initial catalog=master;integrated security=sspi;"
$sqlConnection = New-Object ("System.Data.SqlClient.SqlConnection") $connectionstring
    $conn = New-Object ("Microsoft.SQLServer.Management.common.serverconnection") $sqlconnection
    $cmsStore = New-Object Microsoft.SqlServer.Management.RegisteredServers.RegisteredServersStore($conn)
    $cmsRootGroup = $cmsStore.ServerGroups["DatabaseEngineServerGroup"].ServerGroups[$serverGroup]

    if($recurse)
    {
        return Parse-ServerGroup -serverGroup $cmsRootGroup | select ServerName
    }
    else
    {
        return $cmsRootGroup.RegisteredServers | select ServerName
    }
}

Get-ServerList -cmsName "localhost\SQL2012" -serverGroup "Production" -recurse | Format-Table

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.