(somewhat) Social

Entries in powershell (22)

Thursday
May192011

Central Auditing Of SQL Permissions Scripts

Yesterday I gave a presentation for the PASS PowerShell Virtual Chapter on central auditing of SQL permissions with PowerShell and TSQL. For those that attended feel free to download the scripts I used in my presentation. For those that didn’t…well you can still download them but this picture may not apply to you…

Image from whosawesome.com

Monday
May162011

Presenting For The PASS PowerShell Virtual Chapter

A quick note, on Wednesday at 1PM EST I'll be giving an online presentation on one of the two topics I submitted for the PASS Summit this year.

 The presentation will be heavy on the demo side of things and I'll cover the basics of querying SQL Server using PowerShell, running scripts against multiple machines quickly and easily as well as bulk loading data into SQL.

Please check out http://www.powershell.sqlpass.org/ for a link to the livemeeting.

 

Central auditing of SQL permissions with PowerShell & TSQL

Description: As a DBA it can be a challenge to know who has permissions to what SQL instances and what objects. The more instances you have the more complex that task. In this presentation I'll share a method using PowerShell and TSQL that can be used to capture permissions from all of your SQL instances and load them into a centralized location. We'll even take it a step further by auditing those permissions so that we can quickly and easily identify any that might have changed.

 

Wednesday
May112011

Rock The PASS Summit Vote

The PASS Summit 2011 is fast approaching. It’s being held in October this year and the call for speaker sessions has already closed. In a great turn of events PASS is asking you to take a part in helping to decide on the sessions to be presented. Up until May 20th you can vote on the sessions that you want to see.

I’ve already voted for multiple sessions over at the Session Preferencing page, please ensure that you do so as well. In doing so I hope that you consider voting for two sessions I submitted.

 

PowerShell: Are you checking out my profile? [100 level]

Session Category: Regular Session (75 minutes)
Session Track: Enterprise Database Administration and Deployment
PowerShell is a very powerful management tool and you can spend hours writing magical scripts to provide automation for frequently run tasks. Often forgotten is the PowerShell profile, a place you can add your own functions which can provide you lightning fast access to information.
In this session we'll talk about the power a profile puts at your fingertips. I will also demo (and share) several PowerShell functions that I use frequently for common tasks like checking database backups and disk space.
I'll show you my PowerShell profile if you show me yours.

I’ve given this presentation at a couple of SQLSaturday events and it’s proven very popular both times. I have a couple of new items in my profile which extends this topic further.

 

Centralized auditing of permissions with SQL Server and PowerShell [100 level]

Session Category: Regular Session (75 minutes)
Session Track: Enterprise Database Administration and Deployment
As a DBA it can be a challenge to know who has permissions to what SQL instances and what objects. The more instances you have the more complex that task. In this presentation I'll share a method using PowerShell and TSQL that can be used to capture permissions from all of your SQL instances and load them into a centralized location. We'll even take it a step further by auditing those permissions so that we can quickly and easily identify any that might have changed.

I will actually be presenting a shortened version of this on May 18th at 1pm EST for the PASS PowerShell Virtual Chapter. I hope you can attend that and get an idea of how the longer session could help you in auditing your SQL servers.

Friday
May062011

Handling Periods In Database Names With PowerShell

I came across an interesting problem today. We have a vendor who’s tool, for some unknown reason, creates a database with a period in the name (ie Data.Base). Why on earth anyone would do this I don’t know, but it’s happened. I found this out when my PowerShell script to capture database sizes failed.

It seems that PowerShell was not able to handle that period.

 

Reproducing the problem

Create a new database using SSMS:

CREATE DATABASE [Terrible.DBName]

 

Now open up PowerShell and try to query what’s in that database:

dir SQLSERVER:\SQL\localhost\default\databases\Terrible.DBName

 

Doing so gives the error:

Get-ChildItem : Cannot retrieve the dynamic parameters for the cmdlet. SQL Server PowerShell provider error: The number of keys specified does not match the number of keys required to address this object. The number of keys required are: Name.
At line:1 char:4
+ dir <<<<  SQLSERVER:\SQL\localhost\default\databases\Terrible.DBName
    + CategoryInfo          : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
    + FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.GetChildItem
   Command

I tried encapsulating the path in quotes and escaping the period with a back tick, both to no avail.

