Archive for March, 2010

Script to enable preview pane for PowerShell scripts

If you are running Windows 7, you probably know what preview pane is. And if you use PowerShell and create ps1 scripts, you may also wonder how can you enable preview for PowerShell scripts in Windows Explorer.

Well, Nate Bruneau shared how to edit registry to enable preview for ps1 scripts. Being a scripter myself, I figured I would wrap it into a nice script and share it with everyone. Thanks Nate for your permission.

The scrip runs on Windows 7 and Windows Server 2008 R2. You must run it from elevated PowerShell session. The script uses elevation code from Ian Griffiths’ blog to check for elevation and exits with notification if PowerShell is not running as administrator.

Here’s the script:

#############################################################################
# Enable-ps1Preview.ps1
# This script will enable preview for ps1 files in Windows Explorer.
# Special thanks to Nate Bruneau for the idea.
#
# Created by
# Bhargav Shukla
# http://www.bhargavs.com
#
# DISCLAIMER
# ==========
# THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
# RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
#############################################################################

# Check if OS is Windows 7 or Windows Server 2008 R2, quit if not.
$OS = (Get-WmiObject -Class win32_OperatingSystem).caption
switch -wildcard ($OS)
{
  "*Windows 7*" {"`nChecking Elevation..."}
  "*Windows Server 2008 R2*" {"`nChecking Elevation..."}
  default {Write-Host -ForegroundColor Red "`nYou are not running Windows 7 or Windows Server 2008 R2. You can't use this feature on older OS."; exit}
}

# Function to check if PowerShell is running elevated
function Check-Elevated
{
  $wid=[System.Security.Principal.WindowsIdentity]::GetCurrent()
  $prp=new-object System.Security.Principal.WindowsPrincipal($wid)
  $adm=[System.Security.Principal.WindowsBuiltInRole]::Administrator
  $IsAdmin=$prp.IsInRole($adm)
  if ($IsAdmin)
  {
    Set-Variable -Name elevated -Value $true -Scope 1
  }
}

# Make registry changes if running elevated, throw error if not
Check-Elevated
If ($elevated -eq $true)
{
	# Set Registry Key variables
	$REG_KEY = ".ps1"

	# Open remote registry
	$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('ClassesRoot', ".")

	# Open the targeted remote registry key/subkey as read/write
	$regKey = $reg.OpenSubKey($REG_KEY,$true)

	# Set PerceivedType to "text"
	if ($regKey -ne $null)
	{
		$regKey.Setvalue('PerceivedType', 'text', 'String')
		$regKey.Setvalue('Content Type', 'text/plain', 'String')

		# Close the Reg Key
		$regKey.Close()

		Write-Host -ForegroundColor Green -BackgroundColor Black "Preview for .ps1 files is now enabled. Enable preview pane in Windows Explorer.`n"

	}

}
else
{
  Write-Host -ForegroundColor Red -BackgroundColor Black "Please run PowerShell as administrator before you run this script.`n"
}

You can download the script here – Enable-ps1Preview.ps1

  • Share/Bookmark
Print

Script to suppress Link State Updates

If you are in process of upgrading from Exchange 2003 to Exchange 2010, you must have read “Upgrade from Exchange 2003 Transport” article on Technet which spells out the details of a requirement – “minor link state updates must be suppressed to make sure that message looping doesn’t occur when a route is recalculated”.

I will not go into details of the requirement, however, if you read the details in the “Suppress Link State Updates” article, you know that the task can be daunting, especially if you have multiple Exchange 2003 servers, possibly in geographically dispersed locations.

Well, sweat no more. I have written a script that will help you get it done easily. Here’s the script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#############################################################################
# Suppress-Linkstate.ps1
# Using static text file or Get-ExchangeServer, this script will configure
# selected Exchange 2003 servers to suppress Link State to aid in upgrade to
# Exchange Server 2010 environment.
#
# Use this script in accordance to the following Technet article
# http://technet.microsoft.com/en-us/library/aa996728.aspx
#
# Created by
# Bhargav Shukla
# http://www.bhargavs.com
#
# DISCLAIMER
# ==========
# THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
# RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
#############################################################################
 
function Suppress-Linkstate
{
# Create empty results file or overwrite existing file
"Server Name,SuppressStateChanges,ServiceStopState,ServiceStartState" | Out-File .\results.csv -Encoding ASCII
 
	foreach ($Server in $Servers)
	{
		# Do Nothing if $_ is null
		if ($Server -ne $null)
		{
		# Set server to connect to
		#$Server = "$_"
 
		# Read SuppressStateChanges from servers
 
			# Set Registry Key variables
			$REG_KEY = "System\\CurrentControlSet\\Services\\RESvc\\Parameters"
			$VALUE = "SuppressStateChanges"
 
			# Open remote registry
			$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $Server)
 
			# Open the targeted remote registry key/subkey as read/write
			$regKey = $reg.OpenSubKey($REG_KEY,$true)
 
			# Set SuppressStateChanges to 1 if key exists
			if ($regKey -ne $null)
			{
			$regKey.Setvalue('SuppressStateChanges', '1', 'Dword')
 
			# Write changes to registry without closing it
			$regKey.Flush()
 
			# Read and Store SuppressStateChanges value in variable
			$SuppressStateChanges = $regkey.getvalue($VALUE)
 
			# Close the Reg Key
			$regKey.Close()
			}
 
		# Restart SMTP service, the Microsoft Exchange Routing Engine service, and the Microsoft Exchange MTA Stacks service
		#### You must specify a timeout (in seconds) or the script could potentially never end
		$TimeOut = 30
 
		#### This will stop a single service on all servers in sequence
		$ServiceFilters = "(name = 'smtpsvc')","(name = 'resvc')","(name = 'msexchangemta')"
 
 
			$Locator = new-object -com "WbemScripting.SWbemLocator"
			$WMI = $Locator.ConnectServer($Server, "root\cimv2")
		# Stop Service and check for timeout or sucessful stop
			$ServiceFilters | %{
				$ThisFilter = $_
				(Get-WmiObject -Class Win32_Service -ComputerName $Server -filter "$ThisFilter AND state='running'") | %{
					$Service = $_
					$Refresher = new-object -comobject "WbemScripting.SWbemRefresher"
					$FreshObject = $Refresher.Add($WMI,$Service.__RELPATH)
					$Refresher.Refresh()
					$Then = Get-Date
					:Checking Do {
						$Service.StopService() | out-null
						$Refresher.Refresh()
						if (($FreshObject.Object.properties_ | ?{$_.name -eq "state"}).value -eq "Stopped") {
							$ServiceStopState = "Service $($Service.Name) stopped";
 
							# Store result string in a variable
							$result = "$Server,$SuppressStateChanges,$ServiceStopState,$ServiceStartState"
 
							# Write results to file
							$result | Out-File .\results.csv -Encoding ASCII -Append
 
							$ServiceStopState = $null
 
							break :Checking;
						} Else {
							If (((Get-Date) - $Then).seconds -ge $TimeOut){
								$ServiceStopState = "Service $($Service.Name) stop TIMEOUT";
 
								# Store result string in a variable
								$result = "$Server,$SuppressStateChanges,$ServiceStopState,$ServiceStartState"
 
								# Write results to file
								$result | Out-File .\results.csv -Encoding ASCII -Append
 
								$ServiceStopState = $null
 
								break :Checking;
							}
						}
					} While ($True)
				}
			}
 
		# Start Service and check for timeout or sucessful start
			$ServiceFilters | %{
				$ThisFilter = $_
				(Get-WmiObject -Class Win32_Service -ComputerName $Server -filter "$ThisFilter AND state='Stopped'") | %{
					$Service = $_
					$Refresher = new-object -comobject "WbemScripting.SWbemRefresher"
					$FreshObject = $Refresher.Add($WMI,$Service.__RELPATH)
					$Refresher.Refresh()
					$Then = Get-Date
					:Checking Do {
						$Service.StartService() | out-null
						$Refresher.Refresh()
						if (($FreshObject.Object.properties_ | ?{$_.name -eq "state"}).value -eq "running") {
							$ServiceStartState = "Service $($Service.Name) started";
 
							# Store result string in a variable
							$result = "$Server,$SuppressStateChanges,$ServiceStopState,$ServiceStartState"
 
							# Write results to file
							$result | Out-File .\results.csv -Encoding ASCII -Append
 
							$ServiceStartState = $null
 
							break :Checking;
						} Else {
							If (((Get-Date) - $Then).seconds -ge $TimeOut){
								$ServiceStartState = "Service $($Service.Name) start TIMEOUT";
 
								# Store result string in a variable
								$result = "$Server,$SuppressStateChanges,$ServiceStopState,$ServiceStartState"
 
								# Write results to file
								$result | Out-File .\results.csv -Encoding ASCII -Append
 
								$ServiceStartState = $null
 
								break :Checking;
							}
						}
					} While ($True)
				}
			}
		}
	}
}
 
 
#Add Exchange 2010 snapin if not loaded
# Uncomment following four (4) lines if using dynamic list mentioned below
If ((Get-PSSnapin | where {$_.Name -match "E2010"}) -eq $null)
{
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010
}
 
# Uncomment next line if you want to dynamically get Exchange 2003 servers using Exchange 2010 Management Shell
$Servers = (get-exchangeserver | Where-Object {$_.AdminDisplayVersion -like '*6.5*'} | ForEach-Object {$_.Name})
# Comment line above and Uncomment next line if you want to provide list of servers in a text file (one server per line)
# You can't have both of the abovementioned lines uncommented. Please use one of your choice.
# $Servers = Get-Content .\servers.txt
 
$Servers | Suppress-Linkstate

Few important pointers:

  • The script can use one of the two methods, please comment and uncomment necessary lines as documented in script

     – Dynamically retrieve list of Exchange 2003 servers in your environment using Get-ExchangeServer cmdlet from an Exchange 2010 server

     – Use list of Exchange 2003 servers provided in servers.txt file (one server per line)

  • If you choose to use dynamic method, you  must have Exchange server 2010 admin tools installed on the machine where the script is run from
  • The output will be store in results.csv file
  • Timeout value in line 62 works fine in my lab tests, it may not work for you. Please adjust the value as necessary

You can download the script from here: Suppress-Linkstate.ps1

I am sure there are ways to optimize and improve this script. I would love to know how I can make the script better and more useful. Please feel free to drop me a line using contact form. I will be happy to hear your feedback and improve script. You can also use comments option on this post to leave your feedback.

  • Share/Bookmark
Print

Tags: , , ,

Send a Tweet from PowerShell

I was reading Shay’s article on how to send direct messages on twitter using PowerShell when I couldn’t resist but find a way to send normal tweets from PowerShell. While trying to do that, I also incorporated security so that your transmission is secure and so is your input. Here’s the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Send-Tweet($Message,$UserName)
{
   if ($Message -eq $null) {$Message = Read-Host "Enter your tweet"}
   if ($Username -eq $null) {$Username = read-host "Enter your twitter username"}
   if ($Password -eq $null)
   {
	$Password = read-host -assecurestring "Enter your twitter password"
	$marshal = [Runtime.InteropServices.Marshal]
	$Password = $marshal::PtrToStringAuto($marshal::SecureStringToBSTR($Password))
   }
   $url="https://twitter.com/statuses/update.xml?status=$Message"
   $request = [System.Net.WebRequest]::Create($url)
   $request.credentials= New-Object System.Net.NetworkCredential($UserName,$Password)
   $request.method= "POST"
   $request.contentType = "application/x-www-form-urlencoded"
   $request.GetResponse().statusCode # return the status code of the request
}

To send tweet, you need to type:

Send-Tweet -Message "Hello World!" -Username userxyz

Notice I did not include –Password. Infact, I did not include $Password in parenthesis after function. This way even if you were using tab to cycle through available parameters, you won’t see “Password” as a parameter. It still won’t stop you from typing:

Send-Tweet -Message "Hello World!" -Username userxyz -Password password

But by not declaring the parameter, I am encouraging one to skip typing password in plain-text. When user sends tweet using first option (without password) this script will prompt user for password. How is it secure? Notice –assecurestring when I am prompting user for password. This way your entry will show up as ****** instead of plaintext. If you were to use second method, you will type your password in plaintext and I am assuming you will do that when you are sure your screen is not visible to anyone else.

Another nice feature in my script is, I am using https in URL. This way I am sending authentication string over SSL. Shay’s original script for direct message does not use SSL hence username and password is transmitted in base64 encoding of string “username:password”. Your tweet will be published and will be in public domain but I don’t like the idea of transmitting username and password the same way. Here’s why:

The network capture shows the following:

image

Now let’s see what we can do with highlighted string in PowerShell:

PS> $b64upass = "dXNlcm5hbWU6cGFzc3dvcmQ="
PS> $bytes = [convert]::FromBase64String($b64upass)
PS> $decoded = [System.Text.Encoding]::UTF8.GetString($bytes)
PS> $decoded
username:password

So as you can see, even though Base64 is not plaintext, it’s very easy to decode and your username and password is more valuable than that!

If you are going to use Shay’s script for direct tweets, you can simply change URL to https and it should transmit securely. you can also use other ideas from my script to make password entry more secure.

Here’s another way of doing it:

$Tweetcred = Get-Credential
$UserName=$Tweetcred.GetNetworkCredential().UserName
$Password=$Tweetcred.GetNetworkCredential().Password

Using this method simply asks user for username and password in standard windows security dialog box and stores it in credential object of the OS.

image

I thank Shay for showing me way and hope you will benefit from both his and my scripts!

Update: Including link to Wikipedia article on HTTP Basic Access Authentication for reference.

  • Share/Bookmark
Print

Tags: , , ,

Charter Member of MCITP: Enterprise Messaging Administrator Exchange 2010

I just came to know that I am a “Charter Member” of MCITP: Enterprise Messaging Administrator 2010. It is interesting because I took exams when they were in Beta stage and knew I would be one of the first few, however, when I got certificate from MCP site right after getting certified, it did not who me as Charter Member. I did not really bother wondering why or email MCP team. However, today when I saw someone else posting about it, I was curious so I headed to MCP site and checked again. To my surprise, the Certificate download tool now listed me as Charter Member! Interesting but not surprising. It’s always good to know I got that special designation.

There are different views about what a Charter Member is. Some suggest it is first 5000 who get certified and the MCP site suggests differently. I am going to quote what I consider an authoritative resource: Microsoft Learning Site.

As per the site:

“Charter Members are the pioneering group of individuals who achieve a certification shortly after the certification becomes available. When a Charter Member certification is offered, it is available to candidates who achieve the new certification within six months after the certification exam is released. Charter Members are recognized by receiving the Charter version of the certificate.”

But hey, either way, I am a Charter Member. Hurray! :)

MCITP-2010-Charter

  • Share/Bookmark
Print

Tags: , ,

Enterprise Root CA Web Enrollment – RTFM, but what if I didn’t?

I was building Enterprise Root CA in my lab and was in rush so ofcourse, I did not read the fabulous manual and went forward with installing the Enterprise Root CA without installing IIS. I did know IIS is needed for web enrollment but did not care to check if it was already installed. It sure wasn’t.

So the Root CA was installed but I could not request certificates using web enrollment.

I went ahead and installed IIS and enabled ASP after the fact but web enrollment still wasn’t going to work as the virtual directories for web enrollment were missing as expected. Only if I had read RTFM.

What can I do now? How big of an issue this is going to be for me? luckily not any bigger than issuing a command “certutil –vroot”. As long as IIS was installed correctly, the command creates necessary, checks if ASP is enabled and warns you if not. That’s it for fixing the problem I created for myself. Thanks fabulous programmers at Microsoft to make products work for those who don’t read the fabulous manuals without real headache of reinstall.

Off to my next task…

  • Share/Bookmark
Print

Tags: , ,