## Kedy použiť Skript slúži na **automatické porovnanie a synchronizáciu členstva v databázových rolách** medzi dvoma SQL loginmi na jednej SQL Server inštancii. Umožňuje zistiť, ktoré databázové roly má referenčný účet a ktoré chýbajú cieľovému účtu. Chýbajúce členstvá dokáže následne automaticky doplniť. ### Typické scenáre použitia - **Migrácia servisného účtu** – nový účet potrebuje rovnaké databázové roly ako pôvodný. - **Riešenie Permission denied** – kontrola, či účtu nechýba členstvo v požadovanej role. - **Migrácia SQL Servera** – kontrola a dorovnanie databázových rolí po presune databáz. - **Bezpečnostný audit** – identifikácia rozdielov medzi dvoma SQL loginmi. - **Správa viacerých SQL inštancií** – použitie rovnakého skriptu na serveroch s rozdielnymi databázami. > [!TIP] > Skript automaticky vyhľadáva databázy aj roly na aktuálnej SQL inštancii. Nie je potrebné definovať zoznam databáz ani upravovať názvy rolí. --- ## Ako skript funguje Skript pracuje na princípe **referenčný login → cieľový login**. - **ReferenceLogin** – účet, ktorého členstvá v rolách predstavujú požadovaný stav. - **TargetLogin** – účet, ktorému chceme prípadne doplniť chýbajúce členstvá. Na získanie informácií používa systémové katalógové pohľady: - `sys.databases` – zoznam databáz na inštancii. - `sys.server_principals` – kontrola existencie SQL loginov. - `sys.database_principals` – databázoví používatelia a roly. - `sys.database_role_members` – členstvo používateľov v databázových rolách. Databázových používateľov identifikuje podľa SID príslušných SQL loginov. Názov databázového používateľa preto nemusí byť rovnaký ako názov SQL loginu. ### Režimy vykonania | Režim | Popis | |---|---| | `@Apply = 0` | Iba kontrola a výpis rozdielov (dry-run) | | `@Apply = 1` | Kontrola a automatické doplnenie chýbajúcich členstiev | > [!IMPORTANT] > Predvolený režim je `@Apply = 0`. Skript v tomto režime nemení oprávnenia ani členstvá v databázových rolách. --- ## Postup použitia ### 1. Pripojenie na SQL Server 1. Otvor SQL Server Management Studio (SSMS). 2. Pripoj sa na požadovanú SQL Server inštanciu. 3. Otvor nové query okno. 4. Vlož SQL skript zo záložky **Kód**. Skript sa vykonáva nad aktuálne pripojenou SQL inštanciou. > [!TIP] > Na spoľahlivú kontrolu všetkých databáz používaj administrátorský účet s dostatočnými oprávneniami na čítanie bezpečnostných metadát. ### 2. Konfigurácia loginov V úvodnej časti skriptu uprav: ```sql DECLARE @ReferenceLogin SYSNAME = N'SourceLogin'; DECLARE @TargetLogin SYSNAME = N'TargetLogin'; ``` **Význam parametrov:** - `@ReferenceLogin` – zdrojový SQL login. - `@TargetLogin` – cieľový SQL login, ktorému chceme doplniť členstvá. **Príklad:** ```sql DECLARE @ReferenceLogin SYSNAME = N'app_service_old'; DECLARE @TargetLogin SYSNAME = N'app_service_new'; ``` Skript bude porovnávať roly účtu `app_service_new` voči účtu `app_service_old`. ### 3. Nastavenie rozsahu databáz ```sql DECLARE @IncludeSystemDB BIT = 0; ``` Možnosti: - `0` – kontrolujú sa iba používateľské databázy. - `1` – kontrolujú sa aj systémové databázy okrem `tempdb`. > [!TIP] > Pre bežnú správu aplikačných a servisných účtov odporúčam ponechať `@IncludeSystemDB = 0`. ### 4. Spustenie kontroly (dry-run) Nastav: ```sql DECLARE @Apply BIT = 0; ``` Spusti celý skript pomocou **Execute (F5)**. Skript vykoná: 1. Kontrolu existencie oboch loginov. 2. Automatické vyhľadanie dostupných databáz. 3. Identifikáciu databázových používateľov. 4. Porovnanie členstva v databázových rolách. 5. Výpis zistených rozdielov a upozornení. **V tomto režime sa nevykonajú žiadne zmeny oprávnení.** --- ## Interpretácia výsledkov ### Result 1 – Rozdiely v databázových rolách Príklad výsledku: | DatabaseName | RoleName | Status | |---|---|---| | AppDB | AppWriter | MISSING - ADD | | ReportingDB | db_datareader | MISSING - ADD | | ArchiveDB | db_datawriter | EXTRA - KEEP | #### MISSING - ADD Referenčný účet je členom databázovej roly, ale cieľový účet nie. Pri použití `@Apply = 1` bude cieľový používateľ automaticky pridaný do tejto roly. #### EXTRA - KEEP Cieľový účet je členom databázovej roly, ktorú referenčný účet nemá. Skript túto skutočnosť iba zobrazí. **Existujúce členstvo cieľového používateľa automaticky neodstraňuje.** > [!NOTE] > Ak je výsledok prázdny, skript nezistil rozdiely v priamom členstve databázových rolí v úspešne skontrolovaných databázach. Neznamená to však automaticky, že oba účty majú identické efektívne oprávnenia. ### Result 2 – Upozornenia Druhý výsledok obsahuje informácie o problémoch pri kontrole databáz. Typické upozornenia: - Databáza nie je ONLINE. - Databáza je READ_ONLY. - Databáza nie je dostupná pre aktuálny účet. - Cieľový databázový používateľ neexistuje, hoci referenčný má priradené roly. - Pri kontrole databázy nastala chyba. > [!WARNING] > Ak skript zaznamená upozornenia, režim aplikovania sa zablokuje. Pred opakovaným spustením je potrebné zistiť ich príčinu. --- ## Aplikovanie chýbajúcich členstiev ### 5. Povolenie aplikovania Po kontrole výsledkov a schválení zmeny nastav: ```sql DECLARE @Apply BIT = 1; ``` Spusti celý skript znova. Skript automaticky: 1. Zopakuje porovnanie členstva v databázových rolách. 2. Skontroluje prípadné upozornenia. 3. Vyberie rozdiely označené `MISSING - ADD`. 4. Overí aktuálne členstvo pred vykonaním zmeny. 5. Doplní chýbajúce členstvá pomocou `ALTER ROLE`. 6. Vráti zoznam úspešne vykonaných operácií. Použitý SQL príkaz: ```sql ALTER ROLE [RoleName] ADD MEMBER [DatabaseUser]; ``` > [!WARNING] > Režim `@Apply = 1` mení databázové oprávnenia prostredníctvom členstva v rolách. Na produkcii ho používaj až po vyhodnotení rozdielov a schválení požadovaných zmien. ### 6. Výsledok aplikovania Po úspešnom vykonaní sa zobrazí tretí výsledok. **Result 3 – Applied Changes** | DatabaseName | RoleName | TargetUser | ActionStatus | |---|---|---|---| | AppDB | AppWriter | app_service_new | ADDED | | ReportingDB | db_datareader | app_service_new | ADDED | **Význam stavu:** - `ADDED` – členstvo v databázovej role bolo úspešne doplnené. Skript používa transakčné spracovanie a pri chybe počas aplikovania vykoná `ROLLBACK TRANSACTION`. ### 7. Záverečná kontrola Po dokončení nastav: ```sql DECLARE @Apply BIT = 0; ``` Spusti celý skript ešte raz. **Očakávaný výsledok:** - Žiadne `MISSING - ADD`. - Žiadne nevyriešené upozornenia. - Prípadné `EXTRA - KEEP` predstavujú členstvá cieľového účtu navyše. > [!TIP] > Výsledky kontroly pred zmenou a po zmene je vhodné uložiť ako súčasť dokumentácie alebo change ticketu. --- ## Bezpečnostné mechanizmy ### Dry-run Predvolený režim nevykonáva žiadne zmeny. ### Automatické vyhľadávanie databáz Skript pracuje bez pevného zoznamu databáz a rolí. ### Kontrola existencie členstva Pred pridaním používateľa kontroluje, či už členom požadovanej roly nie je. ### Transakčné spracovanie Zmeny sa vykonávajú v transakcii. V prípade chyby počas aplikovania sa vykoná rollback. ### Ochrana pred neúplnou kontrolou Zistené upozornenia blokujú automatické aplikovanie zmien. ### Bez automatického odoberania oprávnení Skript pridáva iba chýbajúce členstvá. Existujúce členstvá cieľového používateľa nemení. --- ## Obmedzenia skriptu > [!IMPORTANT] > Skript slúži na synchronizáciu **priameho členstva v databázových rolách**, nie na kompletné kopírovanie SQL Server oprávnení. Skript neporovnáva ani nesynchronizuje: - Serverové roly (`sysadmin`, `securityadmin` a ďalšie). - Priame serverové oprávnenia (`GRANT`, `DENY`). - Priame databázové oprávnenia (`SELECT`, `INSERT`, `UPDATE`, `DELETE`, `EXECUTE`). - Vlastníctvo databáz a databázových objektov. - Všetky efektívne oprávnenia zdedené nepriamo. - Heslá a autentifikačné nastavenia SQL loginov. Skript nevytvára chýbajúcich databázových používateľov. Ak referenčný databázový používateľ neexistuje, skript nemá jeho členstvá na porovnanie. Ak cieľový používateľ v tej istej databáze má nejaké roly, zobrazia sa ako `EXTRA - KEEP`. --- ## Bezpečnostné upozornenie Pridanie používateľa do databázovej roly môže výrazne rozšíriť jeho oprávnenia. Príklady: | Rola | Význam | |---|---| | `db_datareader` | Čítanie používateľských tabuliek a pohľadov | | `db_datawriter` | Úprava dát v používateľských tabuľkách | | `db_ddladmin` | Vykonávanie DDL operácií | | `db_owner` | Rozsiahle administrátorské oprávnenia v databáze | Používateľsky definované roly môžu mať vlastné oprávnenia alebo byť členmi ďalších databázových rolí. > [!CAUTION] > Nesynchronizuj oprávnenia iba preto, že dva účty majú byť „rovnaké“. Najskôr over, či cieľový účet skutočne potrebuje všetky oprávnenia referenčného účtu. Dodržiavaj princíp najmenších potrebných oprávnení (Least Privilege). --- ## Odporúčaný DBA workflow 1. Pripoj sa na požadovanú SQL Server inštanciu. 2. Nastav referenčný a cieľový login. 3. Spusti kontrolu s `@Apply = 0`. 4. Skontroluj rozdiely a upozornenia. 5. Over, čo umožňujú chýbajúce databázové roly. 6. V prípade potreby získaj schválenie zmeny. 7. Nastav `@Apply = 1` a aplikuj zmeny. 8. Skontroluj zoznam vykonaných operácií. 9. Opakuj kontrolu s `@Apply = 0`. 10. Over funkčnosť aplikácie alebo služby. --- ## Zhrnutie Skript umožňuje rýchlo identifikovať a kontrolovane doplniť chýbajúce členstvá v databázových rolách medzi dvoma SQL loginmi. **Hlavné výhody:** - Automatické vyhľadanie databáz. - Automatické porovnanie databázových rolí. - Použiteľnosť na rôznych SQL Server inštanciách. - Predvolený bezpečný režim kontroly. - Aplikovanie zmien pomocou jediného prepínača. - Výpis vykonaných zmien. - Bez automatického odoberania existujúcich členstiev. **SQL Server:** 2012+ **Použitie:** Security Audit / Database Role Comparison / Role Synchronization > [!NOTE] > Tento nástroj je určený pre DBA na opakovateľné porovnávanie a dorovnávanie databázových rolí. Nenahrádza kompletný audit efektívnych SQL Server oprávnení.
Porovnanie a synchronizácia databázových rolí medzi loginmi
Automaticky porovná členstvo dvoch SQL loginov v databázových rolách naprieč celou SQL Server inštanciou. Zobrazí chýbajúce aj nadbytočné roly a umožňuje bezpečné dorovnanie pomocou prepínača @Apply (dry-run).
/*
============================================================
DEADLOCK CAFE
Porovnanie a synchronizacia databazovych roli
============================================================
SQL Server: 2012+
Typ: Security / Database Role Synchronization
REZIMY:
@Apply = 0 : Iba kontrola (DRY-RUN)
@Apply = 1 : Doplnenie chybajucich clenstiev
Skript automaticky vyhlada databazy a databazove role.
Nekopiruje priame GRANT/DENY ani serverove role.
============================================================
*/
USE [master];
GO
SET NOCOUNT ON;
SET XACT_ABORT ON;
------------------------------------------------------------
-- 1. KONFIGURACIA - DEADLOCK CAFE PARAMETRE
------------------------------------------------------------
DECLARE @ReferenceLogin SYSNAME =
N'{{ReferenceLogin:SourceLogin|Referenčný SQL login}}';
DECLARE @TargetLogin SYSNAME =
N'{{TargetLogin:TargetLogin|Cieľový SQL login}}';
DECLARE @Apply INT =
{{Apply:0|Aplikovať zmeny? (0=nie, 1=áno)}};
DECLARE @IncludeSystemDB INT =
{{IncludeSystemDB:0|Zahrnúť systémové DB? (0=nie, 1=áno)}};
------------------------------------------------------------
-- 2. VALIDACIA PARAMETROV A LOGINOV
------------------------------------------------------------
IF @Apply NOT IN (0, 1) OR @Apply IS NULL
THROW 50001, 'Parameter Apply musi byt 0 alebo 1.', 1;
IF @IncludeSystemDB NOT IN (0, 1)
OR @IncludeSystemDB IS NULL
THROW 50002, 'IncludeSystemDB musi byt 0 alebo 1.', 1;
IF @ReferenceLogin = @TargetLogin
THROW 50003, 'Loginy musia byt rozdielne.', 1;
DECLARE @ReferenceSID VARBINARY(85);
DECLARE @TargetSID VARBINARY(85);
SELECT @ReferenceSID = sid
FROM sys.server_principals
WHERE name = @ReferenceLogin
AND type = 'S';
SELECT @TargetSID = sid
FROM sys.server_principals
WHERE name = @TargetLogin
AND type = 'S';
IF @ReferenceSID IS NULL
THROW 50004, 'Referencny SQL login neexistuje.', 1;
IF @TargetSID IS NULL
THROW 50005, 'Cielovy SQL login neexistuje.', 1;
------------------------------------------------------------
-- 3. PRACOVNE TABULKY
------------------------------------------------------------
IF OBJECT_ID('tempdb..#RoleDifferences') IS NOT NULL
DROP TABLE #RoleDifferences;
IF OBJECT_ID('tempdb..#Warnings') IS NOT NULL
DROP TABLE #Warnings;
IF OBJECT_ID('tempdb..#Applied') IS NOT NULL
DROP TABLE #Applied;
CREATE TABLE #RoleDifferences
(
DatabaseName SYSNAME NOT NULL,
RoleName SYSNAME NOT NULL,
ReferenceUser SYSNAME NULL,
TargetUser SYSNAME NULL,
ActionType VARCHAR(10) NOT NULL
);
CREATE TABLE #Warnings
(
DatabaseName SYSNAME,
WarningMessage NVARCHAR(4000)
);
CREATE TABLE #Applied
(
DatabaseName SYSNAME,
RoleName SYSNAME,
TargetUser SYSNAME,
ActionStatus VARCHAR(20)
);
------------------------------------------------------------
-- 4. KONTROLA DOSTUPNOSTI DATABAZ
------------------------------------------------------------
INSERT INTO #Warnings
(
DatabaseName,
WarningMessage
)
SELECT
name,
CONCAT(
N'Databaza preskocena. State=',
state_desc,
N', ReadOnly=',
is_read_only,
N', Access=',
COALESCE(
CONVERT(VARCHAR(10), HAS_DBACCESS(name)),
'NULL'
)
)
FROM sys.databases
WHERE
(database_id > 4 OR @IncludeSystemDB = 1)
AND database_id <> 2
AND
(
state_desc <> 'ONLINE'
OR is_read_only = 1
OR ISNULL(HAS_DBACCESS(name), 0) <> 1
);
------------------------------------------------------------
-- 5. AUTOMATICKE POROVNANIE DATABAZOVYCH ROLI
------------------------------------------------------------
DECLARE @DB SYSNAME;
DECLARE @SQL NVARCHAR(MAX);
DECLARE db_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT name
FROM sys.databases
WHERE state_desc = 'ONLINE'
AND is_read_only = 0
AND HAS_DBACCESS(name) = 1
AND database_id <> 2
AND (database_id > 4 OR @IncludeSystemDB = 1);
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO @DB;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SQL = N'
USE ' + QUOTENAME(@DB) + N';
-- Overenie viditelnosti bezpecnostnych metadat
IF COALESCE(IS_SRVROLEMEMBER(''sysadmin''), 0) <> 1
AND COALESCE(IS_MEMBER(''db_owner''), 0) <> 1
AND COALESCE(IS_MEMBER(''db_securityadmin''), 0) <> 1
AND COALESCE(
HAS_PERMS_BY_NAME(
DB_NAME(), ''DATABASE'', ''VIEW DEFINITION''
), 0
) <> 1
BEGIN
THROW 50010,
''Nedostatocna viditelnost databazovych roli.'',
1;
END;
DECLARE @RefID INT;
DECLARE @TargetID INT;
DECLARE @RefUser SYSNAME;
DECLARE @TargetUser SYSNAME;
SELECT
@RefID = principal_id,
@RefUser = name
FROM sys.database_principals
WHERE sid = @RefSID
AND type = ''S'';
SELECT
@TargetID = principal_id,
@TargetUser = name
FROM sys.database_principals
WHERE sid = @TargetSID
AND type = ''S'';
--------------------------------------------------------
-- Cielovy databazovy user neexistuje
--------------------------------------------------------
IF @RefID IS NOT NULL AND @TargetID IS NULL
BEGIN
INSERT INTO #Warnings
VALUES
(
DB_NAME(),
N''Cielovy database user neexistuje.''
);
END
ELSE
BEGIN
----------------------------------------------------
-- ROLE, KTORE CIELOVEMU USEROVI CHYBAJU
----------------------------------------------------
IF @RefID IS NOT NULL AND @TargetID IS NOT NULL
BEGIN
INSERT INTO #RoleDifferences
(
DatabaseName,
RoleName,
ReferenceUser,
TargetUser,
ActionType
)
SELECT
DB_NAME(),
r.name,
@RefUser,
@TargetUser,
''ADD''
FROM sys.database_role_members rm
JOIN sys.database_principals r
ON r.principal_id = rm.role_principal_id
WHERE rm.member_principal_id = @RefID
AND NOT EXISTS
(
SELECT 1
FROM sys.database_role_members x
WHERE x.role_principal_id =
rm.role_principal_id
AND x.member_principal_id = @TargetID
);
END;
----------------------------------------------------
-- ROLE, KTORE MA CIELOVY USER NAVYSE
----------------------------------------------------
IF @TargetID IS NOT NULL
BEGIN
INSERT INTO #RoleDifferences
(
DatabaseName,
RoleName,
ReferenceUser,
TargetUser,
ActionType
)
SELECT
DB_NAME(),
r.name,
@RefUser,
@TargetUser,
''EXTRA''
FROM sys.database_role_members rm
JOIN sys.database_principals r
ON r.principal_id = rm.role_principal_id
WHERE rm.member_principal_id = @TargetID
AND NOT EXISTS
(
SELECT 1
FROM sys.database_role_members x
WHERE x.role_principal_id =
rm.role_principal_id
AND x.member_principal_id = @RefID
);
END;
END;
';
BEGIN TRY
EXEC sys.sp_executesql
@SQL,
N'@RefSID VARBINARY(85),
@TargetSID VARBINARY(85)',
@RefSID = @ReferenceSID,
@TargetSID = @TargetSID;
END TRY
BEGIN CATCH
INSERT INTO #Warnings
(
DatabaseName,
WarningMessage
)
VALUES
(
@DB,
ERROR_MESSAGE()
);
END CATCH;
FETCH NEXT FROM db_cursor INTO @DB;
END;
CLOSE db_cursor;
DEALLOCATE db_cursor;
------------------------------------------------------------
-- 6. RESULT 1 - ZISTENE ROZDIELY
------------------------------------------------------------
SELECT
DatabaseName,
RoleName,
ReferenceUser,
TargetUser,
CASE ActionType
WHEN 'ADD' THEN 'MISSING - ADD'
WHEN 'EXTRA' THEN 'EXTRA - KEEP'
END AS Status
FROM #RoleDifferences
ORDER BY
DatabaseName,
RoleName,
ActionType;
------------------------------------------------------------
-- 7. RESULT 2 - UPOZORNENIA
------------------------------------------------------------
SELECT
DatabaseName,
WarningMessage
FROM #Warnings
ORDER BY DatabaseName;
------------------------------------------------------------
-- 8. APLIKOVANIE ZMIEN
------------------------------------------------------------
IF @Apply = 1
BEGIN
-- Pri upozorneniach sa nevykona ziadna zmena
IF EXISTS (SELECT 1 FROM #Warnings)
BEGIN
THROW 50011,
'Kontrola obsahuje upozornenia. Zmeny zablokovane.',
1;
END;
DECLARE @Role SYSNAME;
DECLARE apply_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT
DatabaseName,
RoleName
FROM #RoleDifferences
WHERE ActionType = 'ADD'
ORDER BY DatabaseName, RoleName;
BEGIN TRY
BEGIN TRANSACTION;
OPEN apply_cursor;
FETCH NEXT FROM apply_cursor INTO @DB, @Role;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SQL = N'
USE ' + QUOTENAME(@DB) + N';
DECLARE @RefID INT;
DECLARE @TargetID INT;
DECLARE @RoleID INT;
DECLARE @DbUser SYSNAME;
SELECT @RefID = principal_id
FROM sys.database_principals
WHERE sid = @RefSID
AND type = ''S'';
SELECT
@TargetID = principal_id,
@DbUser = name
FROM sys.database_principals
WHERE sid = @TargetSID
AND type = ''S'';
SELECT @RoleID = principal_id
FROM sys.database_principals
WHERE name = @Role
AND type = ''R'';
IF @RefID IS NULL OR @TargetID IS NULL
THROW 50012,
''DB user neexistuje.'',
1;
IF @RoleID IS NULL
THROW 50013,
''Databazova rola neexistuje.'',
1;
-- Referencny user musi stale mat danu rolu
IF NOT EXISTS
(
SELECT 1
FROM sys.database_role_members
WHERE role_principal_id = @RoleID
AND member_principal_id = @RefID
)
THROW 50014,
''Referencny user uz nema tuto rolu.'',
1;
-- Ochrana pred duplicitnym priradenim
IF NOT EXISTS
(
SELECT 1
FROM sys.database_role_members
WHERE role_principal_id = @RoleID
AND member_principal_id = @TargetID
)
BEGIN
DECLARE @Command NVARCHAR(MAX);
SET @Command =
N''ALTER ROLE ''
+ QUOTENAME(@Role)
+ N'' ADD MEMBER ''
+ QUOTENAME(@DbUser)
+ N'';'';
EXEC sys.sp_executesql @Command;
INSERT INTO #Applied
(
DatabaseName,
RoleName,
TargetUser,
ActionStatus
)
VALUES
(
DB_NAME(),
@Role,
@DbUser,
''ADDED''
);
END;
';
EXEC sys.sp_executesql
@SQL,
N'@Role SYSNAME,
@RefSID VARBINARY(85),
@TargetSID VARBINARY(85)',
@Role = @Role,
@RefSID = @ReferenceSID,
@TargetSID = @TargetSID;
FETCH NEXT FROM apply_cursor INTO @DB, @Role;
END;
CLOSE apply_cursor;
DEALLOCATE apply_cursor;
COMMIT TRANSACTION;
----------------------------------------------------
-- RESULT 3 - USpesne APLIKOVANE ZMENY
----------------------------------------------------
SELECT
DatabaseName,
RoleName,
TargetUser,
ActionStatus
FROM #Applied
ORDER BY DatabaseName, RoleName;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
IF CURSOR_STATUS('local', 'apply_cursor') >= 0
CLOSE apply_cursor;
IF CURSOR_STATUS('local', 'apply_cursor') > -3
DEALLOCATE apply_cursor;
THROW;
END CATCH;
END;
------------------------------------------------------------
-- 9. ZAVERECNY STATUS
------------------------------------------------------------
SELECT
@@SERVERNAME AS ServerName,
@ReferenceLogin AS ReferenceLogin,
@TargetLogin AS TargetLogin,
CASE @Apply
WHEN 0 THEN 'DRY-RUN'
WHEN 1 THEN 'APPLY COMPLETED'
END AS ExecutionMode,
(SELECT COUNT(*)
FROM #RoleDifferences
WHERE ActionType = 'ADD') AS MissingMemberships,
(SELECT COUNT(*)
FROM #RoleDifferences
WHERE ActionType = 'EXTRA') AS ExtraMemberships,
(SELECT COUNT(*)
FROM #Applied) AS AppliedMemberships,
(SELECT COUNT(*)
FROM #Warnings) AS WarningCount;
------------------------------------------------------------
-- 10. CLEANUP
------------------------------------------------------------
DROP TABLE #RoleDifferences;
DROP TABLE #Warnings;
DROP TABLE #Applied;