Les connexions ne sont pas synchronisées entre les groupes de disponibilité

13

Nous avons 2 serveurs dans un groupe AlwaysOn.

Bien que les comptes d'utilisateurs dans chaque base de données synchronisée existent sur les deux serveurs, les connexions au niveau de l'instance de base de données n'existent que sur l'un des serveurs. C'est-à-dire DBINSTANCE-> Sécurité-> Les connexions sont manquantes sur un serveur.

Par conséquent, en cas de basculement, j'obtiens des échecs de connexion sur le deuxième serveur (qui n'a pas les connexions de niveau d'instance correspondantes).

Comment surmonter ce problème? Étais-je censé configurer le compte utilisateur d'une manière spéciale?

John
la source
J'ai commencé à regarder celui - ci a récemment aussi, je roulais mon propre et je décris actuellement dans ma marque nouveau blog (surtout pour mon propre bénéfice que je vais de l' avant et oublier les choses , mais si son aide à d' autres d'autant mieux) la vie -and-dev.blogspot.co.uk/2015/04/… ceci et le couple suivant afficheront le suivi du basculement AlwaysOn et la synchronisation des tâches / utilisateurs
Vous pouvez utiliser le plugin SSMS de sqlskills Synchroniser les connexions et les travaux du groupe de disponibilité
Kin Shah

Réponses:

15

Je crois comprendre que si vous n'utilisez pas de bases de données contenues , vous devrez vous assurer que les connexions sont créées manuellement sur d'autres instances.

Quelque chose comme ce script de SQLSoldier , initialement publié en tant que transfert de connexions à un miroir de base de données , devrait faire l'affaire.

Mark Storey-Smith
la source
J'ai hâte de convertir plusieurs de nos bases de données de production '05 au nouveau format contenu une fois que nous passerons à '12 dans la semaine ou les deux prochaines. En attendant, j'utilise les outils d'Idera pour copier les connexions, les travaux, etc., sur l'instance secondaire.
Max Vernon
Mark est correct, vous devrez utiliser des bases de données contenues ou copier manuellement les connexions à toutes les instances du cluster en utilisant le même SID sur toutes les instances.
mrdenny
Gardez à l'esprit que je pose des questions sur AlwaysOn, pas sur la mise en miroir. J'ai essayé le script, mais la table sys.servers contient uniquement le serveur local, donc j'obtiens l'erreur suivante lorsque j'exécute la procédure stockée: Msg 7202, Level 11, State 2, Line 1 Could not find server 'otherserver' in sys.servers. Verify that the correct server name was specified. If necessary, execute the stored procedure sp_addlinkedserver to add the server to sys.servers.
John
Je l'ai compris et publié une nouvelle réponse appropriée pour AlwaysOn.
John
@JohnHughes Ce script est approprié pour AlwaysOn. Cependant, comme indiqué par le message d'erreur, vous devrez créer un serveur lié entre les instances.
Mark Storey-Smith
7

Vous devez soit utiliser une base de données contenue, soit recréer les utilisateurs sur les autres serveurs avec le même hachage de mot de passe et le même SID.

Un script pour ce faire est fourni par Microsoft: Comment transférer des connexions et des mots de passe entre des instances de SQL Server

La solution de Mark était partiellement correcte, mais sa solution recommandée était pour les bases de données en miroir, par opposition à AlwaysOn, ce que demandent les questions.

John
la source
2
En quoi la solution pour les bases de données en miroir est-elle différente de la solution pour les groupes de disponibilité (remarque: AlwaysOn est une étiquette marketing pour un ensemble de technologies, pas une fonctionnalité)? Le titre d'un article de blog ne change pas le fait qu'il restaure les connexions manquantes - ce qui, en soi, n'a rien à voir avec l'utilisation de la mise en miroir, des groupes de disponibilité, de l'envoi de journaux, de la sauvegarde / restauration manuelle, etc.
Aaron Bertrand
Malheureusement, je ne peux pas toujours utiliser les comptes AD. Nous avons du mal à comparer les mots de passe. Ne sommes-nous pas en droit de supposer que si les mots de passe ne correspondent pas, le basculement entre les serveurs de répliques échouera?
1

