SQL Flight Deck is a personal blog. A cockpit view into Microsoft SQL Server

> My LinkedIn

  • A Knowledge Knugget: Controlled Exposure to Elevated Permissions

    In my opinion, an underrated and nifty method of permission management – bundling permissions alongside a stored procedure using certificate signing. This method enables sensitive permissions to be localised to an object, protected against unintentional changes, whether accidental or malicious, and overall for users to be empowered with access outside of their login’s permission scope. Below, I will demonstrate and explore this method of controlled permission granting.

    Credit here goes to Erland Sommarskog for introducing me to this method. A write-up of his relating to this topic can be found at https://www.sommarskog.se/grantperm.htmlSection 5 “Using Certificates to Package Server-Level Permissions”. I’d highly recommend a read of Erland’s SQL content.

    Take a scenario in which you have a junior member of a DBA team. You’d like them to own a credential rotation process for your estate’s SQL logins and be able to self-serve this task. Without utilising certificate signing, this would require ALTER ANY LOGIN at the very least, and maybe even CONTROL SERVER if the login is a member of sysadmin (think, break-glass accounts etc.). However, with certificate signing, we’re able to avoid those potentially egregious permissions.

    At a high level, we need:

    • Certificate
      • A Login created from the Certificate
        • The elevated permissions are applied to this Login
    • Stored Procedure
      • This stored procedure is signed by the Certificate
    • User Login
      • This login/user has EXECUTE permission on the Stored Procedure

    Demonstration T-SQL

    packaging-permissions-sqlflightdeck.sql
    SQL
    /*~
    ================================================================
    DEMO: Certificate signing to facilitate credential rotation
    ================================================================
    ~*/
    USE master;
    GO
    -- ------------------------------------------------------------
    -- 1. Create a mock application login
    -- ------------------------------------------------------------
    CREATE LOGIN ApplicationAPI WITH PASSWORD = 'Str0ngInitial!Pwd';
    GO
    -- ------------------------------------------------------------
    -- 2. Create a mock login for our DBA individual
    -- ------------------------------------------------------------
    CREATE LOGIN ImARealDBA WITH PASSWORD = 'AnotherStr0ngPwd!';
    CREATE USER ImARealDBA FOR LOGIN ImARealDBA;
    GO
    -- Create a role for the junior DBA
    CREATE ROLE Role_JuniorDBA;
    ALTER ROLE Role_JuniorDBA ADD MEMBER ImARealDBA;
    GO
    -- ------------------------------------------------------------
    -- 3. Create the stored procedure that performs the password
    -- rotation
    -- ------------------------------------------------------------
    CREATE OR ALTER PROCEDURE dbo.usp_RotateLoginPassword
    @LoginName SYSNAME,
    @NewPassword NVARCHAR(128)
    AS
    BEGIN
    SET NOCOUNT ON;
    DECLARE @sql NVARCHAR(MAX);
    SET @sql = N'ALTER LOGIN ' + QUOTENAME(@LoginName) +
    N' WITH PASSWORD = ' + QUOTENAME(@NewPassword, '''');
    EXEC sp_executesql @sql;
    PRINT 'Password successfully rotated for login: ' + QUOTENAME(@LoginName);
    END;
    GO
    -- ------------------------------------------------------------
    -- 4. Create a certificate and a certificate-based login that
    -- holds the elevated permission (ALTER ANY LOGIN, CONTROL SERVER)
    -- ------------------------------------------------------------
    CREATE CERTIFICATE PasswordRotationCert
    ENCRYPTION BY PASSWORD = 'CertPassw0rd!'
    WITH SUBJECT = 'Certificate for signing password rotation SP';
    GO
    -- Create a login from the certificate
    CREATE LOGIN Cert_PasswordRotation FROM CERTIFICATE PasswordRotationCert;
    GO
    -- Grant the elevated, normally-dangerous permission to the CERT LOGIN only
    GRANT ALTER ANY LOGIN TO Cert_PasswordRotation;
    GRANT CONTROL SERVER TO Cert_PasswordRotation
    GO
    -- ------------------------------------------------------------
    -- 5. Sign the stored procedure with the certificate
    -- This is the step that "packages" the elevated permissions
    -- with the proc's execution context
    -- ------------------------------------------------------------
    ADD SIGNATURE TO dbo.usp_RotateLoginPassword
    BY CERTIFICATE PasswordRotationCert
    WITH PASSWORD = 'CertPassw0rd!';
    GO
    -- ------------------------------------------------------------
    -- 6. Grant EXECUTE on the proc to the Junior DBA role (not the
    -- underlying ALTER ANY LOGIN or CONTROL SERVER permission)
    -- ------------------------------------------------------------
    GRANT EXECUTE ON dbo.usp_RotateLoginPassword TO Role_JuniorDBA;
    GO
    /*~
    ================================================================
    TEST: Impersonate SupportUser and rotate the password
    ================================================================
    ~*/
    EXECUTE AS LOGIN = 'ImARealDBA';
    -- Confirm ImARealDBA does NOT have ALTER ANY LOGIN directly
    SELECT HAS_PERMS_BY_NAME(NULL, NULL, 'ALTER ANY LOGIN') AS DirectPermission;
    -- Expect: 0
    -- But CAN successfully execute the signed procedure
    EXEC dbo.usp_RotateLoginPassword
    @LoginName = 'ApplicationAPI',
    @NewPassword = 'BrandNewR0tatedPwd!';
    REVERT;
    GO
    /*~
    ================================================================
    CLEANUP
    ================================================================
    DROP PROCEDURE IF EXISTS dbo.usp_RotateLoginPassword;
    DROP LOGIN Cert_PasswordRotation;
    DROP CERTIFICATE PasswordRotationCert;
    DROP ROLE Role_JuniorDBA;
    DROP USER ImARealDBA;
    DROP LOGIN ImARealDBA;
    DROP LOGIN ApplicationAPI;
    ================================================================
    ~*/

    As can be seen via the demonstration, [ImARealDBA] is able to execute the stored procedure and successfully change a login’s password, all without having ALTER ANY LOGIN or CONTROL SERVER themselves. Furthermore, should [ImARealDBA] have ALTER permissions on the procedure and want to nefariously amend it with, say, SHUTDOWN WITH NOWAIT, they would be met with a rather unfortunate result; the stored procedure is no longer signed after it has been altered – it must be re-signed with the certificate first.

    To preempt what some might think – no, don’t give the certificate sysadmin rights just because it’s easiest. Think Principle of Least Privilege. Equally, always use a certificate per use-case, not one overarching certificate used everywhere. All it takes is the certificate’s password to be in the wrong hands!

    Using Brent Ozar’s First Responder Kit? This method is a great way to package permissions that those SPs require to run end-to-end, whilst allowing administrative personnel to utilise them to their full potential for analysis, planning or troubleshooting. Detailed in https://www.brentozar.com/askbrent/ under “How to Grant Permissions to Non-DBAs”.

  • Unexpected Command Ordering in Transactional Replication

    The scenario

    Let’s take “Table A” as an example, this table has five columns “A, B, C, D, E”. Table A is replicated to a subscriber via an updatable subscription, allowing for bidirectional data changes – in this case, the type is queued updating.

    Inside a single transaction on the subscriber, the following actions occur:

    1. UPDATE TableA SET A = '', B = '' WHERE PK = 1
    2. An AFTER UPDATE trigger executes:
      • UPDATE TableA SET C = 'I''m a trigger' WHERE PK = 1
    3. UPDATE TableA SET D = '', E = '' WHERE PK = 1

    In total, the same row is successfully updated three times.

    The ordering oddity comes when the queue worker thread is applying those commands on the publisher.

    A profiler trace of the queue worker shows command 2 (originating from the trigger) being attempted first under @execution_mode “QFirstPass”. An update conflict is detected due to command 2 having the “old_msrepl_tran_version” of command 1, not the value of the current row on the publisher and this not matching. As a result, subsequent profiler rows shows the three commands being ran with @execution_mode “QPubWins”.

    Image courtesy of Sonnet 4.6

    Confusion ensues..

    Opus 4.8 suggests the following queue ordering is taking place on the subscriber. This is such that the nested UPDATE of C is queued before the update of columns A & B.

    S1: UPDATE A,B -- version stamped V0→V1 as part of the statement
    └─ T_user fires (NFR)
    └─ UPDATE C (S2) -- version stamped V1→V2
    └─ T_sync(S2) ENQUEUE C (old=V1) ← written to queue FIRST
    └─ T_sync(S1) ENQUEUE A,B (old=V0) ← written SECOND
    S3: UPDATE D,E -- version stamped V2→V3
    └─ T_sync(S3) ENQUEUE D,E (old=V2) ← written THIRD

    The execution of T_sync for S1 coming after the nested user trigger wouldn’t conform with Microsoft’s documentation of replication triggers which must be set as the first in ordering and are so by default.

    Replication automatically generates a first trigger for any table that is included in an immediate updating or queued updating subscription. Replication requires that its trigger is the first trigger. Replication raises an error when you try to include a table with a first trigger in an immediate updating or queued updating subscription. If you try to make a trigger a first trigger after a table is included in a subscription, sp_settriggerorder returns an error.

    The above suggests that the command Is queued in the incorrect order to begin with but what about a scenario where they’re queued in the correct order, but the queue worker thread is obtaining them in the wrong order for processing due to ordering ambiguity?

  • Microsoft Certified: Azure Database Administrator Associate

    I recently passed Microsoft’s Azure Database Administrator Associate exam and would like to share my thoughts for anyone in the same boat, pre or post certification.

    Whilst being an inherent way for me to grow the mental database of all things data, it was primarily an opportunity to prove to myself that I had the necessary skills to digest a wide range of new technologies and technical approaches and to break out of a comfortable skill ringfence for a time.

    The resource I used to prepare

    First up, the resource I think everyone mentions or advises – Microsoft Learn. The structured official course material for DP-300 is a must and provides the general fundamental framework of skills that’re required for the exam. In my opinion, the layout, brevity and clarity of the course content is well done and will be what’s kept most up to date with what they’d like you to know. Each section of the course syllabus is broken into modules, and within each of those is a module assessment. The combination of the module assessments and the cumulative course practice assessment were a good way to validate learning.

    When I had been looking to take this exam a number of months ago, towards the back end of 2025, Microsoft Learn was admittedly the sole resource I was using for preparation. In hindsight, now knowing the style of exam content, this might’ve been a dodged bullet! Whilst the Microsoft Learn content for the course is undeniably good, I would use it more as a starting, supplementary reference point for preparation.

    Revisiting the desire again to take the DP-300 in the last few weeks, I came across a set of course videos that Microsoft had uploaded to YouTube in the December of last year. I took these as a sign… a whopping 100% gain in my previous study resources! 😶‍🌫️ The video material is on-par with the Learn material for me in terms of quality. They’ve had to summarise the syllabus into a format/length without it being a full instructor-led, multi-day marathon. One advantage of the videos compared to the written Learn material is the visual walkthrough of the demos. I’d suspect these would be beneficial for the interactive labs that may be in the exam, though I did not have any to validate that sneaking suspicion.

    Also, don’t forget the new account credits that Azure offer – $200 in credits or 30 days, whichever is first. Use this, or an existing Azure subscription, to explore what’s covered in the demos and familiarise yourself with where settings and configuration live.

    As for third-party services that provide practice exam questions, I’m sure there are numerous that are credible and trustworthy in their question/answer accuracy and would be totally worth the cost but I have not used these before. Research before using – potentially search Reddit for general community feel-good score of one you’re interested in.

    Thoughts on the exam

    My exam consisted of some 47 odd questions with an additional 6 case study questions. Heading into it, I was expecting a series of lab tasks, the existence of which backed up by various anecdotal comments on Reddit, though to my surprise there were none. My belief was that this was such when the Azure lab environment was unavailable, presumably due to maintenance or outage, and that very well could’ve been true. I’d by lying if I didn’t say I was partly relieved.

    Additionally, in associate exams like this, you have access to an embedded web browser that is restricted to the Microsoft Learn domain. This was definitely valuable in confirming a number of answers that I’d marked for review towards the end and in hindsight I’d recommend anyone to be vaguely familiar with the search functionality or documentation layout. There isn’t the time available to be using it on the majority of questions, but where it is, you want to be as efficient as possible.

  • From NULL to WordPress

    Ignore the flight plan in the image above and just be glad that AI isn’t piloting you to your favourite holiday destination. Well, not yet at least…

    In an effort to better my technical proficiency, I thought it was about time to throw together a place to collate and technical write-ups, wins, fails or findings, so here goes.