Deploying signed assemblies in a single script

I’ve been writing a set of routines to control my database backups recently and one part of this was a CLR User Defined Data Type I’d created that allows some rudimentary file handling (with the appropriate security). By it’s very nature this assembly required the EXTERNAL ACCESS privilege and this is where the head-scratching came about.

Liking clean install scripts with minimal payloads (single scripts are easier to check into Source Code and deploy across multiple environments) I needed some method of encapsulating the deploy and ensuring the appropriate asymmetric key, users etc. were created. However normally this would require either the key file or the assembly DLL to be copied along with the .Sql script – and this would break my 1 script rule! So next idea was to create the asymmetric key from the assembly directly within Sql server – but I couldn’t do that until I’d installed the assembly which would fail because it required the EXTERNAL ACCESS privilege; Thus leading to another game of Chicken and Egg.

The solution was remarkably obvious – use a temporary SAFE assembly to create the asymmetric key then dispose of it directly afterwards – after all the assembly is only required at this juncture to create the asymmetric key.

The first section of the script deals with previous execution cleanups etc:

  1. Use RandomThunks
  2.  
  3. ------------/ Start stage 1 : Drop previous objects /--------------------------------------------------
  4.  
  5. If Exists ( Select 1 From sys.assembly_types As T Inner Join sys.schemas As S On T.schema_id = S.schema_id Where T.name = N'SqlFile' And S.Name = N'Maintenance.Database.Archives')
  6.     Drop Type [Maintenance.Database.Archives].SqlFile;
  7.  
  8. If Exists (Select 1 From sys.assemblies Where name = N'RandomThunks.CLR.SqlServerFileUDT' and is_user_defined = 1)
  9.     Drop Assembly [Skipjack.CLR.SqlServerFileUDT];
  10.  
  11. -- This next key should never exist when we run this as it's only use to create the asymmetric key before we Grant the External Access right to it.
  12.  
  13. If Exists (Select 1 From sys.assemblies Where name = N'TEMPORARY. DO NOT USE!' and is_user_defined = 1)
  14.     Drop Assembly [TEMPORARY. DO NOT USE!];
  15.     
  16. If Exists ( Select 1 From sys.server_principals Where name = 'SqlServerFileUDTUser' )
  17.     Drop Login SqlServerFileUDTUser;
  18.     
  19. If Exists ( Select 1 From sys.asymmetric_keys Where name = 'SqlServerFileUDTKey' )
  20.     Drop Asymmetric Key SqlServerFileUDTKey;
  21.  
  22. ------------/ End stage 1 : Drop previous objects /----------------------------------------------------

And the second part deploys the assembly with the requisite privilege:

  1. ------------/ Start stage 2 : Object Creation /--------------------------------------------------------
  2.  
  3. Declare @Assembly VarBinary(MAX);
  4. Set @Assembly = 0x4D5A9000030000000...
  5.  
  6. If Not Exists ( Select 1 From sys.schemas Where Name = 'Maintenance.Database.Archives')
  7.     Exec sp_ExecuteSql N'Create Schema [Maintenance.Database.Archives]';
  8.  
  9. -- Create a temporary assembly that we'll use to build the Asymmetric Key from
  10.  
  11. Create Assembly [TEMPORARY. DO NOT USE!] Authorization dbo
  12.     From @Assembly
  13.     With Permission_Set = Safe;
  14.  
  15. Create Asymmetric Key SqlServerFileUDTKey
  16.     From Assembly [TEMPORARY. DO NOT USE!];
  17.  
  18. -- Drop the temporary assembly - it's use is over.
  19.  
  20. Drop Assembly [TEMPORARY. DO NOT USE!];
  21.  
  22. Create Login SqlServerFileUDTUser
  23.     From Asymmetric Key SqlServerFileUDTKey;
  24.  
  25. -- This needs to be run at the Server level - encapsulating it in a sp_ExecuteSql allows the script to be DB name agnostic.
  26.  
  27. Exec sp_executeSql N'
  28.     Use master;
  29.  
  30.     Grant External Access Assembly
  31.         To SqlServerFileUDTUser;';
  32.  
  33. -- This is the real assembly
  34.  
  35. Create Assembly [Skipjack.CLR.SqlServerFileUDT] Authorization dbo
  36.     From @Assembly
  37.     With Permission_Set = External_Access;
  38.  
  39. Create Type [Maintenance.Database.Archives].SqlFile
  40.     External Name [RandomThunks.CLR.SqlServerFileUDT].SqlFile;
  41.  
  42. ------------/ End stage 2 : Object Creation /----------------------------------------------------------

0 comments:

Post a Comment