Je réponds au message après une longue période, mais cela peut aider quelqu'un d'autre avec le même problème. PowerShell peut être utilisé pour copier les connexions du réplica principal vers les réplicas secondaires. Les détails peuvent être trouvés ici https://maq.guru/synchronizing-sql-server-logins-in-an-always-on-available-group/ .

Divulgation complète: je suis propriétaire du site ci-dessus.

Le script PowerShell:

$Conn=New-Object System.Data.SqlClient.SQLConnection
$QueryTimeout = 120
$ConnectionTimeout = 30

###########################################################
# Execute Query function 
###########################################################
Function executequery($Query, $QueryTimeout, $ServerName)
{
    $Datatable = New-Object System.Data.DataTable
    $ConnectionString = "Server={0};Database={1};Integrated Security=True;Connect Timeout={2}" -f $ServerName,$Database,$ConnectionTimeout
    $Conn.ConnectionString=$ConnectionString
    $Cmd=New-Object system.Data.SqlClient.SqlCommand($Query,$Conn)
    $Cmd.CommandTimeout=$QueryTimeout

            do
                {   
                    $Conn.Open()
                    Start-Sleep -Seconds 2
                }while ($Conn.State -ne 'Open')

            $Reader = $cmd.ExecuteReader()
            $Datatable.Load($Reader)
            $Conn.Close()
            return $Datatable    
}


###########################################################
# Create spHexaDecimal Stored Procedure
###########################################################

Function CreatespHexaDecimal ($ServerName)
    {
    $Query='USE [master];
                GO
                SET ANSI_NULLS ON;
                GO
                SET QUOTED_IDENTIFIER ON;
                GO
                CREATE PROCEDURE [dbo].[spHexaDecimal]
                (
                    @BinValue VARBINARY(256)
                    , @HexValue VARCHAR(514) OUTPUT
                )
                AS

                DECLARE @CharValue VARCHAR(514)
                DECLARE @i INT
                DECLARE @Length INT
                DECLARE @HexString CHAR(16)

                SET @CharValue = ''0x''
                SET @i = 1
                SET @Length = DATALENGTH(@BinValue)
                SET @HexString = ''0123456789ABCDEF''

                WHILE (@i <= @Length)
                BEGIN

                    DECLARE @TempInt INT
                    DECLARE @FirstInt INT
                    DECLARE @SecondInt INT

                    SET @TempInt = CONVERT(INT, SUBSTRING(@BinValue, @i, 1))
                    SET @FirstInt = FLOOR(@TempInt/16)
                    SET @SecondInt = @TempInt - (@FirstInt * 16)
                    SET @CharValue = @CharValue 
                                        + SUBSTRING(@HexString, @FirstInt + 1, 1)
                                        + SUBSTRING(@HexString, @SecondInt + 1, 1)

                    SET @i = @i + 1

                END --WHILE (@i <= @Length)

                SET @HexValue = @CharValue'

                Invoke-Sqlcmd -Query $Query -ServerInstance $ServerName
    }


###########################################################
# CheckStroedProc 
###########################################################

Function CheckStoredProc ($Server)
{
    $Query= 'SELECT 1 AS ExistCheck
             FROM   sysobjects 
             WHERE  id = object_id(N''[dbo].[spHexaDecimal]'') 
                 AND OBJECTPROPERTY(id, N''IsProcedure'') = 1 '

    $Result=executequery $Query $QueryTimeout $Server
    $Exist=$Result | SELECT -ExpandProperty ExistCheck
    IF ($Exist -ne 1)
        {
            CreatespHexaDecimal -ServerName $Server
        }
}

###########################################################
# Get Login Script
###########################################################

