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
}
}
}
}