Filesystem Accounting

Plötzlich wird der verfügbare Speicherplatz immer weniger und es gibt keine Spur woher die Daten kommen.
Genau das war der Grund weshalb das folgende Powershell- Script entstanden ist. Das Script erfasst für Verzeichnisse in definierbaren Einsprungspunkte deren Größe und schreibt die Daten für eine leichte Auswertbarkeit in eine SQL Datenbank.
Es wird nur ein Level gelesen da das vollständige Accounting über alle Filesysteme einfach zu leistungshungrig und zeitintensiv wäre.

Powershell script

# fileserver accounting to sql database
 
$dbserver = 'db.rs.de'
$dbuser = 'diracc'
$dbpass = 'dodalgeheim'
$dbname = 'diracc'
 
# load assemblies
[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SqlServer.Smo”) | Out-Null;
 
# connect to SQL Server
$serverName = "DB"
$server = New-Object -typeName Microsoft.SqlServer.Management.Smo.Server -argumentList "$serverName"
 
# login using SQL authentication, which means we supply the username
# and password
$server.ConnectionContext.LoginSecure=$false;
$server.ConnectionContext.set_Login($dbuser)
$securePassword = ConvertTo-SecureString $dbpass -AsPlainText –Force
$server.ConnectionContext.set_SecurePassword($securePassword)
 
# clear the screen
cls
 
$conn1 = New-Object System.Data.SqlClient.SqlConnection("Server=$dbserver; Database=$dbname; Integrated Security=SSPI")
$conn1.Open()
 
$conn2 = New-Object System.Data.SqlClient.SqlConnection("Server=$dbserver; Database=$dbname; Integrated Security=SSPI")
$conn2.Open()
 
$cmd1 = $conn1.CreateCommand()
$cmd1.CommandText = "SELECT shareid, sharename FROM [dbo].[shares]"
 
$Reader = $cmd1.ExecuteReader()
 
# worker loop
while ($Reader.Read()) {
  $shareid = $Reader.GetValue(0)
  $sharename = $Reader.GetValue(1)
 
  $startfolder = "$sharename\*"
  $folders = get-childitem $startfolder | where{$_.PSiscontainer -eq "True"}
 
  foreach ($fol in $Folders){
    $colItems = (Get-ChildItem $fol.fullname -recurse | Measure-Object -property length -sum)
    $size = ($colItems.sum / 1MB)
 
    # write to database
    # 1. current date
    # 2. $shareid
    # 3. $($fol.name)
    # 4. $size
 
    $cmd2 = $conn2.CreateCommand()
    $cmd2.CommandText = "INSERT INTO [dbo].[dirsize] (datum, shareid, directory, size) VALUES (CURRENT_TIMESTAMP, $shareid, '" + $($fol.name) + "', $size)"    
 
    $cmd2.ExecuteNonQuery() | out-null
  }
}
$Reader.Close()
$conn1.Close()
$conn2.Close()

SQL Layout Tabelle dirsize

USE [diracc]
GO
 
/****** Object:  Table [dbo].[dirsize]    Script Date: 07/19/2012 11:48:22 ******/
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[dirsize](
  [datum] [date] NOT NULL,
  [shareid] [smallint] NOT NULL,
  [directory] [nvarchar](max) NOT NULL,
  [size] [decimal](18, 2) NOT NULL
) ON [PRIMARY]
 
GO

SQL Layout Tabelle shares

USE [diracc]
GO
 
/****** Object:  Table [dbo].[shares]    Script Date: 07/19/2012 11:49:20 ******/
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[shares](
  [shareid] [int] IDENTITY(1,1) NOT NULL,
  [sharename] [nvarchar](50) NOT NULL
) ON [PRIMARY]
 
GO

Beschreibung

Die Ergebnisse werden mit dem Datum des Scans in die Tabelle "dirsize" geschrieben. Diese enthält lediglich eine Referenz "shareid" auf den Namen des Shares. Will man also eine Übersicht ausgeben, die auch den Namen des Shares enthält muß man die Tabellen "dirsize" und "shares" joinen. Ein sehr einfaches Beispiel hierzu findet sich im Anschluß.
Die Tabelle "shares" enthält die Einsprungspunkte der zu überwachenden Verzeichnisse. Ein Share kann hierbei ein Laufwerk mit/ohne Pfad oder ein UNC-Pfad sein. Beispiel: d:\, d:\daten\abteilung, \\server\daten\bayreuth\tk

Wie immer kann auch dieses Powershell Script selbstverständlich wieder über einen geplanten Task aufgerufen werden. Hierzu kann man folgende Zeile verwenden:  
powershell.exe -Noninteractive -Command "&{PFAD_ZUM_SCRIPT}"
  Wie immer: The script is provided as is without any warranty ;-)  

		
			

Beispiel einer einfachen Abfrage

USE diracc;
SELECT ds.datum, sh.sharename, ds.directory, ds.size
  FROM [dbo].[dirsize] AS ds
  INNER JOIN [dbo].[shares] AS sh
  ON sh.shareid = ds.shareid
  ORDER BY ds.size DESC, ds.datum DESC