Showing posts with label powershell. Show all posts
Showing posts with label powershell. Show all posts

Tuesday, December 19, 2017

Start/Stop Group of Azure RM VM's

For testlab purposes, I often have a group of Azure VMs to manually spin up/spin down at once. I've had this script in use for a while and decided I'd comment it up and share.

The SQL Server Availability Groups template in the Azure VM Gallery, for example, creates five VMs in a resource group. Who wants to use the Azure portal in browser to click, wait, x5? And if you try to start/stop too many VMs at a time, they will error out.

PowerShell provides! Since the Start-AzureRMVM and Stop-AzureRMVM cmdlets wait for each VM to finish changing state, it'll take 2-3 minutes per VM, but it's launched in one manual step.
Note, there's also built-in Azure functionality to shut down VMs on a schedule, no coding required. In this case, the use case for this script is to start up my five VMs for a test Availability Group manually, and bring them down when I'm done with my lab. I may also set up the auto-shutdown as insurance that I won't accidentally leave the VMs running.
The below script is quite verbose and well-commented, including the Setup block to get the required module and log you into Azure. You could certainly shorten and simplify the script, but my goal here is readability, and repeatability. (If you have a suggestion on how this can be improved, I'm all for it.)

Specify a resource group name in $ResourceGroupName, and a list of  VM names in $VMs. Important note here: when you have a cluster of VMs, you may want to have them start up and shut down in a specific order. The below example shuts down the domain controller VMs last, and starts them up first.
#Launch VS Code as Administrator

<#
Invoke-Command -script {Install-WindowsFeature -Name "Failover-Clustering" } `
-ComputerName SQLDEV11, SQLDEV12, SQLDEV14, SQLDEV15
Invoke-Command -script {Install-WindowsFeature -Name "RSAT-Clustering-Mgmt" } `
-ComputerName SQLDEV11, SQLDEV12, SQLDEV14, SQLDEV15
Invoke-Command -script {Install-WindowsFeature -Name "RSAT-Clustering-PowerShell" } `
-ComputerName SQLDEV11, SQLDEV12, SQLDEV14, SQLDEV15
#>
#Install-Module SQLSERVER -Force -AllowCLobber 
#Import-Module SQLSERVER

#Must run on the primary node
#TODO: configure initial variable values.

Write-Output "Begin $(Get-Date)"
#Setup: TODO Configure these
$PrimaryReplicaName = "SQLSERVER-0"
$PrimaryReplicaInstanceName = "SQL2K17" #Named instance or DEFAULT for the default instance
$SecondaryReplicaName1 = "SQLSERVER-1"
$SecondaryReplicaInstanceName1 = "SQL2K17" #Named instance or DEFAULT for the default instance
$AvailabilityGroupName = "WWI2017-AG"

#Inventory and test
Get-ChildItem "SQLSERVER:\Sql\$($PrimaryReplicaName)\$($PrimaryReplicaInstanceName)\AvailabilityGroups\$($AvailabilityGroupName)\AvailabilityReplicas\" | Test-SqlAvailabilityReplica | Format-Table

   $AGPrimaryObjPath = "SQLSERVER:\Sql\$($PrimaryReplicaName)\$($PrimaryReplicaInstanceName)\AvailabilityGroups\$($AvailabilityGroupName)\AvailabilityReplicas\$($PrimaryReplicaName+$(IF($PrimaryReplicaInstanceName -ne "DEFAULT"){$("%5C")+$PrimaryReplicaInstanceName} ))"
   $AGPrimaryObj = Get-Item $AGPrimaryObjPath 
   $AGSecondaryObjPath = "SQLSERVER:\Sql\$($PrimaryReplicaName)\$($PrimaryReplicaInstanceName)\AvailabilityGroups\$($AvailabilityGroupName)\AvailabilityReplicas\$($SecondaryReplicaName1+$(IF($SecondaryReplicaInstanceName1 -ne "DEFAULT"){$("%5C")+$SecondaryReplicaInstanceName1} ))"
   $AGSecondaryObj = Get-Item $AGSecondaryObjPath


   
#Set replicas to synchronous before planned failover
        
    Set-SqlAvailabilityReplica `
    -Path $AGPrimaryObjPath `
    -AvailabilityMode SynchronousCommit `
    -FailoverMode "Manual" `
    -ErrorAction Stop
    Set-SqlAvailabilityReplica `
    -Path $AGSecondaryObjPath `
    -AvailabilityMode SynchronousCommit `
    -FailoverMode "Manual" `
    -ErrorAction Stop

#Check for when replicas are synchronized.
Do {
$AGSecondaryObj.Refresh()
$CurrentSync = ($AGSecondaryObj | Select RollupSynchronizationState | Format-Wide | Out-String).Trim()
IF ($CurrentSync -ne "Synchronized") { 
        Write-Output "Waiting for Synchronized state before failover, still $($CurrentSync)"
        Start-Sleep -s 2 
        }
} Until ($CurrentSync -eq 'Synchronized')

#Perform failover
Write-Output "Beginning Failover $(Get-Date)"
Switch-SqlAvailabilityGroup `
    -Path "SQLSERVER:\Sql\$($SecondaryReplicaName1)\$($SecondaryReplicaInstanceName1)\AvailabilityGroups\$($AvailabilityGroupName)\" `
     -ErrorAction Stop `
    #Only include the next line if it is a forced failover
    #-AllowDataLoss -Force
Write-Output "Failover Complete $(Get-Date)"
Start-Sleep -s 10 #Allow failover to resolve

#Return secondary replica to Asynchronous sync
#Note that the values here of Primary and Secondary1 are flipped, because the variables predate the failover.
Invoke-Command -script { `
param($SecondaryReplicaName1, $SecondaryReplicaInstanceName1, $AvailabilityGroupName, $PrimaryReplicaName, $PrimaryReplicaInstanceName)

 Set-SqlAvailabilityReplica `
-Path "SQLSERVER:\Sql\$(($SecondaryReplicaName1))\$(($SecondaryReplicaInstanceName1))\AvailabilityGroups\$(($AvailabilityGroupName))\AvailabilityReplicas\$(($SecondaryReplicaName1)+$(IF(($SecondaryReplicaInstanceName1) -ne "DEFAULT"){$("%5C")+(($SecondaryReplicaInstanceName1))} ))"  `
-AvailabilityMode asynchronousCommit `
-ErrorAction Stop
    Set-SqlAvailabilityReplica `
-Path "SQLSERVER:\Sql\$(($SecondaryReplicaName1))\$(($SecondaryReplicaInstanceName1))\AvailabilityGroups\$(($AvailabilityGroupName))\AvailabilityReplicas\$(($PrimaryReplicaName)+$(IF(($PrimaryReplicaInstanceName) -ne "DEFAULT"){$("%5C")+(($PrimaryReplicaInstanceName))} ))"  `
-AvailabilityMode asynchronousCommit `
-ErrorAction Stop

Get-ChildItem "SQLSERVER:\Sql\$($SecondaryReplicaName1)\$($SecondaryReplicaInstanceName1)\AvailabilityGroups\$($AvailabilityGroupName)\AvailabilityReplicas\" | Test-SqlAvailabilityReplica | Format-Table
} -ComputerName $SecondaryReplicaName1  -Args $SecondaryReplicaName1, $SecondaryReplicaInstanceName1, $AvailabilityGroupName, $PrimaryReplicaName, $PrimaryReplicaInstanceName

