<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://sqlblog.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Search results matching tags 'msbuild' and 'Security'</title><link>http://sqlblog.com/search/SearchResults.aspx?o=DateDescending&amp;tag=msbuild,Security&amp;orTags=0</link><description>Search results matching tags 'msbuild' and 'Security'</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>A strategy for managing security for different environments using the Database Development Tools in Visual Studio 2010</title><link>http://sqlblog.com/blogs/jamie_thomson/archive/2010/07/21/a-strategy-for-managing-security-for-different-environments-using-the-database-development-tools-in-visual-studio-2010.aspx</link><pubDate>Wed, 21 Jul 2010 20:59:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:27217</guid><dc:creator>jamiet</dc:creator><description>&lt;h3&gt;Intro&lt;/h3&gt;
&lt;p&gt;Of late I have been getting down and dirty with the Database Development tools in Visual Studio 2010. You may know this feature set by one of the plethora of other names it has had over recent years such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visual Studio Team System for Database Professionals &lt;/li&gt;
&lt;li&gt;DBPro &lt;/li&gt;
&lt;li&gt;Datadude &lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;For the rest of this post I’ll stick with the colloquial name that most people seem to recognise – datadude.&lt;/p&gt;
&lt;p&gt;Regardless of which moniker you prefer I’m hoping you recognise the feature set that I am referring to here which includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Schema Compare &lt;/li&gt;
&lt;li&gt;Data Compare &lt;/li&gt;
&lt;li&gt;Offline database development &lt;/li&gt;
&lt;li&gt;T-SQL refactoring &lt;/li&gt;
&lt;li&gt;Database unit testing/Data generation &lt;/li&gt;
&lt;li&gt;MSBuild integration &lt;/li&gt;
&lt;li&gt;various other things… &lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;I plan to write a detailed blog post in the coming weeks talking about my overall experiences with datadude but in the meantime I’m writing this blog post to focus on how I have chosen to manage security; by which I mean:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;logins &lt;/li&gt;
&lt;li&gt;users &lt;/li&gt;
&lt;li&gt;role membership &lt;/li&gt;
&lt;li&gt;user and role permissions &lt;/li&gt;&lt;/ul&gt;
&lt;hr&gt;

