Automating SQL Server instance and Database Inventory preparation
In our environment we have a requirement for Automating SQL Server instance and Database Inventory preparation. We tried different methods and came-up with the T-SQL scripts that helped us in creating the environment map. This script will capture the internal sql server details and stores on a centralized sql server database from where we can build a SSRS report that showcases the database environment map.
We need to create tables and corresponding procedures. Table creation:
- Environment: Store data about available environments. Ex: Dev, Test, Prod etc
- Servers: Host information Ex: Windows servers
- Instance_Inventory: SQL Server instance details
- DB Inventory: Database information on each instance
Script for Table Creation:
--Script to create Inventory Tables: /****** Object: Table [dbo].[Environment] Script Date: 01/04/2011 06:30:22 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[Environment]( [ID] [tinyint] IDENTITY(1,1) NOT NULL, [Name] [varchar](30) NOT NULL, [Deleted] [bit] NOT NULL DEFAULT ((0)), PRIMARY KEY CLUSTERED ( [ID] ASC )WITH(PAD_INDEX =OFF,STATISTICS_NORECOMPUTE =OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS =ON,ALLOW_PAGE_LOCKS =ON)ON [PRIMARY] )ON [PRIMARY] GO SET ANSI_PADDING OFF GO /****** Object: Table [dbo].[Servers] Script Date: 01/04/2011 06:30:30 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[Servers]( [ID] [smallint] IDENTITY(1,1) NOT NULL, [Server_Name] [varchar](100) NOT NULL, [OS_Version] [varchar](100) NULL, [EnvironmentID] [tinyint] NULL, [IPAddress] [varchar](20) NULL, [Deleted] [bit] NOT NULL DEFAULT ((0)), PRIMARY KEY CLUSTERED ( [ID] ASC )WITH(PAD_INDEX =OFF,STATISTICS_NORECOMPUTE =OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS =ON,ALLOW_PAGE_LOCKS =ON)ON [PRIMARY] )ON [PRIMARY] GO SET ANSI_PADDING OFF GO /****** Object: Table [dbo].[InstanceInventory] Script Date: 01/04/2011 06:30:25 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[InstanceInventory]( [ID] [smallint] IDENTITY(1,1) NOT NULL, [ServerID] [smallint] NOT NULL, [InstanceName] [varchar](100) NOT NULL, [Version] [varchar](100) NULL, [Edition] [varchar](100) NULL, [InsertedOn] [datetime] NOT NULL DEFAULT (getdate()), [Disabled] [bit] NOT NULL DEFAULT ((0)), PRIMARY KEY CLUSTERED ( [ID] ASC )WITH(PAD_INDEX =OFF,STATISTICS_NORECOMPUTE =OFF,IGNORE_DUP_KEY=OFF,ALLOW_ROW_LOCKS =ON,ALLOW_PAGE_LOCKS =ON)ON [PRIMARY] )ON [PRIMARY] GO SET ANSI_PADDING OFF GO /****** Object: Table [dbo].[DBInventory] Script Date: 01/04/2011 06:30:19 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[DBInventory]( [ID] [smallint] IDENTITY(1,1) NOT NULL, [InstanceInventoryID] [smallint] NOT NULL, [DBName] [varchar](100) NOT NULL, [CompatabilityLevel] [tinyint] NULL, [InsertedOn] [datetime] NOT NULL DEFAULT (getdate()), [DBSize] [varchar](50) NULL )ON [PRIMARY] GO SET ANSI_PADDING OFF GO /****** Object: ForeignKey [FK__DBInvento__Insta__173876EA] Script Date: 01/04/2011 06:30:20 ******/ ALTER TABLE [dbo].[DBInventory] WITH CHECK ADD FOREIGN KEY([InstanceInventoryID]) REFERENCES [dbo].[InstanceInventory] ([ID]) GO /****** Object: ForeignKey [FK__InstanceI__Serve__145C0A3F] Script Date: 01/04/2011 06:30:25 ******/ ALTER TABLE [dbo].[InstanceInventory] WITH CHECK ADD FOREIGN KEY([ServerID]) REFERENCES [dbo].[Servers] ([ID]) GO /****** Object: ForeignKey [FK__Servers__Environ__108B795B] Script Date: 01/04/2011 06:30:30 ******/ ALTER TABLE [dbo].[Servers] WITH CHECK ADD FOREIGN KEY([EnvironmentID]) REFERENCES [dbo].[Environment] ([ID]) GO --Login Script toCreate a uniqueloginonall the Servers: SET NOCOUNT ON DECLARE @exec_stmt NVARCHAR(625) DECLARE @name SYSNAME USE MASTER IF NOT EXISTS(SELECT 1 FROM SYSLOGINS WHERE [name]='Dba_maintenance') BEGIN EXEC sp_addlogin Dba_maintenance,testpwd@1 PRINT'LoginId: Dba_maintenance created' END CREATE TABLE #spdbdesc ( dbname sysname, ) INSERT INTO #spdbdesc (dbname) SELECT NAME FROM master.dbo.sysdatabases DECLARE ms_crs_c1 cursor global for SELECT dbname from #spdbdesc OPEN ms_crs_c1 FETCH ms_crs_c1 INTO @name WHILE @@FETCH_STATUS <> 0 BEGIN SELECT @exec_stmt = 'use '+@name+Char(10)+' if not exists(select 1 from sysusers where name=''Dba_maintenance'')'+Char(10)+' begin'+Char(10)+' EXEC sp_adduser ''Dba_maintenance'',''Dba_maintenance'''+Char(10)+' print ''User created at '+@name+''''+Char(10)+'end' EXECUTE(@exec_stmt) FETCH ms_crs_c1 INTO @name END DEALLOCATE ms_crs_c1 DROP TABLE #spdbdesc
Script for Stored Procedure Creation:
Usp_Inventory_Databases: To capture database information for the given inventory
Usp_Inventory_Instances: To capture instance information from the network
GO ------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------ --GETTING DFATABASE DETAILS FOR THE GIVEN INSTANCE --Parameter @InstanceID - ID column from InstanceInventory ------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------ CREATE PROC usp_Inventory_Databases(@InstanceID SMALLINT) AS BEGIN SET NOCOUNT ON /*** DECLARATION SECTION ***/ DECLARE @InstanceName VARCHAR(100) SELECT @InstanceName=InstanceName FROM InstanceInventory WHERE ID=@InstanceID DECLARE @String VARCHAR(1000) DECLARE @Low NVARCHAR(11) DECLARE @USER VARCHAR(50) DECLARE @PWD VARCHAR(10) DECLARE @i TINYINT DECLARE @Count TINYINT DECLARE @DBName VARCHAR(100) DECLARE @Edition TABLE(Version VARCHAR(50),Edition VARCHAR(50)) /*** Assigning UserID and PWD to the local variables ***/ SET @USER='Dba_maintenance' SET @PWD='RnMqp@1' /*** Updates the Edition and Version information for the given instance ***/ SET @String =' SELECT Version,Edition from OPENROWSET(''MSDASQL'',''DRIVER={SQL Server};SERVER='+@InstanceName+';UID='+'Dba_maintenance'+';PWD='+'RnMqp@1'+''','' SELECT LEFT(@@VERSION,38) AS Version,CAST(SERVERPROPERTY(''''productlevel'''') AS VARCHAR) + '''' ('''' + CAST(SERVERPROPERTY(''''edition'''') AS VARCHAR) + '''')'''' AS Edition'')' INSERT INTO @Edition (Version,Edition)Execute(@String) UPDATE InstanceInventory SET Version=(SELECT Version FROM @Edition), Edition=(SELECT Edition FROM @Edition) WHERE ID=@InstanceID SET @String='' /*** Captures the database_name and db compatibility level into #Databases ***/ IF OBJECT_ID('tempdb..#Databases') IS NULL BEGIN CREATE TABLE #Databases (ID INT IDENTITY,Name VARCHAR(100),CmptLevel TINYINT,DBSize VARCHAR(50),State INT ) END SET @String =' INSERT INTO #Databases (Name,Cmptlevel) SELECT Name, Cmptlevel from OPENROWSET(''MSDASQL'',''DRIVER={SQL Server};SERVER='+@InstanceName+';UID='+@User+';PWD='+@PWD+''', master.dbo.sysdatabases )' Execute(@String) SET @STRING='' /*** Calculates the Database size and updates at #Databases ***/ /*** While updating details first it verifies the state of the database. ***/ /*** If it is not accessable it skips that db and continue to the next DB ***/ SELECT @Low =CONVERT(VARCHAR(11),Low)FROM Master.dbo.spt_values WHEREtype= N'E'and number = 1 SELECT @Count=COUNT(1)FROM #Databases SET @i=1 SET @String='' WHILE(@Count<=@i) BEGIN SELECT @DBName=Name FROM #Databases WHERE ID=@i SET @String= 'UPDATE #Databases SET State= (SELECT S FROM OPENROWSET(''MSDASQL'',''DRIVER={SQL Server};SERVER='+@InstanceName+';UID='+@User+';PWD='+@PWD+''',''SELECT has_dbaccess('''''+@DBName+''''') As S'')) WHERE ID='+CONVERT(VARCHAR,@i) EXECUTE(@String) SET @STRING='' IF((SELECT State FROM #Databases WHERE ID=@i)=1) BEGIN SET @String= 'UPDATE #Databases SET DBSize = (SELECT STR(CONVERT(dec(15),sum(size))* '+ @Low +'/ 1048576,10,2)+ N'' MB'' FROM OPENROWSET(''MSDASQL'',''DRIVER={SQL Server};SERVER='+@InstanceName+';UID='+@User+';PWD='+@PWD+''',' +QUOTENAME(@DBName, N'[')+ N'.dbo.sysfiles)) WHERE ID='+CONVERT(VARCHAR,@i)+' AND State=1' EXECUTE(@String) END SET @i=@i+1 END /*** Inserts the Database details into ***/ INSERT INTO DBInventory (InstanceInventoryID,DBName,CompatabilityLevel,DBSize)SELECT @InstanceID,Name,CmptLevel,DBSize FROM #Databases DROP TABLE #Databases END GO ------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------ /*** GETTING INSTANCE DETAILS FOR THE GIVEN SERVERNAME ***/ /*** Parameter - @ServerName***/ ------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------ CREATE PROC usp_Inventory_Instances(@ServerName VARCHAR(50)) AS BEGIN SET NOCOUNT ON /*** DECLARATION SECTION ***/ DECLARE @Instances TABLE(ServerName VARCHAR(100)) DECLARE @MachedInstances TABLE(ID INT IDENTITY,ServerName VARCHAR(50)) DECLARE @TestConnection TABLE(Name VARCHAR(1000)) DECLARE @Counter SMALLINT DECLARE @I SMALLINT DECLARE @InstanceName VARCHAR(50) DECLARE @ServerID SMALLINT DECLARE @InstanceID SMALLINT DECLARE @String VARCHAR(1000) /*** Checking the given server name againest the existing server list ***/ /*** If the name is not avilable it exit the process***/ SET @ServerID=0 SELECT @ServerID=ID FROM Servers WHERE Server_Name=@ServerName IF(@ServerID=0) BEGIN RETURN 0 END /*** Captures all the existing servers into tab variable @Instances ***/ /*** Get all the related server/instance names by maching the given servername***/ /*** A loop starts to get each individual instance details***/ /*** First it inserts the instancename and assigns the Identity value to @InstanceID ***/ /*** It test the connection using the unique userID and PWD***/ /*** If it is not able to connect to that instance it simply updates the column Disabled as 1***/ /*** If the instance is accessable calls the sp usp_Inventory_Databases by passing the instanceID***/ /*** That sp get all the database details and insert into DBInventory table ***/ /*** Loop continues till the last instance***/ INSERT INTO @Instances EXECXP_CMDSHELL'SQLCMD -L'; INSERT INTO @MachedInstances (ServerName) SELECT LTRIM(RTRIM(ServerName))FROM @Instances WHERE ServerName LIKE'%'+@ServerName+'%' SELECT @Counter=COUNT(1)FROM @MachedInstances SET @i=1 BEGIN TRAN BEGIN TRY WHILE(@Counter<=@i) BEGIN SELECT @InstanceName=ServerName FROM @MachedInstances WHERE ID=@i INSERT INTO InstanceInventory (ServerID,InstanceName) VALUES(@ServerID,@InstanceName) SET @InstanceID=SCOPE_IDENTITY() SET @String='SQLCMD -S '+@InstanceName+' -U Dba_maintenance -P RnMqp@1 -q "USE MASTER;"' DELETE @TestConnection INSERT INTO @TestConnection EXEC XP_CMDSHELL @String IF EXISTS(SELECT 1 FROM @TestConnection WHERE Name LIKE'%Sqlcmd:%Error%' OR Name LIKE '%HResult%Level%' OR Name LIKE '%Login failed%') BEGIN UPDATE INSTANCEINVENTORY SET Disabled=1 WHERE ID=@InstanceID END ELSE BEGIN EXEC usp_Inventory_Databases @InstanceID END SET @i=@i+1 PRINT 'Instance :'+@InstanceName+' Inserted ' END END TRY BEGIN CATCH ROLLBACK TRAN PRINT 'Rollbacked' RETURN 0 END CATCH COMMIT PRINT'All instances and respective database details Inserted Successfully' END
Nice scripts but if you give sample output for tables then it looks much better..
Can you please document the Dba_Maintenance user and permissions required for this to work?