Write-Output "End $(Get-Date)"


This is version of the script updated for Resource Manager VMs. For classic VMs, I have a similar script in a blog post here.

Tuesday, October 03, 2017

VS Code: PowerShell and focusConsoleOnExecute

If you're used to the behavior of SSMS or PowerShell ISE where upon executing code in the script panel, your cursor stays in the script panel, then the behavior of Visual Studio Code's PowerShell default might be annoying. By default in Code, your cursor moves down to the PowerShell console terminal upon executing code.

Maybe that's your thing. If not, there's a preference you can change, immediately.

In Code, go to the File Menu -> Preferences to launch the User Settings screen. There's a setting to overwrite the default called powershell.integratedConsole.focusConsoleOnExecute, that's what you're looking for. Add it to the right-side of the Code settings window like this:
{ "powershell.integratedConsole.focusConsoleOnExecute": false,
}

It'll look like this:

The change takes place immediately.
Not using Code yet? Hop to! https://code.visualstudio.com/Download

My former long-ago colleague and PowerShell prize-winning guru Mike F Robbins has a good blog post and video getting you up to speed on Code, he discusses powershell.integratedConsole.focusConsoleOnExecute and more.



Tuesday, April 05, 2016

SQL Server PerfMon Counters Not Listed in dm_os_performance_counters