&lt;h3&gt;Security in Post-Deployment scripts&lt;/h3&gt;
&lt;p&gt;Datadude has built-in support for managing all of this stuff as we can see from this screenshot:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/jamie_thomson/image_4F758EED.png"&gt;&lt;img style="border-width:0px;display:inline;" class="wlDisabledImage" title="users roles logins in datadude" border="0" alt="users roles logins in datadude" src="http://sqlblog.com/blogs/jamie_thomson/image_thumb_0D8EA99F.png" width="676" height="433"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Notice that we have created a &lt;strong&gt;user&lt;/strong&gt;, a &lt;strong&gt;login &lt;/strong&gt;for that user and a &lt;strong&gt;role &lt;/strong&gt;into which we later want to add the user. They exist as explicit objects within our project which means that datadude “knows” about them and that has a number of advantages including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Datadude can include these objects in a schema compare &lt;/li&gt;
&lt;li&gt;Datadude can do dependency tracking thus enabling offline development (e.g. If the login that is referenced in the CREATE USER statement does not exist then a design-time error will be raised) &lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;That should be enough to justify using datadude to manage users and roles however I have come across a limitation that prohibits us from doing so; we do not deploy the same security model to each of our environments. The reasons for this are very familiar to you I’m sure, we have people that require different permissions on different environments (e.g. our developers will want to be able to issue DDL and UPDATE/INSERT/DELETE on our development environment but we definitely don’t want them to have the same on production).&lt;/p&gt;
&lt;p&gt;As far as I can discern datadude does not have a good solution for dealing with this situation. I have not found a way of conditionally deploying objects depending on which environment we are deploying to. Actually that is not altogether true, we could have a Visual Studio configuration for each environment and include/exclude objects as we choose but that doesn’t play well with the build and deploy model of msbuild which requires that you build each one of those configurations; each of our builds takes about 10 minutes which, when multiplied by the number of environments, results in a total build time that takes too long for our continuous integration (CI) usage.&lt;/p&gt;
&lt;p&gt;Hence we needed a better way to deploy our security model – we chose to use PostDeployment scripts. PostDeployment scripts do exactly what they say on the tin – they are scripts that get run after datadude has deployed the objects that it knows about. They get called (using SQLCMD syntax) from the &lt;font face="Courier New"&gt;Script.PostDeployment.sql &lt;/font&gt;file that exists in every datadude project:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/jamie_thomson/cid_image002_png01CB274D_4ACF5E66.png"&gt;&lt;img style="border-width:0px;display:inline;" class="wlDisabledImage" title="post deployment script solution explorer" border="0" alt="post deployment script solution explorer" src="http://sqlblog.com/blogs/jamie_thomson/cid_image002_png01CB274D_thumb_6A120539.png" width="303" height="221"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My chosen approach is to have a script called &lt;font face="Courier New"&gt;SecurityAdditionsWrapper.sql&lt;/font&gt; that gets called from &lt;font face="Courier New"&gt;Script.PostDeployment.sql&lt;/font&gt;. &lt;font face="Courier New"&gt;SecurityAdditionsWrapper.sql &lt;/font&gt;checks a SQLCMD variable to determine which environment is being deployed to and then calls the appropriate &lt;font face="Courier New"&gt;SecurityAdditionsXXX.sql &lt;/font&gt;script that is responsible for deploying the security model for that environment (where XXX is the environment).&lt;/p&gt;
&lt;p&gt;I then have 3 subfolders that contain all the individual CREATE USER, role membership and permissions scripts that get called from &lt;font face="Courier New"&gt;SecurityAdditionsXXX.sql&lt;/font&gt; as and when required.&lt;/p&gt;
&lt;p&gt;The screenshot below shows the contents of &lt;font face="Courier New"&gt;Script.PostDeployment.sql &lt;/font&gt;&amp;amp; &lt;font face="Courier New"&gt;SecurityAdditionsWrapper.sql&lt;/font&gt; and also the files as they exist in Solution Explorer:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/jamie_thomson/cid_image003_png01CB274F_7DBEEECD.png"&gt;&lt;img style="border-width:0px;display:inline;" class="wlDisabledImage" title="!cid_image003_png@01CB274F" border="0" alt="!cid_image003_png@01CB274F" src="http://sqlblog.com/blogs/jamie_thomson/cid_image003_png01CB274F_thumb_5969E47E.png" width="778" height="614"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some things to notice:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I have a SQLCMD variable called $(DeployType) that will contain the value “DEV”, “UAT” or “PRD” &lt;/li&gt;
&lt;li&gt;All of my security scripts exist in Post-Deployment&amp;nbsp; -&amp;gt; SecurityAdditions &lt;/li&gt;
&lt;li&gt;In the commented-out code you can see that I was trying to be clever and dynamically build the name of the file to be executed using the $(DeployType) variable – the thinking being that if we later added a new environment type I wouldn’t need to change this script. Unfortunately even though this is valid SQLCMD syntax it does not work in datadude; I have reported that issue on Connect as a bug (with repro) at &lt;a href="https://connect.microsoft.com/VisualStudio/feedback/details/575383/datadude-valid-sqlcmd-syntax-throws-error-at-build-time"&gt;[Datadude] Valid SQLCMD syntax throws error at build time&lt;/a&gt;. &lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;You may be wondering how we get a value into that $(DeployType) variable when using msbuild. Well, its pretty simple when you know how. First you need to include the variable in the sqlcmdvars file for your datadude project:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/jamie_thomson/cid_image004_png01CB2755_5A129AA8.png"&gt;&lt;img style="border-width:0px;display:inline;" class="wlDisabledImage" title="!cid_image004_png@01CB2755" border="0" alt="!cid_image004_png@01CB2755" src="http://sqlblog.com/blogs/jamie_thomson/cid_image004_png01CB2755_thumb_0E46C3EF.png" width="430" height="272"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You then need to pass in the appropriate value to that variable at deploy-time. We are using &lt;a href="http://msdn.microsoft.com/en-us/library/dd193258.aspx"&gt;VSDBCMD.exe&lt;/a&gt; to run our deployments and the &lt;a href="http://msdn.microsoft.com/en-us/library/dd193283.aspx"&gt;syntax to pass in a value for a variable&lt;/a&gt; is /p:PropertyName=PropertyValue. We are calling VSDBCMD.exe from inside an msbuild script which therefore looks like this:&lt;/p&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&lt;font color="#0000ff"&gt;&amp;lt;Exec&lt;/font&gt; &lt;font color="#ff0000"&gt;Command&lt;/font&gt;&lt;font color="#9b00d3"&gt;="..\Tools\VSDBCMD\vsdbcmd.exe /Action:Deploy /ConnectionString:'Data Source=$(DatabaseServer);Integrated Security=True;Pooling=False' /DeployToDatabase:+ /ManifestFile:.\MyDB.deploymanifest &lt;strong&gt;/p:DeployType=&amp;amp;quot;$(DeployType)&amp;amp;quot&lt;/strong&gt;; "&lt;/font&gt; &lt;font color="#0000ff"&gt;/&amp;gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;$(DeployType) is of course also a property within our msbuild file, which I am drawing attention to in the bold section above.&lt;/p&gt;
&lt;h3&gt;Wrap-Up&lt;/h3&gt;
&lt;p&gt;In conclusion, I am not saying that this is the &lt;i&gt;correct&lt;/i&gt; way to deploy a SQL Server security model using datadude, it just happens to be the method that I have chosen. I would really like to how people have solved this using datadude so if you have any anecdotes to share please put them in the comments.&lt;/p&gt;
&lt;hr&gt;