dir 'SQLSERVER:\SQL\localhost\default\databases\Terrible.DBName'    
dir SQLSERVER:\SQL\localhost\default\databases\Terrible`.DBName

 

Fixing the problem

Eventually I found the solution. Changing the period to it’s hex value of %2e allowed me to query correctly.

dir SQLSERVER:\SQL\localhost\default\databases\Terrible%2eDBName  

PeriodInDBName

 

Updating scripts

Now that I found the problem and resolution I’ll be going through and changing my scripts to take this into account.

One thing to note, when trying to use –replace to replace the period in a variable you need to place it within square brackets otherwise the entire string gets replaced with %2e.

The wrong way:

$a = "Some.String"
$a = $a -replace ".", "%2e"
$a

PeriodInDBNameWrongString

 

The right way:

$a = "Some.String"
$a = $a -replace "[.]", "%2e"
$a

PeriodInDBNameRightString

Thursday
May052011

Checking Database Space With PowerShell

Have you ever been asked the question “quick, I need to know how much free space there is in the Nodrap database on server Cromulent”?

Ever wished that you could quickly grab that information?

You might well have a TSQL script that will grab that information for you. I’ve been working on pulling that information centrally, grabbing that data for multiple servers becomes a much simpler task when using PowerShell.

 

Why PowerShell and not TSQL?

For me the big bonus that PowerShell provides me for grabbing this data is that I can pull it quickly and easily, I can also simply run it for multiple machines and the same script works for versions SQL 2000 and up.

 

How big is my database?

Open up your favorite PowerShell ISE (you can even use the one that comes with Windows). If the aren’t already loaded you’ll need to add the SQL snapins

Add-PSSnapin SqlServer* 

 

To get a list of databases, their sizes and the space available in each on the local SQL instance.

dir SQLSERVER:\SQL\localhost\default\databases | Select Name, Size, SpaceAvailable | ft -auto

CheckDBWPS SizeSpace

Very quick and easy. The information here is a little misleading though.

The Size is the size of the database and includes size of the transaction log(s). SpaceAvailable only represents the space available in the data files however, unless we’re looking at SQL 2000 in which case it’s the free space in the data and log files. To make things even more confusing Size is reported in MB and SpaceAvailable in KB.

To get more details we need to look at the transaction log information.

 

How big is my transaction log?

To grab this information we need to go deeper. In this case we will focus on the AdventureWorks database.

dir SQLSERVER:\SQL\localhost\default\databases\AdventureWorks\logfiles | 
    Select Name, Size, UsedSpace | ft -auto

CheckDBWPS LogSpace

 

This information is all in KB which helps have it make a little more sense.

 

Doing the math

Now we have the size of the database, the size of the log and the free space in each it’s some quick math which gives us the sizing information.

  • Size = Database Size in MB
  • Size / 1024 (from log) = Log size in MB
  • UsedSpace / 1024 = Log used in MB
  • (Size – UsedSpace) / 1024 = Log free in MB
  • Size – (Size / 1024) (from log) = Data files size in MB
  • SpaceAvailable / 1024 = Space free in data files
  • Size – (SpaceAvailable / 1024) – (Size / 1024) (from log) = Space used in data files
  • Size – ((SpaceAvailable / 1024) – ((Size – UsedSpace) / 1024) = Space used in data files (SQL 2000)

 

Outputting nice results

Running all this and grabbing the data from different areas can lead to messy results. This is where the PowerShell DataTable come to the rescue.

A DataTable is a PowerShell object, much like a SQL table that can hold data for you.

First you create the object, define and add columns and then add rows before finally returning the data.

Here’s a quick example:

$dataTable = New-Object system.Data.DataTable "SomeStuff"
 
#Specify the column names for the data table
$col1 = New-Object system.Data.DataColumn FunkyText,([string])
$col2 = New-Object system.Data.DataColumn JustANumber,([int])
$col3 = New-Object system.Data.DataColumn TodaysDate,([datetime])
 
#Add the columns to the data table
$dataTable.Columns.Add($col1)
$dataTable.Columns.Add($col2)
$dataTable.Columns.Add($col3)
 
#Create a new row
$row = $dataTable.NewRow()
#Add data for each column in the row
$row.FunkyText = "Something really funky"
$row.JustANumber = 1395
$row.TodaysDate = ((Get-Date ).ToString("yyyy-MM-dd HH:mm:ss"))
#Add the new row to the datatable
$dataTable.Rows.Add($row)
 
#Output the datatable
$dataTable | out-gridview

CheckDBWPS Datatable

 

Putting everything together

Running the following script will pull the data and log information for all the databases on the SQL instance specified in the top parameter. It also handles named instances without modification, and is factored to return the correct information for SQL 2000 as well as higher versions

Note: Run DBCC UPDATEUSAGE for SQL 2000 instances to ensure that the data is accurate

Param ($Servername= 'localhost')
 
 
 
Function Get-DBSizes ($Servername)
{
$Servername
 
#Check to see if it's a name instance, if it is the location will be different
if ($Servername -ilike "*\*") { $BaseLocation = "SQLSERVER:\SQL\$Servername\databases" }
    else { $BaseLocation = "SQLSERVER:\SQL\$Servername\default\databases" }
    
$dblist = dir $BaseLocation -Force | select Name
foreach ($db in $dblist)
{
    
    $location = $db.Name
    $locationFixed = $location -replace "[.]", "%2e"
    #Grab the database information
    $DBInfo = dir $BaseLocation -Force | Where-Object {$_.Name -eq $location; $_.Refresh()} | 
        select Name, 
                size,
                SpaceAvailable,
                CompatibilityLevel
 
    #Pull the log information
    #Use measure-object to sum up sizes in the event that we might have more than one log file
    $logsize = dir $BaseLocation\$locationFixed\logfiles | Where-Object {$_.Name; $_.Refresh()} | Measure-Object -Property Size -Sum 
    $logused = dir $BaseLocation\$locationFixed\logfiles | Where-Object {$_.Name; $_.Refresh()} | Measure-Object -Property UsedSpace -Sum 
    
    $sp = $DBInfo.SpaceAvailable
    $TotalDBSizeMB = $DBInfo.size
    $LogSizeMB = ($logsize.sum / 1024)
    $LogUsedMB = ($logused.Sum / 1024)
    $LogFreeMB = ($LogSizeMB - $LogUsedMB)
    $DataFilesSizeMB = ($TotalDBSizeMB - $LogSizeMB)
    $SpaceAvail = ($dbinfo.SpaceAvailable / 1024)
    
    #Because SQL2000 spaceavailable    includes log space we have to do different calculations depending on version
    #Run DBCC UPDATEUSAGE on your 2000 databases nightly to help ensure this data is accurate
    $Compat = $DBInfo.CompatibilityLevel
    if ($Compat -eq 'Version80') { $DataFileFreeMB = ($SpaceAvail - $LogFreeMB)    }
        else { $DataFileFreeMB = $SpaceAvail }
    
    
    $DataFileUsedMB = ($DataFilesSizeMB - $DataFileFreeMB)
    $DataFilePercentUsed = ($DataFileUsedMB / $DataFilesSizeMB) * 100
    $DataFilePercentFree = 100 - $DataFilePercentUsed 
    $LogPercentUsed = ($LogUsedMB / $LogSizeMB) * 100
    $LogPercentFree = 100 - $LogPercentUsed
    $date = (Get-Date ).ToString("yyyy-MM-dd HH:mm:ss")
    
    #Write the results into the data table
    $row = $dataTable.NewRow()
    $row.ServerName = $Servername
    $row.DatabaseName = $location
    $row.TotalDBSizeMB = $TotalDBSizeMB
    $row.DataFilesSizeMB = $DataFilesSizeMB
    $row.DataFilesUsedMB = $DataFileUsedMB
    $row.DataFilesFreeMB = $DataFileFreeMB
    $row.DataPercentUsed = $DataFilePercentUsed
    $row.DataPercentFree = $DataFilePercentFree
    $row.LogFilesSizeMB = $LogSizeMB
    $row.LogFilesUsedMB = $LogUsedMB
    $row.LogFilesFreeMB = $LogFreeMB
    $row.LogPercentUsed = $LogPercentUsed
    $row.LogPercentFree = $LogPercentFree
    $row.Date = $date
    $dataTable.Rows.Add($row)
    
    #And we are done
}
}
 
 
#Create data table to hold the results
$dataTable = New-Object system.Data.DataTable "Results"
#Specify the column names for the data table
$col1 = New-Object system.Data.DataColumn ServerName,([string])
$col2 = New-Object system.Data.DataColumn DatabaseName,([string])
$col3 = New-Object system.Data.DataColumn TotalDBSizeMB,([int])
$col4 = New-Object system.Data.DataColumn DataFilesSizeMB,([int])
$col5 = New-Object system.Data.DataColumn DataFilesUsedMB,([int])
$col6 = New-Object system.Data.DataColumn DataFilesFreeMB,([int])
$col7 = New-Object system.Data.DataColumn DataPercentUsed,([decimal])
$col8 = New-Object system.Data.DataColumn DataPercentFree,([decimal])
$col9 = New-Object system.Data.DataColumn LogFilesSizeMB,([int])
$col10 = New-Object system.Data.DataColumn LogFilesUsedMB,([int])
$col11 = New-Object system.Data.DataColumn LogFilesFreeMB,([int])
$col12 = New-Object system.Data.DataColumn LogPercentUsed,([decimal])
$col13 = New-Object system.Data.DataColumn LogPercentFree,([decimal])
$col14 = New-Object system.Data.DataColumn Date,([datetime])
#Add the columns to the data table
$dataTable.Columns.Add($col1)
$dataTable.Columns.Add($col2)
$dataTable.Columns.Add($col3)
$dataTable.Columns.Add($col4)
$dataTable.Columns.Add($col5)
$dataTable.Columns.Add($col6)
$dataTable.Columns.Add($col7)
$dataTable.Columns.Add($col8)
$dataTable.Columns.Add($col9)
$dataTable.Columns.Add($col10)
$dataTable.Columns.Add($col11)
$dataTable.Columns.Add($col12)
$dataTable.Columns.Add($col13)
$dataTable.Columns.Add($col14)
 
 
 
#Provide the name of the SQL server that we want to check
 
$WarningPreference = "silentlycontinue"
#Call the function to populate the results
#get-content c:\Temp\serverlist.txt | % {Get-DBSizes $_  } 
Get-DBSizes $Servername
 
#Output the results
$dataTable | Out-GridView
 

Click to embiggen

 

Final note

Download the PS1 file and save it on your machine, from there you can call the script and just pass in the SQL instance to get the results eg:

./Get-DatabaseSize.ps1 localhost
./Get-DatabaseSize.ps1 cromulent\awesomesauce

In a follow up post I’ll show how we apply a minor tweak to the script and have the data loaded into a SQL database.