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:
dotnet new sqlproj -tp SqlAzureV12 -n P-$servername-$databasename
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]
)
add the master db reference:
cd P-$servername-$databasename
and then dotnet add package Microsoft.SQLServer.Dacpacs.Azure.Master
add a filter for some sql warnings to the sqlproj file
<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.
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:
GrantConnect.sql
:GRANT CONNECT TO [thatuser];
.sqlproj
:<ItemGroup>
<PostDeploy Include="GrantConnect.sql" />
</ItemGroup>
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.
p:ExcludeObjectTypes=Users
, this skips creation entirely.Check for reference - Post deployment script