Had an issue with a new Azure VM install of SQL Server, but have had this issue in other, older places as well. SQL Server's performance counters in dm_os_performance_counters and/or perfmon have gone missing. How to get them back?

You might see a small subset of all performance counters here in the DMV view. (There should be 1335 rows in this DMV for SQL Server 2014 RTM, 1245 for SQL 2012 SP2. More are added regularly with major releases.) In the example I saw this morning, only 51 rows were returned, and only for the XTP series of objects (XTP Transaction Log, XTP Cursors, etc).

Strangely, I had now found this problem on brand-new Azure VM's running SQL Server, specifically in the pre-built SQL 2014 Availability Group resource group, which can be deployed easily from the new Azure portal.

The issue can be resolved with a pair of PowerShell commands which re-register performance counter names and explanation text to the SQL Server service in the Windows registry.

In PowerShell, modify the below for your service.
  • In the first line, change the driver name parameter to match the SQL Server instance name, MSSQLSERVER is the name for the default instance. To do the same for a specific instance, it would be MSSQL$InstanceName. 
  • In the second line, replace my path below with the path for your instance.

unlodctr MSSQLSERVER

lodctr "C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Binn\perf-MSSQLSERVERsqlctr.ini"
A restart of the affected service, in this case the SQL Server service, is necessary for the changes to take effect.

Thursday, January 21, 2016

Easy Installing .NET 3.5 for SQL Admins

Rule "Microsoft .NET Framework 3.5 Service Pack 1 is required" failed.

This is a common problem and relatively easy fix for SQL admins installing a new SQL Server instance going back a few years, so it's about time to put all my notes about the solution into a single place. You'll get this error early on in the install:


"This computer does not have the Microsoft .NET Framework Service Pack 1 installed..." Similar error messages pop up going all the way back to SQL 2008 R2.

Before you go any further, click OK here but it is not necessary to abort SQL Server setup. Leave the "Feature Roles" page open, you'll be hitting "re-run" later.

This support article goes through the various solutions, some more painful than others.

Here's the fastest:

1. Extract the "...sources\sxs" subfolder from your Windows Server installation media .iso.
2. Run the below PowerShell, pointing at your extracted \sxs subfolder in the /Source syntax.
Dism /online /enable-feature /featurename:NetFx3 /All /Source:D:\sources\sxs /LimitAccess
3. Done.

For example:


Caveat- keep in mind that after installing .NET 3.5, you may now be eligible for additional Windows Updates/hotfixes that weren't there before.

When complete, back on the SQL Server Setup window, hit "re-run" in the "Feature Rules" window and move on with your pleasant SQL Server installation experience.



Then thank whoever should be thanked that you're not installing Oracle today.


Footnote:

Irrationally uncomfortable or afraid of PowerShell? Yes, you can use the "Add Roles and Features" feature of Windows to add this, via the ".NET Framework 3.5 Features" Feature. You'll still need to do step one above to extract the "...sources\sxs" subfolder, only instead provide it in the Alternative sources dialogue box like the below example.






Monday, November 17, 2014

Quickly Add Telnet

Have you found yourself on a server or workstation that doesn't have telnet (typical in new builds and new Azure VM's from the gallery), and need it to test port connectivity? Did you receive this error when you tried to execute telnet in cmdshell or powershell?

telnet : The term 'telnet' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1

Three quick ways to add the Telnet Client to the server you're on right now that are much faster alternatives than adding telnet via the Windows features section of the Control Panel. Note that you must launch PowerShell or Command Prompt as an administrator to run these commands, so non-administrators will need to track down a helpful sysadmin first.

Install telnet via DISM (via cmd/PowerShell):

This option should complete the installation of Telnet in seconds. You may receive an error if a reboot is pending because of another installation.

dism /online /Enable-Feature /FeatureName:TelnetClient
Install telnet via pkgmgr (via cmd/PowerShell):

This option should also complete the installation of Telnet in seconds. PkgMgr was deprecated along with a few other tools when replaced by DISM for Windows 7. PkgMgr still works on later operating systems, including Windows 8.1 and Windows Server 2012 R2.

pkgmgr /iu:"TelnetClient"


Install telnet via Install-WindowsFeature (via PowerShell):

This method is just as fast but unfortunately doesn't work for desktop installations (client-based operating systems) like Windows 7 or 8.1. Neither does the cmdlet it replaced, the deprecated Add-WindowsFeature. For desktop operating systems, go with the first option above.
Import-Module servermanager

Install-WindowsFeature telnet-client

Monday, January 27, 2014

Start/Stop Group of Azure VM's

If you've already configured your local machine's PowerShell environment to interact with your Azure subscription (see here: https://azure.microsoft.com/en-us/documentation/articles/powershell-install-configure/), then you can begin saving yourself an awful lot of button-clicking by scripting out the start/stop of your VMs, especially if you have a cluster of Azure VMs for testing and demonstrating SQL Server Availability Groups, as an example.

You will need to change a few things in these scripts to suit your purposes:
  1. Change "YourAzureAccount@hotmail.com" to your Azure account. For MSDN-based Azure subscriptions like mine, this address was my Hotmail email address.
  2. Change "Visual Studio Premium with MSDN" to the name of your Azure Subscription. This was the name my MSDN-based account was given by default.
  3. Populate the $vms variable with a list of Azure VM's in the cluster you're looking to start/stop as a group, replacing "yourVMName-alwayson-dom" and so forth.
Big thanks and credit for the assist on this blog post to Cody Gros, SharePoint Solutions Architect and my coworker at Sparkhound.

Stop a list of Azure Accounts:
#if expired, Add-AzureAccount

Get-AzureSubscription | ? {$_.ActiveDirectoryUserId -eq 'YourAzureAccount@hotmail.com' -and $_.SubscriptionName -match "Visual Studio Premium with MSDN" } | Set-AzureSubscription -SubscriptionId $_.SubscriptionID

$vmHash =@{}
$vms = "yourVMName-alwayson-dom","yourVMName-alwaysonsql1","yourVMName-alwaysonsql2","yourVMName-alwaysonWSFC"

Get-AzureVM | foreach{$vmHash.Add($_.Name,$_.ServiceName)}

foreach ($vm in $vms) {    
    $currentVMService = $vmHash[$vm] 
    Write-Host "Current VM:$($vm)"
    $thisvm = Get-AzureVM -ServiceName $currentVMService -Name $vm
    Write-Host "Stopping VM:$($thisvm.Name)"
    Stop-AzureVM -Name $thisvm.Name -ServiceName $thisvm.ServiceName #-StayProvisioned  
}
Note about the -StayProvisioned tag above. Specifying this option will retain some IP settings, but will cause your VM's to continue to accrue Azure credit, even while stopped.  Use with care.

Start a list of Azure Accounts:
#if expired, Add-AzureAccount


Get-AzureSubscription | ?{$_.ActiveDirectoryUserId -eq 'YourAzureAccount@hotmail.com' -and $_.SubscriptionName -match "Visual Studio Premium with MSDN" } | Set-AzureSubscription -SubscriptionId $_.SubscriptionID

$vmHash =@{}
$vms = "yourVMName-alwayson-dom","yourVMName-alwaysonsql1","yourVMName-alwaysonsql2","yourVMName-alwaysonWSFC"

Get-AzureVM | foreach{$vmHash.Add($_.Name,$_.ServiceName)}

foreach ($vm in $vms) {    
    $currentVMService = $vmHash[$vm] 
    Write-Host "Current VM:$($vm)"
    $thisvm = Get-AzureVM -ServiceName $currentVMService -Name $vm
    Write-Host "Starting VM:$($thisvm.Name)"
    Start-AzureVM -Name $thisvm.Name -ServiceName $thisvm.ServiceName    
}

If upon running the scripts you receive either of these errors:
get-azuresubscription : The term 'get-azuresubscription' is not recognized as the name of a cmdlet, 
Get-AzureVM : The term 'Get-AzureVM' is not recognized as the name of a cmdlet, function, script file, or 

Then you don't have the PowerShell module loaded, and PowerShell isn't automatically loading it for you. Use the Import-Module command. Below is the default location of the module, downloaded from here: https://azure.microsoft.com/en-us/documentation/articles/powershell-install-configure/
Import-Module 'C:\Program Files (x86)\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Azure.psd1'

Updated 20160322 to reflect the latest module paths

Friday, November 01, 2013

PowerShell: Delete Unneeded Rows/Columns In Excel

I had a project to automate the import of a large number of excel files via SSIS into SQL 2012. Many of the files had different headers, but they all had a column header row that preceded the data.

On some of the files, the header row started on row 2, 3, 4, or 5, and on some others, the first two or three columns were entirely blank. What a pain, right?

One saving advantage that I identified in this proprietary set of excel files: each of the datasets begins with a column headed "Identifier", so that is the cell that I want to end up in cell [A1]. That was definitely key to making this work and easily identifying when my header row began.

I automated cleaning up these spreadsheets with the following PowerShell script
Param([string]$wbname)

Try
{
 $err1 = 0;
 #testing only #$wbname = "E:\foo.xls"

 $xl = New-Object -comobject Excel.Application
 $xl.DisplayAlerts = $False

 $wb = $xl.Workbooks.open($wbname)
 $sheet = $wb.Sheets.Item(1)

#delete columns
while( ($sheet.Cells.Item(1,1).Text -eq "") -and ($sheet.Cells.Item(2,1).Text -eq "") -and ($sheet.Cells.Item(3,1).Text -eq "")){[void]$sheet.Cells.Item(1,1).EntireColumn.Delete()}

#delete rows
while( ($sheet.Cells.Item(1,1).Value() -NotLike "Identifier") -and ($sheet.Cells.Item(1,2).Value() -NotLike "Identifier")){[void]$sheet.Cells.Item(1,1).EntireRow.Delete()}

#cleanup
$wb.Close($true)
$xl.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)
Remove-Variable xl

}
Catch
{
    $err1 = 1;
}
Finally
{
    if($err1 -eq 1)
    {
        #Write-Output "99";
 #Write-Output $_.Exception.Message;

 $stream = [System.IO.StreamWriter] "e:\error.txt"
 $stream.WriteLine($Error[0].InvocationInfo.PositionMessage)
 $stream.WriteLine($wbname)
 $stream.close()


 [Environment]::Exit("99");
    }
    Else
    {
        #Write-Output "0";
        [Environment]::Exit("0");
    }

}
The two while loops are where the customization was for my particular set of problematic Excel spreadsheets, but the general structure of the code is where you can change it up for yourself.

The last four lines are cleanup - while the last might not be very important, the second-to-last line is pretty important to make sure the stick COM object goes away. Explained much better here: http://technet.microsoft.com/en-us/library/ff730962.aspx

In order to put this in sequence in my SSIS package, I used the Execute Process Task. Screenshots below:

Unfortunately the client server I was working for here only had PowerShell 1.0 installed, but from what I understand, this should apply to PowerShell 2.0 and 3.0. I'm open to anyone who has any insight there.

(click to enlarge the below images)








The argument passed to the PowerShell file includes the full path to the Excel file. (I removed the paths from this actual client implementation.)

I'm open to feedback on this, wonder if anyone else has encountered a similar issue. Obviously, the best solution would have been to modify the process that generates these maddeningly inconsistent excel files.

Thursday, October 14, 2010

October User Groups meeting and Powershell Intro for DBAs session materials

Here's a link to my slide deck and examples (including the sample scripts we didn't get to talk about) from the Baton Rouge SQL Server User Group meeting on October 13, 2010. 

The BRSSUG has been holding joint meetings with the BR .net User Group recently and its always worked out well. The two groups can share a sponsor - in last night's case it was Sparkhound - share a lightning round speaker (a short 15-minute topic session) and share door prize giveaways.  The SQL Server group moves to a different conference room after the lightning round.

In the end, a user group is all about community.  Since in reality DBAs and developers work together in the wild, why not share site/time with each other's user groups?

And yes, there was orange Fanta. 

Check out @BRSSUG's twitter page for a few pictures from last night.

Tuesday, August 10, 2010

Backup SQL with Powershell (long)

Had to set up back up a SQL Express 2005 database.  Why some vendors insist on creating software dependent on SQL Express, and then not giving their clients any way to back up said data, is mind-bogglingly stupid.

Here's a PowerShell script I wrote to accept a server name, database, and target backup folder.  It performs a single full backup when called. 

This script, which includes error-handling, sub-folder creation and more robust error reporting, is a lot more involved than any examples I found online, had to amalgamate the knowledge of dozens of google bing searches. 

Some key syntax features of Powershell that I learned about and implemented here:
  1. The InnerException loop is the only way to get the actual SQL Error out.  Otherwise, you're just stuck with Error 3041, "BACKUP failed to complete the command", which is not informative at all.  This is probably the most critical part of this, the ability to get meaningful errors into a text file log in the event of a backup failure.
  2. The Test-Path verifies if the container exists, so that you can create a directory if it doesn't.
  3. The $SMOBackupObject.Database wants a Database name (string) not a Database Object.  That really threw me off for an hour or so.  My fault, ofcourse.
  4. I used the SilentlyContinue ErrorActionPreference so that the user would see a clean error message (in my if $error.count section) instead of a messy red error from PowerShell, which would not be informative anyway (see #1).
  5. I used both the Trap syntax (to access the Exception object) and the if $error.count, because I wanted to have it return something to the end user regardless. 
  6. The out-null parts also hide messy returns to the end-user.
  7. The $error.clear() is also pretty important, as it clears out the errors that had run beforehand, if the user wants to try running the script again.  Not sure why this is necessary, its not intuitive that errors persist like that.  Open to any advice.

    function SQLFullBackup ([string]$SQLServerName, [string]$SQLDBname, [string]$BackupFolder) 
    {  
     
        # SQL Server Backup
        # Executes a single FULL backup of a given database 
     
        $error.clear()
        $ErrorActionPreference = 'SilentlyContinue'
        $BackupDate = Get-Date -format yyyyMMddHHmmss
     
        # SPECIFY SQLServer\Instance name with this variable
        $SQLServer =  New-Object ("Microsoft.SqlServer.Management.Smo.Server") $SQLServerName
     
     
        Trap [Exception] {
            $err = $_.Exception
            while ( $err.InnerException )
                {
                $err = $err.InnerException
                $errorfullmessage += $err.Message + " "
                };
     
            $errorfullmessage | out-File ($BackupFolder + "\BackupHistory_Error_" + $SQLDBName + "_" `
                + $BackupDate + ".txt");
        }
     
        #Create the subfolder if it does not already exist.
        if ((Test-Path -path ($BackupFolder + "\" + $SQLDBName) -pathtype container) -ne $True)
        {
            New-Item -path $BackupFolder -name $SQLDBName -type directory | out-null
        }
     
        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | Out-Null
        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo") | Out-Null
        [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoEnum") | Out-Null
     
        $SMOBackupObject = New-Object ("Microsoft.SqlServer.Management.Smo.Backup")
        $SMOBackupObject.Database = $SQLDBName
        $SMOBackupObject.Action = "Database" # File, Log
        $SMOBackupObject.Incremental = $false
        $SMOBackupObject.Devices.AddDevice($BackupFolder + "\" + $SQLDBName + "\" + $SQLDBName + "_" `
                + $BackupDate + ".bak", "File")
        $SMOBackupObject.SqlBackup($SQLServer) 
     
        if($error.count -gt 0)
        {   
            "Backup of database " + $SQLDBName+ " failed.  See log file in " + $BackupFolder + "\BackupHistory_Error_" `
            + $SQLDBName + "_" + $BackupDate + ".txt"
        }
                Else
        {
            "Backup of database " + $SQLDBName + " successful."
        }
     
    };
     
    Here are the function calls, per database, for the above function:

    SQLFullBackup ".\SQLEXPRESS" "express" "c:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\backup"
    SQLFullBackup ".\SQLEXPRESS" "express2" "c:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\backup"
     
    Here's sample output of a successful call or a failed call.


    PS C:\Windows\system32>C:\powershell_scripts\sqlfullbackup calls.ps1
    Backup of database express successful.
    Backup of database express2 failed.  See log file in 
    c:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\backup\BackupHistory_Error_wexpress2_20100810094357.txt 

    Here's the files created:
    BackupHistory_Error_express2_20100810094357.txt
    \express\express_20100810094357.bak

    And the error log actually contains an informative SQL Error.  In this case:

    An exception occurred while executing a Transact-SQL statement or batch. 
    Could not locate entry in sysdatabases for database 'express2'. No entry found with that name. 
    Make sure that the name is entered correctly.
    BACKUP DATABASE is terminating abnormally. 


    How did this DBA first get into Powershell?  Here's a blog post about it.  

    How I Learned to Stop Worrying and Love the Powershell

    So, as a precursor to my next blog post where I actually do something SQL related with Powershell, I'll post my first two powershell scripts here.  They are completely unrelated to SQL, but was my first exposure to Powershell 2.0 and all its majesty.

    I am a consultant, and my Cisco VPN client app has half a dozen different VPN connections.  I spent a lot of time on client sites, or, on a VPN to the client side from the office or the home office.  Needless to say, there's lots of stuff I do (such as non-billable with like this exercise, and other efforts that are billable) while not on a VPN.

    And since I don't want certain apps running on my machine while I'm on VPN, that I usually run while I am not on VPN, I wrote a pair of Powershell scripts, one called onvpn and another called offvpn.

    Here's what they do:

    offvpn.ps1 starts up my google calendar sync (which syncs my google calendar to exchange to my iphone, very handy), tweetdeck, firefox, and MSN Messenger.

    Clear-Host
    if (-not (Get-Process googlecalendarsync -ea 0) ) { Start-Process "C:\Program Files\Google\Google Calendar Sync\GoogleCalendarSync.exe" }
    if (-not (Get-Process firefox -ea 0) ) { start-process "C:\Program Files\Mozilla Firefox\firefox.exe" }
    if (-not (Get-Process msnmsgr -ea 0) ) { start-process  "C:\Program Files\Windows Live\Messenger\msnmsgr.exe" }

    if ( Get-Process vpngui -ea 0 ) { Stop-Process -name vpngui }
    if ( Get-Process vpnui -ea 0 ) { Stop-Process -name vpnui }

    what does onvpn.ps1 do?  Just the opposite, plus a few more things like the amazonmp3downloader app that only starts up on demand.  It also starts up my vpngui client and my Remote Desktop Client manager app, which I'll inevitably be using over the VPN client.


    Clear-Host
    Get-Process firefox -ea 0 | % { $_.CloseMainWindow() } 
    if ( Get-Process googlecalendarsync -ea 0 ) { Stop-Process -name googlecalendarsync } if ( Get-Process msnmsgr -ea 0 ) { Stop-Process -name msnmsgr } if ( Get-Process amazonmp3downloader -ea 0 ) { Stop-Process -name amazonmp3downloader } if (-not ( Get-Process vpngui -ea 0 ) ) { Start-Process "C:\Program Files\Cisco Systems\VPN Client\vpngui.exe" } if (-not ( Get-Process RDCMan -ea 0 ) ) { Start-Process "C:\Program Files\Remote Desktop Connection Manager\RDCMan.exe" }
     
    The Get-Process call determines if the processes are running before starting or stopping them.

    One small annoyance is that the Stop-Process command isn't that graceful. Firefox always starts up with the "well, this is embarrassing" screen asking me if I want to try and restore the previous session. If there was a way to more gracefully shut down these apps, I'd be open to it.

    (Also, as a corollary, I figured out how to post nice code.  The Visual Studio 2010 PowerTools has a copy-to-HTML feature.  Much nicer, eh?)

    Questions and comments are welcome.

    UPDATE: Updated the onvpn.ps1 script to use a kinder, gentler way of shutting down some apps.  See the comments.