Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent dropping of users when publishing a DACPAC using SqlPackage.exe

Tags:

Is there any way of preventing users being dropped when publishing a DACPAC using SqlPackage.exe, other than changing the setting below, which prevents all objects from being dropped if they're not in the DACPAC.

<DropObjectsNotInSource>True</DropObjectsNotInSource>

We deploy to a number of environments, each with different users. Current workarounds are to either:

  1. Script the users for each environment to recreate them after deploying
  2. Use /Action:Script and manually change the deployment script.

Neither of these are ideal though...

like image 251
Matt Avatar asked Jul 01 '13 11:07

Matt


People also ask

How do I publish a Dacpac file?

A simple command would be "sqlpackage /a:publish /sf:db1. dacpac /tsn:localhost /tdn:db1" to publish dacpac "db1" to a database "localhost\db1". For other publish options type "sqlpackage /a:publish /?" to get the list of applicable actions on the command line.

What is SqlPackage EXE?

SqlPackage.exe is a command-line utility that automates the following database development tasks by exposing some of the public Data-Tier Application Framework (DacFx) APIs: Version: Returns the build number of the SqlPackage application.

How does Dacpac deployment work?

The DAC package is in turn deployed to a test, staging or production database through an automated process or manually with a CLI or GUI tool. The . dacpac can be used to update a database with new or modified objects, to revert to a previous version of the database, or to provision an entirely new database.

Does Dacpac file contain data?

It is to be noted that DACPAC does not contain DATA and other server-level objects. The file can contains all object types which might be kept in SSDT project.


2 Answers

Use SqlPackage.exe parameters (since February 2015 release: New Advanced Publish Options to Specify Object Types to Exclude or Not Drop):

Here's the actual parameters we use in our deployment:

/p:DropObjectsNotInSource=True 
/p:ExcludeObjectTypes=Users;Logins;RoleMembership;Permissions

The first line cleans all, but the next line further refines what not to drop. This combination proved the most effective with us to drop all unnecessary objects, yet retain the login mappings as they were.

Detailed documentation of all the parameters and their possible values can be found from MSDN - SqlPackage.exe

like image 107
Tuukka Haapaniemi Avatar answered Nov 09 '22 11:11

Tuukka Haapaniemi


I ran into the same issue and used Pre/Post deployment scripts to reinsert users, permissions, roles, etc like the suggested blog post. However this became unmaintainable in the long run (users unable to authenticate during deployment, if the deployment fails permissions are not restored, security changes require going through source control and re-deployment).

Recently, I reevaluated the problem as we were migrating our deployment platform. With the DacFx API (and bug fixes) released, I was able to extend the deployment process in SSDT by creating a DeploymentPlanModifier. They provide an example for filtering objects on creation, with simple modifications I filter any drops for permission based object types (using /p:AdditionalDeploymentContributors argument).

[ExportDeploymentPlanModifier( UserMappingFilter.PlanFiltererContributorId, "1.0.0.0" )]
public class UserMappingFilter : DeploymentPlanModifier
{
    public const string PlanFiltererContributorId = "Dac.UserMappingFilter";

    protected override void OnExecute( DeploymentPlanContributorContext context )
    {
        DeploymentStep next = context.PlanHandle.Head;
        while( next != null )
        {
            DeploymentStep current = next;
            next = current.Next;

            DropElementStep dropStep = current as DropElementStep;
            if( dropStep != null && ShouldFilter( dropStep ) )
            {
                base.Remove( context.PlanHandle, dropStep );
            }
        }
    }

    private bool ShouldFilter( DropElementStep createStep )
    {
        TSqlObject target = createStep.TargetElement;


        if( target.ObjectType.Name == "RoleMembership" || target.ObjectType.Name == "User" || target.ObjectType.Name == "Role" )
        {
            return true;
        }


        return false;
    }
}
like image 37
DanStory Avatar answered Nov 09 '22 10:11

DanStory