Hi All,
I started looking at a project to check the archive bit of SQL backup files. The scope of the project is as follows.
- Connect to a SQL instance from an inventory DB
- Get a list of the backup drives by querying msdb and working out the directories used for backups in the last 7 days.
- Connect to the SQL instance via the network path
- Check the backup directory returned in step 2
- Get the list of all the .bak files etc in the directory and check the archive bit status in the last 36 hours.
- Once the archive bit status has been retrieved from step 5, store the details on a table
- Generate a CSV file with the results from step 6
I have had to overcome a lot of challanges to get the script to this level, but I know it should be better. One of my limitations is the fact that I am only using powershell V2 and thats what deployed to PC's at this shop. I would like recomendations on improvements on the following areas.
I believe the script can be improved, but I am looking for constructive feedback. I only recently started coding powershell.
- The script converts local folders onto UNc network paths, can this be re-written or improved to use local paths ?
- My SQL connectivity scripts, I believe they can be better.
- Output to a CSV file, at the moment I am struggling to export the contents of the array onto a CSV file.
- Other tips.
Thanks in advance.
See the code below.
# Chcek server list for the archive bit. # This is a wrapper script for checking the list of servers. Script will get server list from a SQL instance. #Function for connecting to a SQL server instance. function Invoke-Sqlcmd3 { param ( [string]$ServerInstance,#="localhost", [string]$Database, [string]$Query, [Int32]$CommandTimeout=30 ) $connectionString = "Server={0};Database={1};Integrated Security=True" -f $ServerInstance, $Database $connection = New-Object System.Data.SqlClient.SQLConnection($connectionString) try { $connection.Open() $command = $connection.CreateCommand() $command.CommandTimeout = $QueryTimeout $command.CommandType = [System.Data.CommandType]::Text $command.CommandText = $Query $dataSet = New-Object System.Data.DataSet $dataAdapter = New-Object System.Data.SqlClient.SqlDataAdapter($command) [void]$dataAdapter.Fill($dataSet) $dataSet.Tables[0] } finally { if ($connection.State -eq [System.Data.ConnectionState]::Open) { $connection.Close() } } } Function Get-ArchiveStatus { param ( [string] $path , [string] $NetworkPath, [string] $Server, [string] $DBServer, [string] $Database, [int] $HoursThreshold ) $files = Get-ChildItem -Path $NetworkPath -include *.diff,*.trn,*.bak,*.sysbak -Recurse $attribute = [io.fileattributes]::archive #Construct filename $Outfilename = "J:\Log\BackupCheck_"+ (Get-Date -format ddMMyyyy) + ".csv" #Set up database connection Params. $con = "server=$DBServer;database=$Database;Integrated Security=sspi" $ResultsArray = @() $ThresholdDate = (get-date).AddHours(-$HoursThreshold) # Check for the Archive Bit for the servers. Foreach($file in $files) { IF ($file.CreationTime -lt $ThresholdDate) { If((Get-ItemProperty -Path $file.fullname).attributes -band $attribute ) { write-host "I got her" $FileDate = $file.CreationTime "$file.fullname does not appear to have been backed up" $sql = " INSERT INTO dbo.tblCheckArchiveBit ([ServerName],[Directory],[DirectoryFile] ,[FileCreationDate],[Archived]) SELECT '$Server', '$path', '$file', '$FileDate','N' " Invoke-Sqlcmd3 -serverinstance $DBServer -database $Database -query $sql $ResultsArray += New-Object PSObject -Property @{ServerName=$Server;FilePath=$path;FileName=$file;FileCreationDate=$FileDate; ThresholdDate=$ThresholdDate} ResultsArray | Export-csv $Outfilename -NoTypeInformation $MyServerName | out-file J:\Log\Serversxx.txt -append } ELSE { "$file.fullname does appear to have $attribute bit set and backed up." } } } #end loop } #end of function $Server = "SERVERNAME" $Database = "InventoryDB" $con = "server=$Server;database=$Database;Integrated Security=sspi" $cmd = "SELECT TOP 1 [ServerName] FROM Inventory WHERE Env = 'PROD' " $da = new-object System.Data.SqlClient.SqlDataAdapter ($cmd, $con) $dt = new-object System.Data.DataTable $da.fill($dt) | out-null # get a list of all the server names and put them in a table foreach ($srv in $dt) # check every server in the table { #Ping the server here $MyServerName = $srv.ServerName $Reply = Test-Connection -ComputerName $MyServerName -Count 1 -Quiet # ping server to see if it's alive if ($Reply –eq “True”) { $MyServerName | out-file J:\Log\Servers.txt -append #Get the backup directory and the server name again from SQL $sqlcommand = "SELECT distinct @@SERVERNAME SQLServer , SUBSTRING(BMF.physical_device_name,1,LEN(BMF.physical_device_name)-CHARINDEX('\',REVERSE(BMF.physical_device_name))) AS BackupDirectory FROM msdb.dbo.backupset B JOIN msdb.dbo.backupmediafamily BMF ON B.media_set_id = BMF.media_set_id WHERE B.backup_finish_date <= GETDATE() - 7 AND SUBSTRING(BMF.physical_device_name,1,LEN(BMF.physical_device_name)-CHARINDEX('\',REVERSE(BMF.physical_device_name))) NOT LIKE 'VNBU%'" #Run command to get the backup directory $con = "server=$MyServerName;database='msdb';Integrated Security=sspi" $da = new-object System.Data.SqlClient.SqlDataAdapter ($sqlcommand, $con) $instances = new-object System.Data.DataTable $da.fill($instances) | out-null # get a list of all the server names and put them in a table foreach ($servers in $instances) # check every server in the table { $ServerToCheck = $servers.SQLServer $NativeBackup = $servers.BackupDirectory $BackupDirectory = $servers.BackupDirectory $BackupDirectory = "\\" + $servers.SQLServer +"\"+ $BackupDirectory.Replace(":","$") $firstcharacter = $BackupDirectory.trim().Substring(0,1) write-host $BackupDirectory write-host $servers.SQLServer"The server is " + $ServerToCheck + " and the directory is " + $BackupDirectory + " first character is " + $firstcharacter | Out-File J:\Log\Log.txt -append if ((Test-Path $BackupDirectory -PathType Container) -eq $true) { Get-ArchiveStatus -path $NativeBackup -NetworkPath $BackupDirectory -Server $servers.SQLServer -DBServer "SERVERNAME" -Database "DBCentral" -HoursThreshold 36 $ResultsArray | out-file J:\Log\FinalOutput.txt } } } }