Anmeldungen werden nicht über Verfügbarkeitsgruppen hinweg synchronisiert


12

Wir haben 2 Server in einer AlwaysOn-Gruppe.

Während die Benutzerkonten in jeder synchronisierten Datenbank auf beiden Servern vorhanden sind, sind die Anmeldungen auf Datenbankinstanzebene nur auf einem der Server vorhanden. Dh DBINSTANCE-> Security-> Logins fehlen auf einem Server.

Daher erhalte ich bei einem Failover Anmeldefehler auf dem zweiten Server (der nicht über die entsprechenden Anmeldungen auf Instanzebene verfügt).

Wie überwinde ich dieses Problem? Sollte ich das Benutzerkonto auf besondere Weise einrichten?


Ich habe vor kurzem begonnen , auch in diesem suchen, rollte ich meine eigene und beschreibe es zur Zeit in meinem neuen Blog (Vor allem für meinen eigenen Nutzen , wie ich vorwärts gehen und Dinge vergessen , aber wenn seine Hilfe an andere umso besser) Leben -and-dev.blogspot.co.uk/2015/04/… Dieses und das folgende Paar zeigen AlwaysOn-Failover-Tracking und Job- / Benutzersynchronisierung

Sie können das SSMS-Plug-in von sqlskills Synchronize Availability Group Logins und Jobs
Kin Shah,

Antworten:


15

Meines Wissens ist das, wenn Sie nicht enthaltene Datenbanken verwenden müssen Sie sicherstellen, dass Anmeldungen auf anderen Instanzen manuell erstellt werden .

Etwas wie dieses Skript von SQLSoldier , ursprünglich als " Logins auf einen Datenbankspiegel übertragen" veröffentlicht , sollte den Trick machen.


Ich freue mich darauf, einige unserer Produktionsdatenbanken '05 in das neue Format umzuwandeln, sobald wir in den nächsten ein oder zwei Wochen auf '12 wechseln. In der Zwischenzeit verwende ich die Tools von Idera, um Anmeldungen, Aufträge usw. auf die sekundäre Instanz zu kopieren.
Max Vernon

Markieren ist richtig, Sie müssen entweder enthaltene Datenbanken verwenden oder die Anmeldungen manuell auf alle Instanzen im Cluster kopieren, wobei für alle Instanzen dieselbe SID verwendet wird.
mrdenny

Denken Sie daran, dass ich nach AlwaysOn frage und nicht nach Mirroring. Ich habe das Skript ausprobiert, aber die Tabelle sys.servers enthält nur den lokalen Server, sodass beim Ausführen der gespeicherten Prozedur die folgende Fehlermeldung angezeigt wird: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

Ich habe es herausgefunden und eine neue Antwort für AlwaysOn gepostet.
John

@JohnHughes Dieses Skript ist für AlwaysOn geeignet. Sie müssen jedoch, wie in der Fehlermeldung angegeben, einen Verbindungsserver zwischen den Instanzen erstellen.
Mark Storey-Smith

6

Sie müssen entweder eine enthaltene Datenbank verwenden oder die Benutzer auf den anderen Servern mit demselben Kennwort-Hash und derselben SID neu erstellen.

Ein Skript hierfür wird von Microsoft bereitgestellt: Übertragen von Anmeldungen und Kennwörtern zwischen Instanzen von SQL Server

Marks Lösung war teilweise richtig, jedoch war seine empfohlene Lösung für gespiegelte Datenbanken, im Gegensatz zu AlwaysOn, was in den Fragen gefragt wird.


2
Inwiefern unterscheidet sich die Lösung für gespiegelte Datenbanken von der Lösung für Verfügbarkeitsgruppen (Hinweis: AlwaysOn ist ein Marketinglabel für eine Reihe von Technologien, keine Funktion)? Der Titel eines Blogposts ändert nichts an der Tatsache, dass fehlende Logins wiederhergestellt werden - was an und für sich nichts damit zu tun hat, ob Sie Spiegelung, Verfügbarkeitsgruppen, Protokollversand, manuelle Sicherung / Wiederherstellung usw. verwenden.
Aaron Bertrand

Leider kann ich nicht immer AD-Konten verwenden. Wir haben Probleme damit, die Passwörter zu vergleichen. Nehmen wir nicht an, dass das Failover zwischen Replikatservern fehlschlägt, wenn die Kennwörter nicht übereinstimmen?

1

Ich antworte nach langer Zeit auf den Post, aber es kann jemand anderem bei dem ähnlichen Problem helfen. Mit PowerShell können Anmeldungen von der primären Replik auf die sekundären Repliken kopiert werden. Details finden sich hier https://maq.guru/synchronizing-sql-server-logins-in-an-always-on-availability-group/ .

Vollständige Offenlegung: Ich besitze die oben genannte Website.

Das PowerShell-Skript:

$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
    }

2
Danke Muhammad, kannst du einige Inhalte des Links einbinden? Wenn der Link abbricht, ist Ihre Frage ansonsten viel weniger nützlich!
LowlyDBA

@ LowlyDBA, ich habe versucht, das Skript hier zu platzieren, aber leider ist es zu lang :( Der Link wird nicht sterben, da ich diese Website besitze.
Muhammad

Das Drehbuch ist nicht sehr lang. Ich werde es für Sie hinzufügen. In diesem Fall müssen Sie Ihre Zugehörigkeit offenlegen, wenn Sie Ihre eigenen Links veröffentlichen.
LowlyDBA

Entschuldigung @LowlyDBA, mir war die Offenlegung nicht bekannt. Ich habe versucht, das Skript hier in den Kommentarbereich zu stellen, aber es ist auf eine bestimmte Anzahl von Zeichen beschränkt. Ich habe das komplette Skript hinzugefügt. Danke
Muhammad

Es ist ziemlich beschämend, dass die Synchronisierung auf Serverebene nicht Teil von AG ist. Ihre Antwort erhält eine positive Bewertung für den Artikel, in dem MS darauf hingewiesen wird.
youcantryreachingme

0

Sie müssen Windows-Domänenanmeldungen verwenden und in jeder Instanz erstellen. Da die SID über Active Directory verwaltet wird, können Sie auf alle Replikate der Verfügbarkeitsgruppe zugreifen, wenn die Anmeldung im primären Replikat vorhanden ist. Eine andere Option, die Sie benötigen, um ein Zertifikat zu verwenden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.