Automating SQL Server instance and Database Inventory preparation

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

 

Posted in SQL Server DBA | Tagged , , , , | 2 Comments
Subscribe
Notify of
guest
2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Shripad
Shripad
6 years ago

Nice scripts but if you give sample output for tables then it looks much better..

Mark Burns
Mark Burns
4 years ago

Can you please document the Dba_Maintenance user and permissions required for this to work?