sql-serverazure-sql-databasedatabase-projectsqlpackage

SQLPackage adding REVOKE CONNECT to users - how to remove just the REVOKE CONNECT? (SDK-Style database project)


TLDR: running SQLPACKAGE /action:Scripts against my DACPAC results in users/logins created, but the create script runs a "CREATE USER FOR LOGIN X; REVOKE CONNECT TO X". I need it to not run the revoke - why is it doing that?

I am trying to create my CICD scripts so that I can get pipelines going.

I script out the DB from production:

SqlPackage /Action:Extract /TargetFile:P-$servername-$databasename/sql_schema /DiagnosticsFile:P-$servername-$databasename/$date.log `
/p:ExtractAllTableData=false /p:VerifyExtraction=true /SourceServerName:$servername `
/SourceDatabaseName:$databasename /SourceUser:sqluser /SourcePassword:"passwordgoeshere" /p:ExtractTarget=ObjectType

(confirmed that I get two files per user, one a CREATE LOGIN WITH thatuser PASSWORD = N'longstringofcrap' and the other a CREATE USER [thatuser] FOR LOGIN [thatuser])

<PropertyGroup>  
<SuppressTSqlWarnings>71558;71502</SuppressTSqlWarnings>
</PropertyGroup>
        <Element Type="SqlRoleMembership">
            <Relationship Name="Member">
                <Entry>
                    <References Name="[thauser]" />
                </Entry>
            </Relationship>
            <Relationship Name="Role">
                <Entry>
                    <References ExternalSource="BuiltIns" Name="[db_datareader]" />
                </Entry>
            </Relationship>
        </Element>

and

        <Element Type="SqlLogin" Name="[thatuser]" Disambiguator="9">
            <Property Name="Password" Value="longrandompassword" />
            <Annotation Type="GloballyScoped" />
        </Element>
        <Element Type="SqlUser" Name="[thatuser]">
            <Property Name="LoginIdentifierScript">
                <Value><![CDATA[[thatuser]]]></Value>
            </Property>
            <Property Name="AuthenticationType" Value="1" />
            <Relationship Name="Login">
                <Entry>
                    <References Name="[thatuser]" Disambiguator="9" />
                </Entry>
            </Relationship>
        </Element>
$formattedDate = (Get-Date).ToString("yyyyMMdd_HHmmss")
sqlpackage /action:script /SourceFile:"$home\Documents\Gitlab_Repos\database_projects\P-switchclaims-$databasename\bin\Debug\P-switchclaims-$databasename.dacpac" `
 /TargetServerName:$servername `
 /TargetDatabaseName:$databasename `
 /TargetUser:sqladmin_az_dev /TargetPassword:"$password" `
 /DeployScriptPath:"$home\Documents\to_deploy\deployscript-$databasename-$($formattedDate).sql" `
 /p:ScriptDatabaseOptions=False `
 /p:DoNotAlterReplicatedObjects=true `
 /p:DropObjectsNotInSource=false `
 /p:AdditionalDeploymentContributors=AgileSqlClub.DeploymentFilterContributor `
 /p:AdditionalDeploymentContributorPaths=$home\Documents\Gitlab_Repos\DACtools `
 /p:AdditionalDeploymentContributorArguments="SqlPackageFilter0=IgnoreName(.*_azurelogin);SqlPackageFilter1=IgnoreName(sp_MS.*);" 

If I look in the script it created, I get:

PRINT N'Creating User [thatuser]...';


GO
CREATE USER [thatuser] FOR LOGIN [thatuser];


GO
REVOKE CONNECT TO [thatuser];


GO

I need to add these logins. If I skip that via /p:ExcludeObjectTypes="Users;" then it never creates them - still not what I want. Do I have to create users by hand and then trust it won't revoke it afterwards? Looks like it won't revoke if I create it, and I have added a post-deploy script to GRANT CONNECT, though that still doesn't seem the right way to handle this.


Solution

  • DACPAC deployment script include REVOKE CONNECT TO [thatuser] after CREATE USER FOR LOGIN

    This behavior occurs because SqlPackage uses model-based deployment. When the model (DACPAC) doesn't explicitly include a GRANT CONNECT on the user, but the target database already has it granted (implicitly or explicitly), the deployment engine revokes it to align the target with the model.

    The root cause is SqlPackage performs model-based deployments. So, when you extract the database using:

    SqlPackage /Action:Extract ...
    

    It includes SqlLogin and SqlUser in the DACPAC. The model assumes only what’s explicitly defined. Since CREATE USER FOR LOGIN implicitly grants CONNECT, and this isn't stored in the DACPAC, SqlPackage sees it as a mismatch if the target already has CONNECT, and emits:

    REVOKE CONNECT TO [thatuser];
    

    To prevent REVOKE CONNECT, you must make the GRANT CONNECT explicit in your DACPAC by using a post-deployment script. Follow the below mentioned steps for the same:

    1. Create a file GrantConnect.sql:
    GRANT CONNECT TO [thatuser];
    
    1. Reference it in your .sqlproj:
    <ItemGroup>
      <PostDeploy Include="GrantConnect.sql" />
    </ItemGroup>
    
    1. Rebuild the DACPAC and rerun SqlPackage /Action:Script:
    SqlPackage /Action:Script ...
    

    This ensures SqlPackage treats CONNECT as part of the desired model and won't attempt to revoke it during deployment.

    Check for reference - Post deployment script