Function Get-Script ($Server)
{

$Query='DECLARE @TempTable TABLE
(Script NVARCHAR(MAX))
DECLARE @Login NVARCHAR (MAX)
DECLARE CURLOGIN CURSOR FOR
SELECT name 
FROM sys.server_principals
WHERE CONVERT(VARCHAR(24),create_date,103) = CONVERT(VARCHAR(24),GETDATE(),103)
    OR CONVERT(VARCHAR(24),modify_date,103) = CONVERT(VARCHAR(24),GETDATE(),103)

OPEN CURLOGIN
    FETCH NEXT FROM CURLOGIN INTO @Login

WHILE @@FETCH_STATUS = 0
BEGIN
    SET NOCOUNT ON
    DECLARE @Script NVARCHAR (MAX)
    DECLARE @LoginName VARCHAR(500)= @Login
    DECLARE @LoginSID VARBINARY(85)
    DECLARE @SID_String VARCHAR(514)
    DECLARE @LoginPWD VARBINARY(256)
    DECLARE @PWD_String VARCHAR(514)
    DECLARE @LoginType CHAR(1)
    DECLARE @is_disabled BIT
    DECLARE @default_database_name SYSNAME
    DECLARE @default_language_name SYSNAME
    DECLARE @is_policy_checked BIT
    DECLARE @is_expiration_checked BIT
    DECLARE @createdDateTime DATETIME



    SELECT @LoginSID = P.[sid]
        , @LoginType = P.[type]
        , @is_disabled = P.is_disabled 
        , @default_database_name = P.default_database_name 
        , @default_language_name = P.default_language_name 
        , @createdDateTime = P.create_date 
    FROM sys.server_principals P
    WHERE P.name = @LoginName

    /** Some Output **/
    SET @Script = ''''




    --If the login is a SQL Login, then do a lot of stuff...
    IF @LoginType = ''S''
    BEGIN

        SET @LoginPWD = CAST(LOGINPROPERTY(@LoginName, ''PasswordHash'') AS VARBINARY(256))

        EXEC spHexaDecimal @LoginPWD, @PWD_String OUT   
        EXEC spHexaDecimal @LoginSID, @SID_String OUT

        SELECT @is_policy_checked = S.is_policy_checked
            , @is_expiration_checked = S.is_expiration_checked
        FROM sys.sql_logins S

        /** Create Script **/
        SET @Script = @Script + CHAR(13) + CHAR(13)
                        + ''IF EXISTS (SELECT name FROM sys.server_principals WHERE name= ''''''+ @LoginName + '''''') '' 
                        + CHAR(13) + '' BEGIN ''
                        + CHAR(13) + CHAR(9) + '' ALTER LOGIN '' + QUOTENAME(@LoginName)
                        + CHAR(13) + CHAR(9) + ''WITH PASSWORD = '' + @PWD_String + '' HASHED''
                        + CHAR(13) + CHAR(9) + '', DEFAULT_DATABASE = ['' + @default_database_name + '']''
                        + CHAR(13) + CHAR(9) + '', DEFAULT_LANGUAGE = ['' + @default_language_name + '']''
                        + CHAR(13) + CHAR(9) + '', CHECK_POLICY '' + CASE WHEN @is_policy_checked = 0 THEN ''=OFF'' ELSE ''=ON'' END
                        + CHAR(13) + CHAR(9) + '', CHECK_EXPIRATION '' + CASE WHEN @is_expiration_checked = 0 THEN ''=OFF'' ELSE ''=ON'' END
                        + CHAR(13) + '' END ''
                        + CHAR(13) + ''ELSE''
                        + CHAR(13) + '' BEGIN ''
                        + CHAR(13) + CHAR(9) + '' CREATE LOGIN '' + QUOTENAME(@LoginName)
                        + CHAR(13) + CHAR(9) + ''WITH PASSWORD = '' + @PWD_String + '' HASHED''
                        + CHAR(13) + CHAR(9) + '', SID = '' + @SID_String
                        + CHAR(13) + CHAR(9) + '', DEFAULT_DATABASE = ['' + @default_database_name + '']''
                        + CHAR(13) + CHAR(9) + '', DEFAULT_LANGUAGE = ['' + @default_language_name + '']''
                        + CHAR(13) + CHAR(9) + '', CHECK_POLICY '' + CASE WHEN @is_policy_checked = 0 THEN ''=OFF'' ELSE ''=ON'' END
                        + CHAR(13) + CHAR(9) + '', CHECK_EXPIRATION '' + CASE WHEN @is_expiration_checked = 0 THEN ''=OFF'' ELSE ''=ON'' END
                        + CHAR(13) + '' END ''

        SET @Script = @Script + CHAR(13) + CHAR(13)
                        + '' ALTER LOGIN ['' + @LoginName + '']''
                        + CHAR(13) + CHAR(9) + ''WITH DEFAULT_DATABASE = ['' + @default_database_name + '']''
                        + CHAR(13) + CHAR(9) + '', DEFAULT_LANGUAGE = ['' + @default_language_name + '']''

    END
    ELSE
    BEGIN

        --The login is a NT login (or group).
        SET @Script = @Script + CHAR(13) + CHAR(13)
                        + ''IF NOT EXISTS (SELECT name FROM sys.server_principals WHERE name= ''''''+ @LoginName + '''''') '' 
                        + CHAR(13) + '' BEGIN ''
                        + CHAR(13) + CHAR(9) + '' CREATE LOGIN '' + QUOTENAME(@LoginName) + '' FROM WINDOWS''
                        + CHAR(13) + CHAR(9) + ''WITH DEFAULT_DATABASE = ['' + @default_database_name + '']''
                        + CHAR(13) + '' END ''
    END

    /******************************************************************************************/
    --This section deals with the Server Roles that belong to that login...
    /******************************************************************************************/

    DECLARE @ServerRoles TABLE
        (
        ServerRole SYSNAME
        , MemberName SYSNAME
        , MemberSID VARBINARY(85)
        )

    INSERT INTO @ServerRoles EXEC sp_helpsrvrolemember

    --Remove all Roles
    SET @Script = @Script + CHAR(13)
    SET @Script = @Script 
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''sysadmin''''''
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''securityadmin''''''
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''serveradmin'''''' 
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''setupadmin'''''' 
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''processadmin''''''
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''diskadmin'''''' 
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''dbcreator'''''' 
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''bulkadmin'''''' 

    /** Output to script... **/
    --SET @Script = @Script + CHAR(13) + CHAR(13)

    --Test if there are any server roles for this login...
    IF EXISTS(SELECT 1 FROM @ServerRoles WHERE MemberName = @LoginName)
    BEGIN

        SET @Script = @Script + CHAR(13)

        DECLARE @ServerRole SYSNAME
        DECLARE curRoles CURSOR LOCAL STATIC FORWARD_ONLY

        FOR SELECT ServerRole 
            FROM @ServerRoles
            WHERE MemberName = @LoginName

        OPEN curRoles

        FETCH NEXT FROM curRoles
        INTO @ServerRole

        WHILE @@FETCH_STATUS = 0
        BEGIN

            /** Output to Script **/
            SET @Script = @Script 
                            + CHAR(13) + ''EXEC sp_addsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + QUOTENAME(@ServerRole, '''''''')

            FETCH NEXT FROM curRoles
            INTO @ServerRole

        END

        --Cleanup.
        CLOSE curRoles
        DEALLOCATE curRoles

    END
    INSERT INTO @TempTable
    VALUES(@Script)

    FETCH NEXT FROM CURLOGIN INTO @Login
END
CLOSE CURLOGIN;
DEALLOCATE CURLOGIN;
SELECT Script FROM @TempTable'

$Result=executequery $Query $QueryTimeout $Server

If($Result -eq $null)
    {
        break
    }
Else
    {
        [Void][System.IO.Directory]::CreateDirectory("C:\temp")
        $Path = "C:\temp"
        $Acl = (Get-Item $Path).GetAccessControl('Access')
        $Username = Get-WmiObject win32_service | Where name -EQ 'SQLSERVERAGENT' | Select -ExpandProperty StartName
        $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($Username, 'Full', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
        $Acl.SetAccessRule($Ar)
        Set-Acl -path $Path -AclObject $Acl
        $Result | select -ExpandProperty Script | Out-File C:\temp\Script.txt
    }
}


###########################################################
# SCRIPT BODY 
###########################################################

$Query= "SELECT ISNULL(SERVERPROPERTY ('InstanceName'), 'DEFAULT') InstanceName 
            , name AGName
            , replica_server_name Replica
            , role_desc 
            FROM sys.dm_hadr_availability_replica_states hars 
            INNER JOIN sys.availability_groups ag ON ag.group_id = hars.group_id 
            INNER JOIN sys.availability_replicas ar ON ar.replica_id = hars.replica_id
            WHERE role_desc = 'PRIMARY'
            ORDER BY role_desc asc"
Write-Host "Is this Primary Replica?"
$Result=executequery $Query $QueryTimeout $PrimaryReplica
If ($Result -eq $null)
    {
        Write-Host "No, it's not."
        break
    }
Else
    {
        Write-Host "Yes, it is."
        $PrimaryReplica= $Result | select -ExpandProperty Replica
        Write-Host "Check for prerequisite, if not present deploy it."
        CheckStoredProc -Server $PrimaryReplica
        Write-Host "Get script for new/modifies login(s)."
        Get-Script -Server $PrimaryReplica

        $Query= "SELECT ISNULL(SERVERPROPERTY ('InstanceName'), 'DEFAULT') InstanceName 
                    , name AGName
                    , replica_server_name Replica
                    , role_desc 
                    FROM sys.dm_hadr_availability_replica_states hars 
                    INNER JOIN sys.availability_groups ag ON ag.group_id = hars.group_id 
                    INNER JOIN sys.availability_replicas ar ON ar.replica_id = hars.replica_id
                    WHERE role_desc = 'SECONDARY'
                    ORDER BY role_desc asc"

        $Result=executequery $Query $QueryTimeout $PrimaryReplica
        $SecondaryReplicas= $Result | select -ExpandProperty Replica
        $Query= Get-Content -Path 'C:\temp\Script.txt' | Out-String
        ForEach($SecondaryReplica in $SecondaryReplicas)
            {
                Invoke-Sqlcmd -Query $Query -ServerInstance $SecondaryReplica
                Write-Host "Successfully copied login(s) to $SecondaryReplica"
            }
        Remove-Item C:\temp\Script.txt
    }
Muhammad
la source
2
Merci Muhammad, pouvez-vous inclure une partie du contenu du lien? Si le lien meurt, votre question deviendra beaucoup moins utile sinon!
LowlyDBA
@LowlyDBA, j'ai essayé de mettre le script ici mais malheureusement, c'est trop long :( Le lien ne mourra pas car je possède ce site.
Muhammad
Le script n'est pas très long. Je vais l'ajouter pour vous. Et dans ce cas, vous devez divulguer votre affiliation lorsque vous publiez vos propres liens.
LowlyDBA
Excuses @LowlyDBA, je n'étais pas au courant de la divulgation. J'ai essayé de mettre le script ici dans la section des commentaires mais il est limité à un certain nombre de caractères. J'ai ajouté le script complet. Merci
Muhammad
Il est assez honteux que la synchronisation au niveau du serveur ne fasse pas partie d'AG. Votre réponse obtient un vote positif pour l'article appelant MS à ce sujet.
youcantryreachingme
-1

Vous devez utiliser les connexions au domaine Windows et les créer dans chaque instance, car le SID est géré par Active Directory, vous pourrez accéder à tous les réplicas membres du groupe de disponibilité si la connexion existe dans le réplica principal. une autre option, vous devez utiliser un certificat.

user70509
la source