Last active
December 24, 2015 22:39
-
-
Save mbourgon/6873939 to your computer and use it in GitHub Desktop.
EN_to_TFS_2 - SQL script to call the powershell script based off of your Event Notifications table which has the changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
USE eventnotificationrec | |
GO | |
CREATE PROCEDURE [dbo].[EN_TFS_Checkin] | |
AS | |
/* | |
Purpose - using Event Notifications, determines which objects have changed (sub-database-level, for now). | |
Once list has been generated, repeatedly call a custom powershell script that will | |
"get" and "checkout" the object from TFS if it exists, overwrite it with a current copy (scripted via SMO), | |
then "checkin" the new object with a comment based on the loginname | |
mdb 2013/10/04 1.00 Works well (albeit a little slowly)and handled multiple days' worth of checkins. | |
Error items were due because of short-term objects that are created then dropped. | |
mdb 2013/10/11 1.10 Using TFSsecurity to find valid users. If user is valid, use their name to checkin. | |
mdb 2013/10/22 1.11 Changing INDEX to include table. Makes it easier to read in history. | |
mdb 2013/11/08 1.12 Let's handle extended properties! | |
mdb 2013/12/03 1.13 the PS code can now script out database creation. Adding things like ALTER_DATABASE, | |
and dealing with full-database checkins. | |
mdb 2013/12/04 1.14 fixing author bug | |
To be done still: | |
* Script server-level | |
* Database-level: perms and ALTER/CREATE_SCHEMA | |
* Logic for Drop and Renames. How do we want to handle that? (TF.exe RENAME, either to new name or "DROPPED - oldname") | |
* Better logic to skip events? We would conceivably need more table(s) to hold the exception types. | |
sample of items not yet done | |
add_role_member --must be at the database level(?) | |
ADD_SERVER_ROLE_MEMBER | |
ALTER_ASSEMBLY | |
ALTER_CREDENTIAL | |
*/ | |
SET NOCOUNT ON | |
DECLARE @cmd VARCHAR(8000), @min INT, @max INT, @EventType VARCHAR(128), @Time_of_last_run datetime, @now DATETIME | |
, @loginname VARCHAR(128), @loginname_temp VARCHAR(128), @TargetObjectType VARCHAR(128) | |
--mdb 2013/11/08 we didn't add ObjectType as it would screw up our dupe-elimination, but I need TargetObjectType for ExtendedProperties | |
DECLARE @change_list TABLE (id INT IDENTITY, ServerName VARCHAR(128), EventType VARCHAR(128), LoginName VARCHAR(128) | |
, DatabaseName VARCHAR(128), SchemaName VARCHAR(128), ObjectName VARCHAR(128) | |
, TargetObjectName VARCHAR(128), TargetObjectType VARCHAR(128)) | |
DECLARE @valid_tfs_logins TABLE (id INT IDENTITY, loginname VARCHAR(128)) | |
IF OBJECT_ID('tempdb..#holding_tank_for_errors') IS NOT NULL | |
DROP TABLE #holding_tank_for_errors | |
CREATE TABLE #holding_tank_for_errors (id INT IDENTITY, resultant VARCHAR(800)) | |
if object_id('tempdb..#tfssecurity') is not null | |
DROP TABLE #tfssecurity | |
CREATE TABLE #tfssecurity (id INT IDENTITY, resultant nVARCHAR(800)) | |
--TFSSecurity. Ensures the user is valid by querying for "Team Foundation Valid Users" (though you may have other groups). | |
-- If valid, uses their name for checkin. If not, uses a default (set in PS) and adds it to the Comment | |
INSERT INTO #tfssecurity | |
(resultant) | |
EXEC master..xp_cmdshell 'C:\tfs_cli\app_2013\tfssecurity /imx "Team Foundation Valid Users" /collection:http://yourtfserver:8080/tfs/DefaultCollection' | |
--find only users, parsing into a comparable field. | |
INSERT INTO @valid_tfs_logins (loginname) | |
SELECT | |
SUBSTRING(resultant, CHARINDEX(']',resultant)+2, CASE CHARINDEX('(', resultant) WHEN 0 THEN 200 ELSE CHARINDEX('(', resultant) - CHARINDEX(']',resultant)-3 end) | |
FROM #tfssecurity | |
WHERE resultant LIKE '%[[]U]%' | |
--go back 1 day, the first time you run. | |
IF (SELECT COUNT(*) FROM eventnotificationrec.dbo.TFS_Last_Time_Run) = 0 | |
BEGIN | |
INSERT INTO eventnotificationrec.dbo.TFS_Last_Time_Run VALUES (GETDATE()-1) | |
END | |
--find the last time we ran, so we're only getting in that range | |
SET @time_of_last_run = (select MAX(last_time_run) | |
FROM eventnotificationrec.dbo.TFS_Last_Time_Run) | |
--located here, as soon as possible before the query to get the list of events. We could change it to use the ID field instead. | |
SELECT @now = GETDATE() | |
SELECT @time_of_last_run AS last_run_time, @now AS now_run_time | |
-- can simplify this further. | |
-- Multiple changes to the same object don't need multiple checkins. Indexes, for instance, only need 1 checkin of the table. | |
-- will probably need a case statement since (for indexes, in this case) each would be a different object w/same targetobject | |
-- as of 1.1, each index change requires a separate checkin. | |
INSERT INTO @change_list | |
SELECT ServerName, EventType, LoginName, DatabaseName, SchemaName, ObjectName, TargetObjectName, TargetObjectType | |
FROM eventnotificationrec.dbo.ENAudit_View | |
WHERE EventType IN | |
( | |
--these lines are blocked out by the groupings below, use them to make it easier to read this. | |
'ALTER_FUNCTION','ALTER_PROCEDURE','ALTER_TABLE','ALTER_VIEW','CREATE_FUNCTION', | |
'CREATE_PARTITION_FUNCTION', 'CREATE_PARTITION_SCHEME', 'CREATE_PROCEDURE', 'CREATE_TABLE', 'CREATE_VIEW' | |
,'ALTER_TABLE' | |
,'Create_Type', 'Alter_Type' | |
,'ALTER_TRIGGER', 'ALTER_INDEX', 'ALTER_SPATIAL_INDEX' | |
,'CREATE_TRIGGER', 'CREATE_INDEX', 'CREATE_SPATIAL_INDEX' | |
,'ALTER_EXTENDED_PROPERTY','CREATE_EXTENDED_PROPERTY','DROP_EXTENDED_PROPERTY' | |
,'ALTER_AUTHORIZATION_DATABASE','ALTER_DATABASE' | |
) | |
AND NOT (EventType = 'ALTER_INDEX' AND commandtext LIKE '%reorg%') --ignore reorgs | |
AND NOT (EventType = 'ALTER_INDEX' AND commandtext LIKE '%REBUILD%' AND commandtext NOT LIKE '%fillfactor%') | |
AND insert_datetime > @time_of_last_run | |
AND insert_datetime <= @now | |
GROUP BY ServerName, EventType, LoginName, DatabaseName, SchemaName, ObjectName, TargetObjectName, TargetObjectType | |
ORDER BY MAX(insert_datetime) | |
select count(*) from @change_list AS number_of_items_to_do | |
--now that we have a list to process, invoke the powershell script for each. | |
--The powershell script does the work; this just tells it what's changed and needs to be done. | |
SELECT @min = MIN(id), @max = MAX(id) FROM @change_list | |
WHILE @min <= @max | |
BEGIN | |
SET @EventType = NULL | |
SET @loginname_temp = NULL | |
SET @loginname = NULL | |
SET @TargetObjectType = NULL | |
--Using TFSSecurity block above, change the loginname to be the actual loginname if they're a valid user | |
SELECT @loginname_temp = loginname FROM @change_list WHERE id = @min | |
IF EXISTS (SELECT * FROM @valid_tfs_logins WHERE loginname = @loginname_temp) | |
BEGIN | |
SET @loginname = ' -author "' + @loginname_temp + '" -comment "' | |
END | |
ELSE | |
BEGIN | |
SET @loginname = ' -comment "' + ISNULL(@Loginname_Temp,'blank.username') + ' ----- ' | |
END | |
--clear the error trap - we have 2, one transient and one permanent | |
TRUNCATE TABLE #holding_tank_for_errors | |
--easier to select this once; makes below code more readable. | |
SELECT @EventType = EventType, @TargetObjectType = TargetObjectType FROM @change_list WHERE id = @min | |
--basic call, comment has loginname & eventtype | |
IF @EventType IN ('ALTER_FUNCTION','ALTER_PROCEDURE','ALTER_VIEW','CREATE_FUNCTION', | |
'CREATE_PARTITION_FUNCTION', 'CREATE_PARTITION_SCHEME', 'CREATE_PROCEDURE', 'CREATE_TABLE', 'CREATE_VIEW') | |
or (@EventType IN ('ALTER_EXTENDED_PROPERTY','CREATE_EXTENDED_PROPERTY','DROP_EXTENDED_PROPERTY') AND @TargetObjectType = '') | |
BEGIN | |
SELECT @cmd = 'powershell -file c:\tfs_cli\sql_script_to_tfs.ps1 -server ' + servername | |
+ ' -database "' + databasename + '" -schematoscript "' + SchemaName + '" -objecttoscript "' + ObjectName + '"' | |
+ @LoginName + EventType + ' ' + ObjectName | |
+ CASE | |
WHEN TargetObjectName = '' THEN '' | |
WHEN TargetObjectName IS NOT NULL THEN ' on ' + TargetObjectName | |
ELSE '' END + '"' | |
FROM @change_List WHERE id = @min | |
END | |
--when we want to include the actual change in the comment, to get specifics like ALTER TABLE ADD COLUMN | |
ELSE | |
IF @EventType IN ('ALTER_TABLE') | |
BEGIN | |
SELECT @cmd = 'powershell -file c:\tfs_cli\sql_script_to_tfs.ps1 -server ' + servername | |
+ ' -database "' + databasename + '" -schematoscript "' + SchemaName + '" -objecttoscript "' + ObjectName + '"' | |
+ @LoginName + EventType + ' ' + ObjectName + '"' | |
FROM @change_List WHERE id = @min | |
END | |
--objects without schemas, so searching the SMO to match SchemaName won't find anything | |
ELSE | |
IF @EventType IN ('CREATE_TYPE', 'ALTER_TYPE') | |
BEGIN | |
SELECT @cmd = 'powershell -file c:\tfs_cli\sql_script_to_tfs.ps1 -server ' + servername | |
+ ' -database ' + databasename + ' -objecttoscript ' + ObjectName | |
+ @LoginName + EventType + ' ' + ObjectName + '"' | |
FROM @change_List WHERE id = @min --when scripted, they have no schema | |
END | |
--objects that are actually "part" of another object, and thus use the TargetObject to determine what to script out. | |
ELSE | |
IF @EventType IN ('ALTER_TRIGGER', 'ALTER_INDEX', 'ALTER_SPATIAL_INDEX', 'CREATE_TRIGGER', 'CREATE_INDEX', 'CREATE_SPATIAL_INDEX') | |
or (@EventType IN ('ALTER_EXTENDED_PROPERTY','CREATE_EXTENDED_PROPERTY','DROP_EXTENDED_PROPERTY') AND @TargetObjectType <> '') | |
BEGIN | |
SELECT @cmd = 'powershell -file c:\tfs_cli\sql_script_to_tfs.ps1 -server ' + servername | |
+ ' -database "' + databasename + '" -schematoscript "' + SchemaName + '" -objecttoscript "' + TargetObjectName + '"' | |
+ @Loginname + EventType + ' ' + ISNULL(ObjectName,'') + '.' + TargetObjectName + '"' | |
FROM @change_List WHERE id = @min | |
END | |
--database-level objects, such as CREATE or changes to a database | |
ELSE | |
IF @EventType IN ('ALTER_AUTHORIZATION_DATABASE','ALTER_DATABASE','CREATE_DATABASE') | |
BEGIN | |
SELECT @cmd = 'powershell -file c:\tfs_cli\sql_script_to_tfs.ps1 -server ' + servername | |
+ ' -database "' + databasename + '"' | |
+ @LoginName + EventType + ' ' + databasename + '"' | |
FROM @change_List WHERE id = @min | |
END | |
--run the powershell command, saving errors to a temp table | |
RAISERROR (@cmd, 0, 1) WITH NOWAIT | |
INSERT INTO #holding_tank_for_errors | |
EXEC master..xp_cmdshell @cmd | |
--if any errors during execution, save to a permanent table to look at later. | |
IF EXISTS | |
( | |
SELECT * | |
FROM #holding_tank_for_errors | |
WHERE resultant LIKE '%sql_script_to_tfs.ps1%' | |
) | |
BEGIN | |
INSERT INTO eventnotificationrec.dbo.TFS_Checkin_Errors (time_job_run, original_id, resultant) | |
SELECT GETDATE(), id, resultant | |
FROM #holding_tank_for_errors ORDER BY id | |
END | |
SET @min = @min + 1 | |
END | |
--and now that we're successful, add a record so we know when we ran. | |
INSERT INTO eventnotificationrec.dbo.TFS_Last_Time_Run (last_time_run) VALUES (@now) | |
GO |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment