PowerShell working with SQL Server SMO Month…

It’s SQL Server SMO week and I’ll be presenting at the next Space Coast SQL Server User Group on Thursday October 11th at 6:30PM. For more information check out the following #SCSUG link: http://spacecoast.sqlpass.org/

Also, I’m restarting the Florida PowerShell User Group virtual meeting this coming Friday October 12th at 11:30am rescheduled t0 October 26th due to equipment problems with the same topic: “PowerShell working with SQL Server SMO“. I will be providing the virtual meeting link on Friday morning before the meeting at my http://www.FLPSUG.com site.

Stay tuned for more information.

Create PowerShell SMO scripts without installing SQL Server

There’s no need to install SQL Server to create PowerShell SMO scripts.  So, how I do this?  You need three stand-alone packages from the “Microsoft® SQL Server® 20xx Feature Pack”.  Notice the “xx” in the SQL Server version.  This is because the
“feature packs” has been available since SQL Server Version 2005.   In my scenario I decided to use the “Microsoft® SQL Server® 2012 Feature Pack”: http://www.microsoft.com/en-us/download/details.aspx?id=29065

You need to installed them in the following order:

  1. Microsoft® System CLR Types for Microsoft® SQL Server® 2012
  2. Microsoft® SQL Server® 2012 Shared Management Objects
  3. *Microsoft® Windows PowerShell Extensions for Microsoft® SQL Server® 2012

Make sure to pick the right package for your system: (x86) or (x64).

When you’re done with the installation then you are ready to start scripting with SMO.  Also, you will notice that the SQL Server SQLPS module will get installed in your system.

Use the “Get-Module -ListAvailable” command and look for the SQLPS module in the bottom of the screen.

Now we can create the following PowerShell SMO script:

[sourcecode language=”powershell”]
## – Loading the SQL Server SMO Assembly"
[System.Reflection.Assembly]::loadwithPartialName("Microsoft.SQLServer.SMO");

## – Build SQL Server object connecting using default Windows Authentication:
$SQLSvr = ‘WIN2K3SQL1’;
$MySQL = new-object(‘Microsoft.SqlServer.Management.Smo.Server’) $SQLSvr;

## – Changing from default Windows to SQL Authentication:
$MySQL.ConnectionContext.LoginSecure = $false;
$MySQL.ConnectionContext.set_Login(‘User1’);
$SqlUPwd = ConvertTo-SecureString ‘$Pwd01!’ -AsPlainText -Force;
$MySQL.ConnectionContext.set_SecurePassword($SqlUpwd);

## – Get some information:
$MySQL.Information | Select-Object Parent, Version, Edition | fl;
[/sourcecode]

Here’s the results:
Notice I installed my three “SQL Server 2012 feature” packages and created to use a PowerShell SMO script to access a SQL Server 2000 Engine.  But, Keep In Mind, that SQL Server SMO 2012 will work for most parts to extract information off SQL Server 2000.   Just by trial and error you will notice some limitations. Also, although I have the SQLPS module available but I didn’t have to use it yet.

Go ahead and give it a try!

Here’s the link of others SQL Server Features Pack:

Feature Pack for Microsoft SQL Server 2005 – November 2005: http://www.microsoft.com/en-us/download/details.aspx?id=15748

*Microsoft SQL Server 2008 Feature Pack, August 2008: http://www.microsoft.com/en-us/download/details.aspx?id=16177

*Microsoft® SQL Server® 2008 R2 Feature Pack: http://www.microsoft.com/en-us/download/details.aspx?id=16978

*Note:  The “Microsoft® Windows PowerShell Extensions for Microsoft® SQL Server” is available since SQL Server 2008 (and greater).

PowerShell and T-SQL converting XML UTC DateTime value to DateTime string

Following my previous blog (“PowerShell – SQL Server SMO has XML and XSD Schema method“) I continue to use SMO to generate my XML files, I encountered that my SQL datetime field is converted to a UTC DateTime string. This gave me some work to get it back to a DateTime type. I had the requirement to provide my source file in XML format.

I realized using SMO WriteXml() method will write the DateTime type to a UTC DateTime string format in the XML file resulting in something like this:

[sourcecode language=”text”]
:
<OrderDate>2001-07-01T00:00:00-04:00</OrderDate>
:
[/sourcecode]

Unfortunate, trying to pass this value to a SQL DateTime field is tricky. I look around the internet and there’s plenty of examples of UTC but not in this format “2001-07-01T00:00:00-04:00″.   So, I ended up using PowerShell to ease off the pain.

Here’s an example of how PowerShell can easly convert UTC DateTime string to a regular System.DateTime type:

[sourcecode language=”powershell”]
$utc = "2001-07-01T00:00:00-04:00";
[datetime] $converte_DateTime = $utc;

## – Resulting in:
#-> Sunday, July 1, 2001 12:00:00 AM

[/sourcecode]

That was easy. Right! But the issue is when we want to pass this value into a SQL DateTime type. Then, here’s a possible solution now that I got PowerShell to convert my UTC string to string DateTime output.

Using the string generated from PowerShell, as a proof-of-concept, I can test this value in the following T-SQL script:

[sourcecode language=”sql”]
–now using t-SQL to convert a UTC string to a standard datetime type value:
Declare @stringFromPowerShellDateTime nvarchar(50)
Declare @fixDate nvarchar(50)
Declare @cleanup int
Declare @dtDateTime DateTime

Set @stringFromPowerShellDateTime = ‘Sunday, July 1, 2001 12:00:00 AM’
Set @cleanup = (charindex(‘,’, @stringFromPowerShellDateTime, 1))
Set @fixDate = (Substring(@stringFromPowerShellDateTime, (@cleanup+1), len(@stringFromPowerShellDateTime)))
Set @dtDateTime = (Convert(DateTime, @fixDate, 101))

Select @dtDateTime

— – Resulting in:
– -> 2001-07-01 00:00:00.000

[/sourcecode]

The end result of this SQL script is to be able to create my SQL DateTime type from the PowerShell converted UTC-to-DateTime-String.

Now I need go back to my first XML file, and update my DateTime elements values with the converted UTC to string format. Here’s where I go back PowerShell to fix my XML file:

[sourcecode language=”powershell”]
## – the following routine will correct UTC datetime as string:
[xml] $xmlfile = Get-Content ‘C:\PowerShellScripts\ADWtblOrders1.xml’;
foreach($xItem in ($xmlFile.newdataset.table))
{
[string] $dt = ([DateTime] $xItem.OrderDate).ToString();
$xItem.OrderDate = $dt;
};
$xmlfile.save(‘C:\PowerShellScripts\ADWtblOrders1_New.xml’);

[/sourcecode]

The results after convert the DateTime to the new XML DateTime values fix will look like this:

[sourcecode language=”text”]
:
<OrderDate>7/1/2001 12:00:00 AM</OrderDate>
:
[/sourcecode]

Now, this DateTime values can be consume in SQL Server without any issues. Notice that I’m using the “xml” Accelerator which has a *.Save() method so you can save your changes to file .

I think you got the pieces necessary to use PowerShell, XML, SMO, and T-SQL.

Don’t be afraid!

PowerShell – SQL Server SMO has XML and XSD Schema method.

As I continue to work with Microsoft BizTalk Server it still amazed me when I can find something useful.  As I was building a BizTalk solution that involves getting data from our SQL Server table, I found an SMO method that allow me to create both the XML and the XML Schema (XSD) so I can use them in my BizTalk solution.

Here’s the quick route using PowerShell and SQL Server SMO (SQL Server Management Objects) to create your XML Schema from a SQL table.  Keep in mind, in order to use SQL Server SMO you must have SQL Server 2005 or greater.

1. Here’s a simple T-SQL script to query a table:

[sourcecode language=”powershell”]
## – Prepare you SQL Script in ‘PowerShell’ Here-String:
## (for more Info on ‘Here-String’use the command: get-help about_Quoting_Rules
$Sqlqry1 = @’
Use Northwind
SELECT TOP 3 [OrderID]
,[CustomerID]
,[EmployeeID]
,[OrderDate]
,[RequiredDate]
,[ShippedDate]
,[ShipVia]
,[Freight]
,[ShipName]
,[ShipAddress]
,[ShipCity]
,[ShipRegion]
,[ShipPostalCode]
,[ShipCountry]
FROM [Northwind].[dbo].[Orders]
‘@;

[/sourcecode]

2. Then, the next three SMO lines we get the information and store it in our variable.  Make sure to define the variable as an array so you can get the right format:

[sourcecode language=”powershell”]
## – Load the SQL Server SMO Assembly:
[System.Reflection.Assembly]::loadwithPartialName(‘Microsoft.SQLServer.SMO’)

## – Connect to your SQL Server Instance and execute your SQL Query:
$MySQL = New-Object(‘Microsoft.SqlServer.Management.SMO.Server’) ‘BIZTALK2K10DEV’;
$tbldata = $MySQL.Databases[‘Northwind’].ExecuteWithResults($Sqlqry1);

[/sourcecode]

3. The following line just check the first 10 records in your PowerShell object:

[sourcecode language=”powershell”]
## – Display the first 10 lines of your data:
($tbldata.Tables) | Select -First 10 $_ | Format-Table -AutoSize;

[/sourcecode]

4. Finally, we can use the “.WriteXML()” and “.WriteXmlSchema()” methods to create our XML and XSD files:

[sourcecode language=”powershell”]
## – Create you XML file with your data:
($tbldata).WriteXml(‘C:\PowerShellScripts\tblServiceFacts.xml’);

## – To create the XML Schema (.xsd file) use the following method:
($tbldata).WriteXmlSchema(‘C:\PowerShellScripts\tblServiceFacts.xsd’);

[/sourcecode]

Sapien XML Editor - looking at teh XSD file created with SMO
Sapien XML Editor - looking at the XML file generated with SMO

We are ready to incorporate the XSD file in your BizTalk solution(s).

There’s a few things to keep in mind. The above version will work with SQL Server 2008 but will behave different with SQL Server 2005 and PowerShell V2.0 it might giving you an “overload” error.

So, if you encounter the following error, then here’s a possible how-to fix it: (it work for me but further testing in other environment gave me a different results.  It’s all just about testing and understanding your tools.

[sourcecode language=”powershell”]
## – Create you XML file with your data:
$tbldata.WriteXml([string] ‘C:\PowerShellScripts\tblOrders.xml’);

## – To create the XML Schema (.xsd file) use the following method:
$tbldata.WriteXmlSchema([string] ‘C:\PowerShellScripts\tblOrders.xsd’);

[/sourcecode]

Don’t forget to test your code to make sure you’re getting what you want.   The main code will work in both PowerShell v2.0/v3.0, and SQL Server 2008 (or greater).

Awesome Technology!   I will come back to this issue later and provide the PowerShell V2 solution.

Installing SQL Server 2012 on Server Core 2012

The Truth, it wasn’t easy.  I look around some blogs but they always failed to mention what was done prior to the actual installation of SQL Server on a Server Core machine.  But, installing SQL Server on a Server Core 2012 makes it better if you have installed the Minimal-Server interface.  Yes! The new Server Core gives you the flexibility to partially add and remove the GUI shell.

Here’s what I did to get my SQL Server on a Server Core 2012 installed in a Virtual Machine.   Remember, when using Hyper-V, you can use the shapshot feature to create a point of recovery state in case you need to go back and start over.

Setting up Server Core 2012 with Minimal-Server Interface

First, I need to make sure Server Core has all the components in place.  In my test building a virtual machine I found that I needed to have:

1. Connection to the internet or some features will fail to install.

2. I need to assign a static IP Address (so I can join my domain).

3. Add an admin user account to the local administrator group (Workgroup or Domain admin user account).

4. Configure Remote Management.

5. Configure Remote Desktop.

To get this done I use the vbscript application “SConfig” tool to assist configuring my Server Core.  It’s a great tool, and it can be executed from PowerShell prompt too.

Configuring Server Core 2012 with VBScript "SConfig" Tool

Now, keep in mind you need to meet some requirements before installing SQL Server.  Most important is installing .NET Framework 3.5.  But, if you successfully install the following Windows Features to enable the Minimal-Server interface on Server Core 2012 then you should be fine for setting up SQL Server.  Again, connection to the internet is mandatory (at least in my scenario).

Here’s the features required to *install and enabled the Minimal-Server interface:

Install-WindowsFeature -Name UpdateServices

Install-WindowsFeature -Name Net-Framework-Core

Install-WindowsFeature -Name Server-Gui-Mgmt-Infra -restart

*Note: Notice these are PowerShell commands.

Yes! You can use PowerShell.  Just type *PowerShell as soon as the command prompt shows up and you can continue building the you Server Core.  A restart of the server will be needed.

Tip: To start PowerShell in Server Core 2012 DOS command prompt, type “PowerShell” and press Enter .  Then, to get back to DOS command prompt, type the “Exit” and press Enter.

To get a list of all installed features, use the following PowerShell command:

Get-WindowsFeature

Use the above command verify the .NET Framework 3.5 features has been enabled.  Of course, after rebooting the server you will notice the Server Manager GUI will be displayed along with the command prompt.  This is a good indication .NET Framework is in place.

After rebooting, you will get the Minimal-Server Interface.

Server Core 2012 with Minimal-Server interface

Installing SQL Server 2012 on Server Core 2012

Now, I’m are ready to install SQL Server 2012.  I have attached the SQL Server 2012 ISO image to my virtual machine.  Here’s the *Setup command to install: SQLEngine, AS, IS, and Conn.  For more information: http://msdn.microsoft.com/en-us/library/hh231669.aspx

Setup.exe /qs /ACTION=Install /FEATURES=SQLEngine,AS,IS,Conn /INSTANCENAME=”MSSQLSERVER” /SQLSVCACCOUNT=”DomainName” \AccountName” /SQLSVCPASSWORD=”*******”  /SQLSYSADMINACCOUNTS=”DomainName\AccountName” /AGTSVCACCOUNT=”NT AUTHORITY \Network Service” /ASSVCACCOUNT=”NT Service\MSSQLServerOLAPService” /ASSYSADMINACCOUNTS=””DomainName\AccountName” /ASSERVERMODE=”TABULAR” /TCPENABLED=1 /IACCEPTSQLSERVERLICENSETERMS

*Note: The above command is a *one-liner.

A Word of Caution. I’ve experience that sometimes the setup will finished with no indication that everything completed successfully.  To find later that the SQLEngine wasn’t installed

I had to verify the SQL Server instance was installed by looking at the Microsoft SQL Server folder under “C:\Program Files\Microsoft SQL Server” and use the PowerShell command “Get-Service” to see all services were active.

Another way to make sure SQL Server was installed on Server Core

Now, I need to configure the SQL Remote Access to option “1” using the “SQLCMD -A” command from the “C:\Program Files\Microsoft SQL Server\110\tools\binn” folder with the following T-SQL lines:

EXEC sys.sp_configure N’remote access’, N’1′

GO

RECONFIGURE WITH OVERRIDE

GO

Exit

Using SQLCMD -A command

Test SQL Server connection remotely

Now, before trying to connect from another machine, I need to configure the Windows *Firewall rule to allow access to my SQL Server by executing the following line in the DOS command prompt:

netsh firewall set portopening protocol = TCP port = 1433 name = SQLPort mode = ENABLE scope = SUBNET profile = CURRENT

Setting Firewall SQL Server rule

*Note: This command will  execute but read the warning message

The moment of Truth!  Testing that I can connect to SQL Server on Server Core 2012 from a Windows 8 Desktop using SSMS.

Windows 8 client Virtual Machine connecting to SQL on Server Core 2012

Conclusion

In this blog I have accomplished the following in a virtual machine:

1. Build a Server Core 2012 with Minimal-Shell interface.

2. Use the VBScript “SConfig” to help configuring my server.

3. Install SQL Server 2012: Engine, Analysis Services, and Integration Services.

4. Verify remote connectivity using SSMS from a desktop machine.

I’m just having fun with technology!

PowerShell V3.0 Object difference vs V2.0…?

I was unconsciously doing something while coding in PowerShell V3.0 Beta, and it caught me off guard.  As I’m getting more comfortable working with PowerShell Objects, I didn’t realized there is an enhancement in place.  There’s a quick way to access with your objects values. I’m trying to scripting for PowerShell V2.0 but I started see errors, then I realize I was accessing my items differently in PowerShell V3.0.

Let me show some PowerShell V3.0 code that access a SQL Server SMO collection of type “Microsoft.SqlServer.Management.Smo.Information”.  I want to access the values of the property “Properties” in the SQL Server Information SMO Object.

*hint*: Remember to use the command “Get-Member” to expose the content (method & properties) in your PowerShell variable.

[sourcecode language=”powershell”]

## – Load Assembly and create your SQL object:
[System.Reflection.Assembly]::loadwithPartialName("Microsoft.SQLServer.SMO");
$MySQL = New-Object(‘Microsoft.SqlServer.Management.SMO.Server’);

## – Getting to your Information Properties"
$MySQL.Information;
$MySQL.Information.Properties;

[/sourcecode]

The last two lines will give you some results back. Now, try the next line:

[sourcecode language=”powershell”]

## – Only valid in PowerShell V3.0:
$MySQL.Information.Properties.name;

[/sourcecode]

This line will display all values in the property “Name”, and that’s great.

Go to another machine with PowerShell V2.0, and execute the lines:

[sourcecode language=”powershell”]

## – Load Assembly and create your SQL object:
[System.Reflection.Assembly]::loadwithPartialName("Microsoft.SQLServer.SMO");
$MySQL = New-Object(‘Microsoft.SqlServer.Management.SMO.Server’);

## – Only valid in PowerShell V3.0:
$MySQL.Information.Properties.name;

[/sourcecode]

Notice you will not get any results back.

Now, change the line below and the execute:

[sourcecode language=”powershell”]

## – Valid in both PowerShell V3.0 and V2.0:
$MySQL.Information.Properties | Select name;

[/sourcecode]

And, it will display all values from this property.

As you notice, using PowerShell V3.0 can list items from the collection without forcing you to pipe “|” the value to another command (“.. | Select Name ..“). Just be careful and make sure the code stay compatible with PowerShell V2.0.

I like this enhancement!

T-SQL & PowerShell – Another way to Identify Database Snapshots

Just a quick blog on spotting your Database Snaphots. I just couldn’t believe that I’ve been missing creating SQL database snapshots but sometimes having so much work make you blind.  I’ve been using a lot Hyper-V Snapshot features and recently (Thanks to Chad Miller) I got the chance to create and test a few.

So, first we need to create a new db snapshot of my AdventureWorks database using T-SQL Script:

[sourcecode language=”SQL”]
CREATE DATABASE AdventureWorks_dbSnapShot_0001 ON
( NAME = AdventureWorks_Data, FILENAME =
‘C:\Program Files\Microsoft SQL Server\MSSQL11.MSQLDENALICTP3\MSSQL\DATA\AdventureWorks_Data_0001.ss’ )
AS SNAPSHOT OF AdventureWorks;
GO
[/sourcecode]

Now, I go to SSMS and verify my new database snapshot exist by going into the ‘Object Explorer’ and looking under ‘Database Shashots’ folder.

Using T-SQL

If I use the following T-SQL command to list all my databases:

[sourcecode language=”SQL”]
Select * from [sys].[sysdatabases]
[/sourcecode]

I will get all of them listed including the snapshots. So, here’s another way to use T-SQL to identify all database snapshots. For that, I’m going to do a select and use from the Master db the view named “[sys].[databases]”:

[sourcecode language=”SQL”]
SELECT [name]
,[database_id]
,[source_database_id]
,[owner_sid]
,[create_date]
FROM [master].[sys].[databases]
[/sourcecode]

In this result, pay attention to the “[source_database_id]” column. Notice that this column is mostly “null” except when if it has a value. This value will match the “[Database_ID]” column. So, you can use this to identify database snapshots.

Using PowerShell

Now, let’s take a look on how to use PowerShell to identify database snapshots using SMO. In the following code I’m listing all databases but snapshots are also included.

[sourcecode language=”PowerShell”]
Import-Module SQLPS -DisableNameChecking
$MySQL = New-Object Microsoft.SqlServer.management.Smo.Server ‘YourServername\InstanceName’
$MySQL.Databases | Select name
[/sourcecode]

So, in order to identify the database snapshot I need to look deeper into our .NET database object. Using the “Get-Member” command I can see what’s object are available.

[sourcecode language=”PowerShell”]
($MySQL.Databases) | Get-Member | Out-Gridview
[/sourcecode]

Using the “Out-Gridview” command to view my results a separate popup window, I found two properties of interest:

  1. IsDatabaseSnapshot‘ – which show “true” us if is a snapshot.
  2. DatabaseSnapshotBaseName‘ – which give us the origne of database name of the snapshot.

So, now I can use the following PowerShell commands to show all my databases and identify the snapshots:

[sourcecode language=”PowerShell”]
$MySQL.Databases | `
Select name, Owner, RecoveryModel, `
IsDatabaseSnapshot, DatabaseSnapshotBaseName `
FT -AutoSize;
[/sourcecode]

Conclusion

Using both T-SQL and PowerShell examples, now you have a startup point to spot and take some control over your database snapshots.

Happy PowerShelling!

“PowerShell Query for the T-SQL Developer” Slide deck and samples

Once again, Thanks! to Patrick LeBlanc for allowing me to present at today’s (02/15/2012) SQLLunch.com.  It was a Great! turned out with over 60 people attending.  To You All THANKS!!  (Recorded session will be available)

Well, here’s my slide deck presentation, and the sample Demo scripts:

Also, I included the following comparison chart that help in your quest to adopt PowerShell.  This excel file is also included in the zipped file.  This is what I covered today except for “Sum”.

I just realized that this topic could evolved to include a Part II where I could show more on: Group, Sum, Logic, and Functions.  So, later on, I will be posting more information the next “PowerShell Query for the T-SQL Developer” Part II.

Stay Tuned!!

PowerShell Start-Demo now allows multi-lines one-liner

Well, I just couldn’t stop making necessary changes to this great presenter tool.  So, now Start-Demo will process your multi-lines one-liners which this couldn’t be done in the previous version.

So, a good example of a one-liner having line continuation, using the Send-MailMessage cmdlet, where we can use the “`” tick to separate the long command line with its parameters:

[sourcecode language=”powershell”]
## – Send email notification:
Send-MailMessage `
-To ‘Userto@Company1.com’ `
-From ‘SenderFrom@Company2.com’ `
-Subject ‘Test send from email’ `
-Body ‘Check email!’ `
-SmtpServer ‘Exchnage.Mailserver.com’
[/sourcecode]

Here’s an example of how it look like using “Start-Demo” in StudioShell:

And, here’s the updated Start-DemoSS.ps1 Version 2.0A 2.0B (02/12/2012):
(apologies for the previous code posted here. It somehow I posted a bad code but I fix it tonight)

[sourcecode language=”powershell”]
###########################################################################
# Original Version: 1.1
# Updated to Version 2.0B, Maximo Trinidad, 02/12/2012
#————————————————————————–
# Comments:
# 1. Customized the foreground color to Cyan and backgroundColor to Black.
# 2. Created a Dump color to default to White.
# 3. Added to put back the default foreground and background colors.
# 4. Commented out the ‘(!) Suspense’ option because Studio Shell can’t
# handle "$host.NestedPrompt".
# 5. Modify the Help menu to acomodate changes.
# 6. Commented out all "$Host.UI.RawUI.WindowTitle".
# 7. Replaced all "[System.Console]::ReadLine()" with "Read-Host".
# 8. Added an end of results ‘write-host"– Press Enter to continue –"’
# follow with a read-host similate a pause.
#
# Modifications:
# 02/10/2012 – Add section identify oneliners with continuation tick "`".
# 02/10/2012 – Cleanup all unused V1 lines.
# 02/10/2012 – Make code to properly display continuation lines.
# 02/12/2012 – Fix info on Start time and duration.
# 02/12/2012 – Adjust execution message spacing.
#
###########################################################################

function Start-Demo
{
param($file=".\demo.txt", [int]$command=0)

## – Saved previous default Host Colors:
$defaultForegroundColor = $host.UI.RawUI.ForegroundColor;
$defaultBackgroundColor = $host.UI.RawUI.BackgroundColor;

## – Customizing Host Colors:
$host.UI.RawUI.ForegroundColor = "Cyan";
$host.UI.RawUI.BackgroundColor = "Black";
$CommentColor = "Green"
$MetaCommandColor = "Cyan"
$DumpColor = "White"
$otherColor = "Yellow"
Clear-Host

## – setting demo variables:
$_Random = New-Object System.Random
$_lines = @(Get-Content $file)
$Global:starttime = [DateTime]::now
$_PretendTyping = $true
$_InterkeyPause = 100
$Global:Duration = $null

Write-Host -for $otherColor @"
Start-Demo: $file – Start time: $starttime
Version 2.0B (02/12/2012)
NOTE: Start-Demo replaces the typing but runs the actual commands.
.

"@
$continuation = $false;

# We use a FOR and an INDEX ($_i) instead of a FOREACH because
# it is possible to start at a different location and/or jump
# around in the order.
for ($_i = $Command; $_i -lt $_lines.count; $_i++)
{
if ($_lines[$_i].StartsWith("#"))
{
Write-Host -NoNewLine $("`n[$_i]PS> ")
Write-Host -NoNewLine -Foreground $CommentColor $($($_Lines[$_i]) + " ")
continue
}
else
{
# Put the current command in the Window Title along with the demo duration
$Global:Duration = [DateTime]::Now – $Global:StartTime
Write-Host -NoNewLine $("`n[$_i]PS> ")
$_SimulatedLine = $($_Lines[$_i]) + " "

for ($_j = 0; $_j -lt $_SimulatedLine.Length; $_j++)
{
Write-Host -NoNewLine $_SimulatedLine[$_j]

if ($_PretendTyping)
{
if ([System.Console]::KeyAvailable)
{
$_PretendTyping = $False
}
else
{
Start-Sleep -milliseconds $(10 + $_Random.Next($_InterkeyPause))
};
};

} # For $_j
$_PretendTyping = $true

} # else

if($_Lines[$_i] -notmatch ‘`’)
{
#Write-Host "Yes $($_Lines[$_i])" -BackgroundColor white -ForegroundColor red;
$_input = Read-Host;
} #else { $continuation = $true}

switch ($_input)
{
################ HELP with DEMO
"?"
{
Write-Host -ForeGroundColor Yellow @"
——————————————————————————–
Start-Demo – Updated to Version 2.0B (12/12/2012)
Help Running Demo: $file
.
(#x) Goto Command #x (b) Backup (?) Help
(fx) Find cmds using X (q) Quit (s) Skip
(t) Timecheck (d) Dump demo (px) Typing Pause Interval
.
NOTE 1: Any key cancels "Pretend typing" for that line. Use unless you
want to run a one of these meta-commands.
.
NOTE 2: After cmd output, enter to move to the next line in the demo.
This avoids the audience getting distracted by the next command
as you explain what happened with this command.
.
NOTE 3: The line to be run is displayed in the Window Title BEFORE it is typed.
This lets you know what to explain as it is typing.
.
NOTE 4: Although this script is functional try not to "Goto" a continuation
one-liner or it will go to a continues loop. I will correct this sympton
soon. (02/12/2012)
———————————————————————————
"@;
Write-Host "– Press Enter to continue –" -BackgroundColor white `
-ForegroundColor Magenta;
Read-Host; cls;
$_i -= 1
}

#################### PAUSE
{$_.StartsWith("p")}
{
$_InterkeyPause = [int]$_.substring(1)
$_i -= 1
}

#################### Backup
"b"
{
if($_i -gt 0)
{
$_i —

while (($_i -gt 0) -and ($_lines[$($_i)].StartsWith("#")))
{
$_i -= 1
}
}

$_i —
$_PretendTyping = $false
}

#################### QUIT
"q"
{
Write-Host -ForeGroundColor $OtherColor ""
$host.UI.RawUI.ForegroundColor = $defaultForegroundColor;
$host.UI.RawUI.BackgroundColor = $defaultBackgroundColor;
cls;
return
}

#################### SKIP
"s"
{
Write-Host -ForeGroundColor $OtherColor ""
}

#################### DUMP the DEMO
"d"
{
for ($_ni = 0; $_ni -lt $_lines.Count; $_ni++)
{
if ($_i -eq $_ni)
{
Write-Host -ForeGroundColor Yellow "$("*" * 25) >Interrupted< $("*" * 25)"
}
Write-Host -ForeGroundColor $DumpColor ("[{0,2}] {1}" -f $_ni, $_lines[$_ni])
}
$_i -= 1
Write-Host "– Press Enter to continue –" -BackgroundColor white `
-ForegroundColor Magenta;
Read-Host; cls;
}

#################### TIMECHECK
"t"
{
$Global:Duration = [DateTime]::Now – $Global:StartTime
Write-Host -ForeGroundColor $OtherColor $("Demo has run {0} Minutes and {1} Seconds`nYou are at line {2} of {3} " `
-f [int]$Global:Duration.TotalMinutes,[int]$Global:Duration.Seconds,$_i,($_lines.Count – 1))
$_i -= 1
}

#################### FIND commands in Demo
{$_.StartsWith("f")}
{
for ($_ni = 0; $_ni -lt $_lines.Count; $_ni++)
{
if ($_lines[$_ni] -match $_.SubString(1))
{
Write-Host -ForeGroundColor $OtherColor ("[{0,2}] {1}" -f $_ni, $_lines[$_ni])
}
}
$_i -= 1
};

##################### SUSPEND # –> not working in StudioShell: help (!) Suspend (not working)
#
# {$_.StartsWith("!")}
# {
# if ($_.Length -eq 1)
# {
# Write-Host -ForeGroundColor $CommentColor ""
# function Prompt {"[Demo Suspended]`nPS>"}
# $host.EnterNestedPrompt()
# }else
# {
# trap [System.Exception] {Write-Error $_;continue;}
# Invoke-Expression $(".{" + $_.SubString(1) + "}| out-host")
# }
# $_i -= 1
# }
# ——————————————————————————–

#################### GO TO
{$_.StartsWith("#")}
{
$_i = [int]($_.SubString(1)) – 1
$Scriptline = $null;
$continuation = $false;
continue
}

#################### EXECUTE
default
{
trap [System.Exception] {Write-Error $_;continue;};
## – 02/10/2012-> Commented out original line below
# Invoke-Expression $(".{" + $_lines[$_i] + "}| out-host")

## – add section identify oneliners with continuation tick:
[string] $Addline = $null;
if($_lines[$_i] -match ‘`’)
{
#Write-Host " Found tick = $($_lines[$_i])" -ForegroundColor yellow;
$Addline = $_lines[$_i].replace(‘`’,”).tostring()
$Scriptline += $Addline;
$continuation = $true;
}
else
{
$Scriptline += $_lines[$_i].ToString();
$continuation = $false;
};
if($continuation -eq $false)
{
## – Executive:
Write-Host " `r`n`t Executing Script…`r`n" -ForegroundColor $otherColor;
Invoke-Expression $(".{" +$Scriptline + "}| out-host")
}
## – ——————————————————————–
if($continuation -eq $false)
{
Write-Host "`r`n";
Write-Host "– Press Enter to continue –" -ForegroundColor Magenta `
-BackgroundColor white;
$Global:Duration = [DateTime]::Now – $Global:StartTime
Read-Host;
$Scriptline = $null;
};
}
} # Switch
} # for
## Next three list to put backl the console default colors and do a clear screen:
$host.UI.RawUI.ForegroundColor = $defaultForegroundColor;
$host.UI.RawUI.BackgroundColor = $defaultBackgroundColor;
cls;
$Global:Duration = [DateTime]::Now – $Global:StartTime; Write-Host "`r`n";
Write-Host "Start-Demo of $file completed:" -ForegroundColor $otherColor;
Write-Host -ForeGroundColor Yellow $("Total minutes/sec: {0}.{1}, Date: {2}" `
-f [int]$Global:Duration.TotalMinutes, [int]$Global:Duration.Seconds, [DateTime]::now);
} # function
[/sourcecode]

Have Fun with it!

PowerShell Start-Demo makeover for StudioShell

I blogged about a nice Visual Studio addon “StudioShell” that fits very well with SSMSSQL Server Management Studio 2012“, and I found this tool very useful for presenting.  But, when I try running the (long time) famous “Start-Demo” script, it gave me some errors.  Well, Thanks (again) to StudioShell developer Jim Christoper, he gave me a hint to why this was happenning.  Basically, this old “Start-Demo” uses some .NET calls which StudioShell can’t interpret because of the way it was develop, so it need a makeover.

So, I proceed to dive into the code and make some basic changes so it can work inside StudioShell.  Here’s how it looks running it from SSMS executing a demo script, and asking for (?) Help:

Start-Demo running in StudioShell

I change the color scheme, and added a message pause after displaying the result of the onliner.

Showing some of the changes

Also, the new function will work on a normal PowerShell console, and at the end of the start-demo it will put back your default console colors.

Here’s the updated “Start-DemoSS.ps1“code:

[sourcecode language=”powershell”]
###########################################################################
# Original Version: 1.1
# Updated to Version 2.0, Maximo Trinidad, 02/09/2012
#————————————————————————–
# Comments:
# 1. Customized the foreground color to Cyan and backgroundColor to Black.
# 2. Created a Dump color to default to White.
# 3. Added to put back the default foreground and background colors.
# 4. Commented out the ‘(!) Suspense’ option because Studio Shell can’t
# handle "$host.NestedPrompt".
# 5. Modify the Help menu to acomodate changes.
# 6. Commented out all "$Host.UI.RawUI.WindowTitle".
# 7. Replaced all "[System.Console]::ReadLine()" with "Read-Host".
# 8. Added an end of results ‘write-host"– Press Enter to continue –"’
# follow with a read-host similate a pause.
###########################################################################

function Start-Demo
{
param($file=".\demo.txt", [int]$command=0)

## – Saved previous default Host Colors:
$defaultForegroundColor = $host.UI.RawUI.ForegroundColor;
$defaultBackgroundColor = $host.UI.RawUI.BackgroundColor;

## – Customizing Host Colors:
$host.UI.RawUI.ForegroundColor = "Cyan";
$host.UI.RawUI.BackgroundColor = "Black";
$CommentColor = "Green"
$MetaCommandColor = "Cyan"
$DumpColor = "White"
Clear-Host

## – setting demo variables:
$_Random = New-Object System.Random
$_lines = @(Get-Content $file)
$_starttime = [DateTime]::now
$_PretendTyping = $true
$_InterkeyPause = 100
Write-Host -for $CommentColor @"
NOTE: Start-Demo replaces the typing but runs the actual commands.
.
<Demo [$file] Started. Type `"?`" for help>
"@

# We use a FOR and an INDEX ($_i) instead of a FOREACH because
# it is possible to start at a different location and/or jump
# around in the order.
for ($_i = $Command; $_i -lt $_lines.count; $_i++)
{
if ($_lines[$_i].StartsWith("#"))
{
Write-Host -NoNewLine $("`n[$_i]PS> ")
Write-Host -NoNewLine -Foreground $CommentColor $($($_Lines[$_i]) + " ")
continue
}else
{
# Put the current command in the Window Title along with the demo duration
$_Duration = [DateTime]::Now – $_StartTime
#X – $Host.UI.RawUI.WindowTitle = "[{0}m, {1}s] {2}" -f [int]$_Duration.TotalMinutes, `
# [int]$_Duration.Seconds, $($_Lines[$_i])
Write-Host -NoNewLine $("`n[$_i]PS> ")
$_SimulatedLine = $($_Lines[$_i]) + " "
for ($_j = 0; $_j -lt $_SimulatedLine.Length; $_j++)
{
Write-Host -NoNewLine $_SimulatedLine[$_j]
if ($_PretendTyping)
{
if ([System.Console]::KeyAvailable)
{
$_PretendTyping = $False
}
else
{
Start-Sleep -milliseconds $(10 + $_Random.Next($_InterkeyPause))
}
}
} # For $_j
$_PretendTyping = $true
} # else

#X – $_OldColor = $host.UI.RawUI.ForeGroundColor
$host.UI.RawUI.ForeGroundColor = $MetaCommandColor
#X – $_input=[System.Console]::ReadLine().TrimStart()
$_input= Read-Host
#X – $host.UI.RawUI.ForeGroundColor = $_OldColor

switch ($_input)
{
################ HELP with DEMO
"?"
{
Write-Host -ForeGroundColor Yellow @"
——————————————————————————–
Help Running Demo: $file
.
(#x) Goto Command #x (b) Backup (?) Help
(fx) Find cmds using X (q) Quit (s) Skip
(t) Timecheck (d) Dump demo (px) Typing Pause Interval
.
NOTE 1: Any key cancels "Pretend typing" for that line. Use <SPACE> unless you
want to run a one of these meta-commands.
.
NOTE 2: After cmd output, enter <CR> to move to the next line in the demo.
This avoids the audience getting distracted by the next command
as you explain what happened with this command.
.
NOTE 3: The line to be run is displayed in the Window Title BEFORE it is typed.
This lets you know what to explain as it is typing.
———————————————————————————
"@;
Write-Host "– Press Enter to continue –" -BackgroundColor white `
-ForegroundColor Magenta;
Read-Host; cls;
$_i -= 1
}

#################### PAUSE
{$_.StartsWith("p")}
{
$_InterkeyPause = [int]$_.substring(1)
$_i -= 1
}

#################### Backup
"b" {
if($_i -gt 0)
{
$_i —

while (($_i -gt 0) -and ($_lines[$($_i)].StartsWith("#")))
{ $_i -= 1
}
}

$_i —
$_PretendTyping = $false
}

#################### QUIT
"q"
{
Write-Host -ForeGroundColor $CommentColor "<Quit demo>"
$host.UI.RawUI.ForegroundColor = $defaultForegroundColor;
$host.UI.RawUI.BackgroundColor = $defaultBackgroundColor;
cls;
return
}

#################### SKIP
"s"
{
Write-Host -ForeGroundColor $CommentColor "<Skipping Cmd>"
}

#################### DUMP the DEMO
"d"
{
for ($_ni = 0; $_ni -lt $_lines.Count; $_ni++)
{
if ($_i -eq $_ni)
{ Write-Host -ForeGroundColor Yellow "$("*" * 25) >Interrupted< $("*" * 25)"
}
Write-Host -ForeGroundColor $DumpColor ("[{0,2}] {1}" -f $_ni, $_lines[$_ni])
}
$_i -= 1
Write-Host "– Press Enter to continue –" -BackgroundColor white `
-ForegroundColor Magenta;
Read-Host; cls;
}

#################### TIMECHECK
"t"
{
$_Duration = [DateTime]::Now – $_StartTime
Write-Host -ForeGroundColor $CommentColor $(
"Demo has run {0} Minutes and {1} Seconds`nYou are at line {2} of {3} " -f
[int]$_Duration.TotalMinutes,
[int]$_Duration.Seconds,
$_i,
($_lines.Count – 1)
)
$_i -= 1
}

#################### FIND commands in Demo
{$_.StartsWith("f")}
{
for ($_ni = 0; $_ni -lt $_lines.Count; $_ni++)
{
if ($_lines[$_ni] -match $_.SubString(1))
{
Write-Host -ForeGroundColor $CommentColor ("[{0,2}] {1}" -f $_ni, $_lines[$_ni])
}
}
$_i -= 1
}

# #################### SUSPEND
# –> not working in StudioShell: help (!) Suspend (not working)
#
# {$_.StartsWith("!")}
# {
# if ($_.Length -eq 1)
# {
# Write-Host -ForeGroundColor $CommentColor "<Suspended demo – type ‘Exit’ to resume>"
# function Prompt {"[Demo Suspended]`nPS>"}
# $host.EnterNestedPrompt()
# }else
# {
# trap [System.Exception] {Write-Error $_;continue;}
# Invoke-Expression $(".{" + $_.SubString(1) + "}| out-host")
# }
# $_i -= 1
# }

#################### GO TO
{$_.StartsWith("#")}
{
$_i = [int]($_.SubString(1)) – 1
continue
}

#################### EXECUTE
default
{
trap [System.Exception] {Write-Error $_;continue;}
Invoke-Expression $(".{" + $_lines[$_i] + "}| out-host")
Write-Host "– Press Enter to continue –" -BackgroundColor white -ForegroundColor Magenta;
$_Duration = [DateTime]::Now – $_StartTime
#X – $Host.UI.RawUI.WindowTitle = "[{0}m, {1}s] {2}" -f [int]$_Duration.TotalMinutes, `
# [int]$_Duration.Seconds, $($_Lines[$_i])
#X – [System.Console]::ReadLine()
Read-Host;
}
} # Switch
} # for
## Next three list to put backl the console default colors and do a clear screen:
$host.UI.RawUI.ForegroundColor = $defaultForegroundColor;
$host.UI.RawUI.BackgroundColor = $defaultBackgroundColor;
cls;
$_Duration = [DateTime]::Now – $_StartTime
Write-Host -ForeGroundColor $CommentColor $("<Demo Complete {0} Minutes and {1} Seconds>" `
-f [int]$_Duration.TotalMinutes, [int]$_Duration.Seconds)
Write-Host -ForeGroundColor $CommentColor $([DateTime]::now)
} # function
[/sourcecode]

Please, notice I named the script as “Start-DemoSS.ps1” meaning for StudioShell, still the function will be loaded as “Start-Demo“.

Usage Syntax – To load from script folder location:

.  .\Start-DemoSS.ps1

Usage Syntax – To excute a Demo.txt file:

Start-Demo .\MyDemoScript.txt

What’s inside the demo file? This is a text file containing PowerShell oneliners, and “#” comment lines.

In case you want to try StudioShell: http://studioshell.codeplex.com/