I’ve just spent some really fun hours trying to clean up users and logins from both our production and test-servers. Over the course of several years and restores from production to test, a number of orphaned users and logins have accumulated, that is, database users that do not have an associated login and logins that do not have any associated database users.
The following script attempts to identify both by extracting users (and user roles) from all databases and comparing them with the server logins. The output suggests the Sql statements to clean up the mess, but do peruse it throughly before executing!
Nuff said, here is the code:
- – Script to assist cleaning up orphaned Logins and Users on Sql Server
- – Query to extract Users and Roles
- DECLARE @sqlquery varchar(500)
- SET @sqlquery =
- 'SELECT DB_NAME() AS DatabaseName, P1.name AS UserName, P2.name AS UserRole FROM sys.database_role_members DRM '
- +'JOIN sys.database_principals p1 ON P1.principal_id = DRM.member_principal_id '
- +'JOIN sys.database_principals p2 ON P2.principal_id = DRM.role_principal_id '
- +'WHERE P1.type IN (''S'', ''U'') AND LEN(P1.sid) > 1'
- – Query to extract User-databases only, ie not master, tempdb etc
- DECLARE @dbname char(50)
- DECLARE c1 CURSOR READ_ONLY FOR
- SELECT Name FROM sys.databases WHERE LEN(owner_sid) > 1
- – Temporary table to hold all Database Users and Roles
- IF OBJECT_ID('tempdb..#USERNAMES') IS NOT NULL DROP TABLE #USERNAMES
- CREATE TABLE #USERNAMES
- DatabaseName varchar(100),
- UserName varchar(100),
- UserRole varchar(100)
- – Loop over databases with a cursor
- OPEN c1
- FETCH NEXT FROM c1 INTO @dbname
- WHILE @@FETCH_STATUS = 0
- PRINT 'Adding ' + @dbname
- EXEC('USE [' + @dbname + ']; ' + 'INSERT INTO #USERNAMES ' + @sqlquery);
- FETCH NEXT FROM c1 INTO @dbname;
- CLOSE c1 DEALLOCATE c1
- – Logins not used in any databases. NOTE a specific login is excepted
- SELECT Name, 'DROP LOGIN [' + Name + '];' AS Sql
- FROM sys.server_principals P
- LEFT OUTER JOIN #USERNAMES U ON U.UserName = P.Name
- WHERE type = 'S'AND is_disabled = 0 AND LEN(sid) > 1 AND U.DatabaseName IS NULL AND name NOT IN ('udvikler')
- – Users in databases without server Logins. NOTE a specific login is excepted
- SELECT DISTINCT DataBaseName, UserName, 'USE [' + DatabaseName + ']; DROP USER [' + UserName + '];' AS Sql
- FROM #USERNAMES U
- LEFT OUTER JOIN sys.server_principals P ON U.UserName = P.Name
- WHERE name IS NULL AND UserName NOT IN ('dbo')
- –SELECT * FROM #USERNAMES
The ability to restore a SQL Server database over and over can sometimes be handy in a test scenario, for instance when you need to test a schema-update for autodeployment through DbUp/Octopus – or you otherwise need to have your database in a known state prior to testing.
However this is quite often blocked because the database is in use, resulting in the above message.
My solution to this is to script the restore to run in single user mode like so:
- USE [master]
- ALTER DATABASE [BenchmarkDB] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
- RESTORE DATABASE [BenchmarkDB] FROM DISK = N'D:\BenchmarkDB.bak' WITH FILE = 1, MOVE N'BenchmarkDB_log' TO N'D:\Data\BenchmarkDB.ldf', NOUNLOAD, REPLACE,STATS = 5
- ALTER DATABASE [BenchmarkDB] SET MULTI_USER WITH ROLLBACK IMMEDIATE
To achieve the same in Management Studio then go to Options and check this box:
To check whether your database got correctly reset to Multiuser Mode, go to Database Properties > Options, and look at the “State” subgroup, “Restrict Access” item (you’ll need to scroll down to the bottom of the list to see it):
DbUp is a nice open source project that allows you to deploy Sql-scripts via a .Net executable, thus enabling deployment via a pipeline like Octopus Deploy. DbUp tracks which scripts have already been run on your database so you don’t have to worry about double-updates to your database.
The basic problems with idempotency and databases remain of course, but that is a completely different story and you still have to manage that.
DbUp has a nice fluent interface, but doing programmatic configuration unfortunately rather breaks that. It took me a while to figure out so here is how I managed to change the excution timeout in my DbUp deployment C# program for a long running script.
First use DeploymentChanges.To.SqlDatabase(connectionstring).WithScriptsEmbeddedInAssembly( … ) to create a variable which is an UpgradeEngineBuilder.
Then invoke the Configuration callback action on the UpgradeEngineBuilder like so:
c.ScriptExecutor.ExecutionTimeoutSeconds = 30 * 60; // 30 minutes in seconds
Console.WriteLine("Configure ExecutionTimeoutSeconds to " + c.ScriptExecutor.ExecutionTimeoutSeconds);
After that you can use the fluent interface again to invoke the Sql-script:
var upgradeEngine = upgradeEngineBuilder.LogToConsole().Build();
var result = upgradeEngine.PerformUpgrade();
This is a very annoying thing to encounter when connecting to a Remote Desktop on a machine in the network. That green bar just goes round and round for ages! I don’t know why this happens – in my case it is a machine in another domain that I log in to:
Let us say that this machine resides on IP address 10.20.30.40, then the following Powershell snippet will fix your woes:
- Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Terminal Server Client\Servers\10.20.30.40' -Name UsernameHint -Value ''
- mstsc.exe /v:10.20.30.40
There are other ways to go about this, such as the REG command in a batch-file, but these days doing .bat files is just soooo old school :)
If you have SQL Server database project in Visual Studio that needs to reference another database, ie a View containing a 3 level reference like [OEDB].[dbo].[Inventory], you need to reference the other database by right-clicking “References” and choosing “Add Database Reference”.
This brings up this screen:
Near the bottom is something called “Database variable”. If this is filled (which is the default) all references to that database in the imported SQL must go via the variable, as can be seen from the example just below [$(oedb)].[Schema1].[Table1] the “Database variable” field.
If you fail to correct occurrences in your SQL from [DatabaseName] to [$(DatabaseName)] you will probably experience an error list with hundreds of SQL71561 errors like this:
SQL71561: View: [dbo].[qryCreateYMD] contains an unresolved reference to an object. Either the object does not exist or the reference is ambiguous because it could refer to any of the following objects: [OEDB].[dbo].[Inventory].[Id], [OEDB].[dbo].[Inventory].[OEDB]::[dbo].[Inventory].[Id] or [OEDB].[dbo].[Inventory].[OEDB]::[dbo].[Inventory].[Id]
In my mind this wasn’t really the expected behavior, but what you can do to just use the references you have without changing your views, is to simply clear out the “Database variable” field:
As can be seen from the Example usage, references wil now work as expected…
This is basically what is explained here (when I finally found the explanation), but I wanted to add the screenshots of how this works.
Setting up a new development box for myself I had forgotten all about the necessity to use the aspnet_regiis.exe –i command. The information is fairly easily available, but with this post I’d like to cut away the fat and stick to a very easy how-to.
On my Windows 8.1 aspnet_regiis resulted in this message:
Microsoft (R) ASP.NET RegIIS version 4.0.30319.33440
Administration utility to install and uninstall ASP.NET on the local machine.
Copyright (C) Microsoft Corporation. All rights reserved.
Start installing ASP.NET (4.0.30319.33440).
This option is not supported on this version of the operating system. Administrators should instead install/uninstall ASP.NET 4.5 with IIS8 using the "Turn Windows Features On/Off" dialog, the Server Manager management tool, or the dism.exe command line tool. For more details please see http://go.microsoft.com/fwlink/?LinkID=216771.
Finished installing ASP.NET (4.0.30319.33440).
Googling around a bit more turned up this link that explains how to go to the Windows Features (via appwiz.cpl) to activate .Net 4.5 on IIS:
Or to run this from an administrative prompt:
dism /online /enable-feature /all /featurename:IIS-ASPNET45
Please note the /all parameter which is not mentioned in the KB article, but necessary to automatically include all the necessary components.
In order to get my webservices running I have spent more hours on struggling with the IIS beast. It appears that (according to this) you have to have to switch on yet another feature:
Or, if you want to automate all this, more dism commands:
dism /online /enable-Feature /all /FeatureName:WCF-HTTP-Activation
dism /online /enable-Feature /all /FeatureName:WCF-HTTP-Activation45
I have occasionally found it convenient to execute a command against all the databases on a server, such as ensuring that all the databases on my test-server have RECOVERY MODE set to SIMPLE, and performing a SHRINK operation to reclaim space on the disk for each of them. Googling this problem there were quite a lot of samples like this, but here is what worked for me:
DECLARE @dbname char(50)
DECLARE c1 CURSOR READ_ONLY FOR
WHERE name NOT IN ('master', 'tempdb', 'model', 'msdb')
FETCH NEXT FROM c1 INTO @dbname
WHILE @@FETCH_STATUS = 0
PRINT 'Opdaterer ' + @dbname
EXEC('ALTER DATABASE ' + @dbname + 'SET RECOVERY SIMPLE;');
EXEC('DBCC SHRINKDATABASE(N'''+ @dbname + ''');');
FETCH NEXT FROM c1 INTO @dbname;
CLOSE c1 DEALLOCATE c1
This is of course very easy to modify for other purposes. Some posts warned against bad performance on cursors, but this isn’t really an issue in this scenario.