&lt;h3&gt;One more thing…&lt;/h3&gt;
&lt;p&gt;Cranking all of those files out by hand is a laborious undertaking, especially if you have lots of users with lots of permissions. In our case we introduced datadude to be used with an existing system hence we already had all our security setup on existing servers but we had no good way of getting it into the structure that I explained above. Powershell and SQLCMD to the rescue!&lt;/p&gt;
&lt;p&gt;I have produced a series of scripts that interrogate a named instance and produce all of the files that I explain above. Namely:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A file for each user which gets dropped into %SolutionRoot%\%ProjectName%\Scripts\Post-Deployment\Users\ &lt;/li&gt;
&lt;li&gt;A file for each principle that issues all the GRANT/DENY EXECUTE/SELECT/INSERT/UPDATE/DELETE for that principle, it gets dropped into %SolutionRoot%\%ProjectName%\Scripts\Post-Deployment\PermissionsSets\ &lt;/li&gt;
&lt;li&gt;A file that specifies all of the role memberships, for all database principles. &lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Creating the CREATE USER file and the file containing the permissions for each principle is&amp;nbsp;quite involved. For that we need some nested loops that loop over (a) all the databases for which you want to scripts users and (b) all the principles in each database. It then dumps the appropriate files into the appropriate folders and then you have the simple task of adding them to your project and calling them from your Post Deployment script.&lt;/p&gt;
&lt;p&gt;The files are stored in a zip folder on my SkyDrive at &lt;a title="http://cid-550f681dad532637.office.live.com/self.aspx/Public/BlogShare/20100721/GeneratePermissions.zip" href="http://cid-550f681dad532637.office.live.com/self.aspx/Public/BlogShare/20100721/GeneratePermissions.zip"&gt;http://cid-550f681dad532637.office.live.com/self.aspx/Public/BlogShare/20100721/GeneratePermissions.zip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To run the Powershell script simply unzip all the files into the root folder of your solution and issue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font face="Courier New"&gt;&amp;gt;GeneratePermissions.ps1 -SQLInstance instanceName -Environment XXX&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;changing SQLInstance &amp;amp; XXX accordingly (the &lt;font face="Courier New"&gt;$Environment&lt;/font&gt; variable is simply a suffix that is added to the names of some of the generated files). The Powershell script contains more detailed notes within it in the comments section at the top.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/jamie_thomson/image_0A78F952.png"&gt;&lt;img style="border-width:0px;display:inline;" class="wlDisabledImage" title="powershell script comments" border="0" alt="powershell script comments" src="http://sqlblog.com/blogs/jamie_thomson/image_thumb_62664A32.png" width="739" height="273"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hope this helps!&lt;/p&gt;
&lt;p&gt;Disclaimer: I’m giving this away for free and I won’t be supporting it so use at your own peril (in other words, don’t come here complaining that it doesn’t work on your environment). On the other hand if you find a problem and are able to fix it – please let me know &lt;img class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://sqlblog.com/blogs/jamie_thomson/wlEmoticonsmile_19CC5B61.png"&gt;&lt;/p&gt;
&lt;p&gt;UPDATE: I did actually find some problems with the scripts and have now uploaded a new set. I'm confident that they'll work this time.&lt;/p&gt;
&lt;p&gt;UPDATE 2: I've updated the scripts again. They now create the SecurityAdditionsXXX.sql file as well as the individual CREATE USER &amp;amp; permissions files;&amp;nbsp;SecurityAdditionsXXX.sql contains all of the calls to the relevant scripts for environment XXX.&lt;/p&gt;
&lt;p&gt;SecurityAdditionsXXX.sql also issues all of the role membership statements as well. So just to clarify - you now no longer have a need to run the script that generates the role memberships - GeneratePermissions.ps1 takes care of everything.&lt;/p&gt;&lt;p&gt;UPDATE 3: Peter Schott has taken this, adapted it for hiw own needs, and sent it back to me so that I can share it here. His modified version of the scripts is at &lt;a href="http://cid-550f681dad532637.office.live.com/self.aspx/Public/BlogShare/20100721/GeneratePermissions%5E_PeterSchott.zip"&gt;http://cid-550f681dad532637.office.live.com/self.aspx/Public/BlogShare/20100721/GeneratePermissions%5E_PeterSchott.zip&lt;/a&gt;&amp;nbsp;and includes these changes (mainly to deal with roles):&lt;/p&gt;&lt;p&gt;1. Changed the Powershell script:&lt;br&gt;&amp;nbsp; * Added a section to handle role permissions. (really don't care for the way VS does it)&lt;br&gt;&amp;nbsp; * Tweaked the Database Principle section - for some reason, the variable in the PS script I extracted wasn't set properly so I just changed the names around&lt;br&gt;&amp;nbsp; * Added a bunch of lines of code to add to the Array. My Powershell's not too strong so wasn't sure about the best way to add new DBs/Projects to the DB Array. I ended up doing a bunch of sets. I know it's not ideal, but I just wanted to get it done one time.&lt;br&gt;&amp;nbsp; * Tweaked the section that creates the Users. If one of those logins doesn't exist, the grant permissions script would stop. Now I check to make sure that the login exists before the script runs.&amp;nbsp;I sometimes get empty (Login = [])&amp;nbsp; sections. No ideas on that yet. I fix or delete that file as appropriate.&lt;br&gt;&lt;br&gt;2. Added a "GetDatabaseRoleList.sql" file.&amp;nbsp; Gets the roles for the DB so we can run it through the Permissions file&lt;br&gt;&lt;br&gt;3. Permissions SQL file. I tweaked this with a query that UNIONs the object permissions with the Schema permissions. I didn't break it down further &lt;br&gt;like you did, but our permissions are so messed up right now that it doesn't matter much to me. (we have &lt;i&gt;far&lt;/i&gt; too many permissions granted to &lt;br&gt;individual users instead of roles because the prior DBA didn't understand the point of DB Roles for permissions and even then far too many permissions because we had a job that nightly granted all permissions on all objects to certain usernames :-P&amp;nbsp; )&lt;br&gt;&lt;/p&gt;&lt;p&gt;And one more change that needs to be made which Peter informed me of later: &lt;/p&gt;&lt;p&gt;Ended up changing the GeneratePermissions.ps1 file to change all of the Out-File &lt;br&gt;to "Out-File -width 500 ".&lt;br&gt;&lt;br&gt;After doing that, the permissions weren't truncated at 80 characters.&amp;nbsp; Also tweaked the Invoke-SQLCmd functions to use -MaxCharLength 500 so the output line would be long enough. That may have been overkill or there may be a better way to handle, but some of my permission lines were definitely longer than the allowed line lengths.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://twitter.com/jamiet"&gt;@Jamiet&lt;/a&gt;&lt;/p&gt;P.S. I have used Powershell in the past but this is the first time I have really got into the inner workings of it and I’ve got to tell you – its just fantastic. The productivity you get is just incredible – if you don’t believe me have a look at the script herein and see how easy it is to generate a dataset from SQL Server and then loop over it inside your script. Its two lines of code - really fantastic stuff – I recommend any SQL Server pro go out and spend some time getting to know Powershell. </description></item></channel></rss>