September 2014 - Posts

I teach a three-day course for System Administrators. I call it "Pragmatic PowerShell" - and that's exactly what it is about. Teaching administrators what they need to know about using PowerShell.

Of course, discoverability is a large part of the process. So we cover the standard stuff: Active Directory, DNS, DHCP, Exchange, and Lync. But what I find is that while most SysAdmins are comfortable with executing "one command, one result", most of them don't understand the concepts behind looping, functions, and conditional statements. And that's fine - if they weren't mainframe SysAdmins, they probably never had to write scripts before.

Note: Mainframe SysAdmins have been writing scripts for many decades; Job Control Language (JCL) on IBM mainframes, Work Flow Language (WFL) on Burroughs mainframes, Symbolic Stream Generator (SSG) on Univac mainframes, etc. etc.

So, it is important to cover these things to the students in ways that they can understand, and hopefully, to have a little fun with it at the same time.

Therefore, I went back and found a few of the simple computer games I used to play. In the 1970's and 1980's, many BASIC-based computer games were available, and these were commonly published in books and magazines and on BBSs (Bulletin Board Systems). People would buy the books or the magazines and then type the games into their computer. YES, actually type them!

I was six years-old when Neil Armstrong landed on the moon. I remember watching the TV transmission, sitting at my grandmother's house. Like most boys of my age, I wanted to grow up and become an astronaut! Alas, it was not to be.

But playing computer games, I never lost that desire. And it took me, a decade later, to study Physics in college.

"Lunar Lander", or "Lunar Exploration Module", or "Rocket" were common versions of programs that simulated a landing on the moon. Usually with some ASCII graphics.

Here is a PowerShell version (with some minor enhancements) of "Rocket".

Would you like to play a game?

 


###
### Rocket.ps1
###
### Algorithm is from the original Jim Storer "Lunar Lander" Basic 
### program from 1969
###
### Adapted from
### 	http://www.vintage-basic.net/bcg/rocket.bas
###	Retrieved on 2014-09-15
###
###	with a couple of minor bugfixes.
###
### Original program source came from "Basic Computer Games",
### first published in 1978. ISBN-10: 0894800523.
###
### This was one of the very first computer games that I ever
### played, a version in the late 1970's. Until graphics became
### more common (early 1980's), text games were the prevalent
### versions of all games. I can remember playing this game on DEC
### PDP computers (loaded from paper tape) and from original Apple
### computers (loaded from cassette tape).
###
### Michael B. Smith
### michael at TheEssentialExchange dot com
### September, 2014
###

###
### Newtonian one-dimensional physics says:
###
###	x' = x + vt + (1/2) * a * t * t
###
### Deriving this equation is trivial and left as an
### exercise for the reader. (Hint: use induction.)
###
### This program implements that algorithm, adjusted for
### the gravitational attraction of the moon.
###
### On the Earth, the gross value of gravitational
### acceleration is 32 feet/second/second.  The value of
### the gravitational acceleration on the moon is about
### one-sixth of that amount ( 16.6% ). This program
### uses a value of 5 feet/second/second. (Which is 
### close - the calculated value is 5.31 ft/sec/sec,
### which is only a 6.2% difference).)
###
### When originally written, floating point math was
### much more expensive than integer math. So most of
### the calculations will provide integer results.
### Newer versions of this program (based on lunar.bas
### and on lem.bas) use floating point math and will
### provide slightly more accurate results.
###
### However - you may find that this game is more
### challenging than you expect. :)
###

function GetYorN( [string] $prompt )
{
	while( 1 )
	{
		$answer = Read-Host $prompt
		if( $answer -eq "n" -or $answer -eq "no" )
		{

			return "n"
		}
		if( $answer -eq "y" -or $answer -eq "yes" )
		{
			return "y"
		}
	}	
}

function print-header
{
	" " * 30 + "Rocket"
	" " * 15 + "Creative Computing  Morristown, New Jersey"
	" "
	" "
	" "
	"Apollo Lunar Landing Simulation"
	"------ ----- ------- ----------"
	" "
}

function print-instructions
{
	$a = GetYorN "Do You Want Instructions (Yes or No)?"
	if( $a -eq "n" )
	{
		return
	}

	"You are landing on the moon and have taken over manual"
	"control 1,000 feet above a good landing spot. You have"
	"a downward velocity of 50 feet/sec. 150 units of fuel"
	"remain."
	" "
	"Here are the rules that govern your Apollo space-craft:"
	" "
	"[1] After each second, the height, velocity, and remaining"
	"    fuel will be reported via Digby, your on-board computer."
	"[2] After the report, a '?' will appear. Enter the number"
	"    of units of fuel you wish to burn during the next"
	"    second. Each unit of fuel will slow your descent by"
	"    1 foot/sec."
	"[3] The maximum thrust of your engine is 30 feet/sec/sec"
	"    or 30 units of fuel per second. If you enter a value"
	"    which exceeds 30, your value will be set to 30. If"
	"    you enter a value less than zero, your value will be"
	"    set to zero."
	"[4] When you contact the lunar surface, your descent engine"
	"    will automatically shut down and you will be given a"
	"    report of your landing speed and remaining fuel."
	"[5] If you run out of fuel, the '?' will no longer appear"
	"    but your second-by-second report will continue until"
	"    you contact the lunar surface."
	" "
}

function print-introduction
{
	"Beginning landing procedure.........."
	" "
	"G o o d  L u c k ! ! !"
	" "
	" "
	"SEC  FEET      SPEED     FUEL     PLOT OF DISTANCE"
	" "
}

function process-contact
{
	"*** Moon Contact ***"       ## 670 580
	$h = $h + 0.5 * ( $v1 + $v ) ## 680
	if( $b -eq 5 )
	{
		## time delta = height / velocity
		$d = $h / $v         ## 720 690
	}
	else
	{
		## time delta = -v + ( Sqrt( v*v + ( h * ( 10 - 2 * b ) ) ) / ( 5 - b )
		$d = ( -$v + [Math]::Sqrt( ( $v * $v ) + $h * ( 10 - 2 * $b ) ) ) / ( 5 - $b ) ## 700
	}

	$v1 = $v + ( 5 - $b ) * $d   ## 730 710
	"Touchdown at " + ( $t + $d ).ToString( 'N1' ) + " seconds."  ## 760
	"Landing velocity " + $v1.ToString( 'N1' ) + " feet/second."  ## 770
	$f.ToString() + " units of fuel remaining."                   ## 780

	if( $v1 -eq 0 )
	{
		"Congratulations! A perfect landing!!"                      ## 800
		"Your license will be renewed........Later."                ## 805
	}

	if( [Math]::Abs( $v1 ) -ge 2 )
	{
		"***** Sorry, but you blew it!!!! *****"                    ## 820
		"Everyone on the Lunar Landing Module is dead."
		"Appropriate condolences will be sent to the next of kin."  ## 830
	}

	" "                                                                 ## 840 
	" "
	" "
}

function print-status
{
	$str = "{0,-4:N0} {1,-8:N0}  {2,-8:N0}  {3,-7:N0}  I" -f $t, $h, $v, $f
	$str += ( "*" * ( [int] ( $h / 15 / 2 ) ) ) + "*"
	$str
}

###
###	Main
###

	print-header
	print-instructions

	while( 1 )
	{
		print-introduction      ## 390   860

		$t = 0	   ## 455	## time in seconds, at beginning of simulation
		$h = 1000  ## 455	## height in feet, at beginning of simulation
		$v = 50    ## 455	## velocity in feet per second, at beginning of simulation
		$f = 150   ## 455	## available fuel in arbitrary units, at beginning of simulation

		[bool] $done = $false

		while( $f -gt 0 -or !$done )
		{
			if( $f -gt 0 )
			{
				print-status                 ## 490 610

				$b = [int] ( Read-Host '?' ) ## 500
				if( $b -lt 0 )  { $b = 0  }  ## 510
				if( $b -gt 30 ) { $b = 30 }  ## 520
				if( $b -gt $f ) { $b = $f }  ## 530
			}

			$v1 = $v - $b + 5               ## 540 660
			$f = $f - $b                    ## 560
			$h = $h - 0.5 * ( $v1 + $v )    ## 570

			if( $h -le 0 )                  ## 580
			{                               ## 580
				$done = $true           ## 580
				break                   ## 580
			}                               ## 580

			$t = $t + 1                     ## 590
			$v = $v1                        ## 600

			if( $f -gt 0 )                  ## 610
			{                               ## 610
				continue                ## 610
			}                               ## 610

			if( $b -ne 0 )                  ## 615
			{                               ## 620
				"*** Out of fuel. ***"  ## 620
			}                               ## 620

			print-status                    ## 640

			$b = 0	                        ## 650
		}

		process-contact                         ## 670 - 840

		$a = GetYorN "Another mission (Yes or No)?"
		if( $a -eq "n" -or $a -eq "no" )
		{
			" "
			"Apollo Launch Control, signing out."
			"------ ------ -------- ------- ----"
			" "

			break  ## exit while(1) loop
		}
	}

The line numbers are from rocket.bas available from the link shown in the script source.

Follow me on Twitter at @essentialexch 

"WOULD YOU LIKE TO PLAY A GAME?" is a quote from "WarGames", starring a very young Matthew Broderick and Ally Sheedy, released in 1983.

Posted by michael | 1 comment(s)
Filed under:

Exchange 2010 and Exchange 2013 are rich with cmdlets providing you access to information regarding your Exchange environment.

However, as of Exchange 2013 CU6, there are 956 (!!) cmdlets. Knowing which cmdlets to use can be challenging. It can also be difficult to decide what information from a cmdlet's output is relevant.

Here is my attempt to consolidate output for Client Access Servers. This script is in daily use, and it works for me. I hope you find it useful.

If this script is executed on an Exchange Server, it will by default generate information for that server. You can change that by specifying the "-server <servername>" switch. The "-location <location-name>" switch is used to specify a physical location where a particular server resides. For example "London" or "Charlotte". The script defaults to using "location". FInally, if you specify "-Verbose", the script will output timing information. That is, how long the script took to execute and how long each cmdlet took to execute.

Enjoy!


##
## Dump-CasInformation.ps1
##
## Michael B. Smith
## September 8, 2014
## michael at TheEssentialExchange dot com
## http://TheEssentialExchange.com/blogs/michael
##
## No warranties, express or implied. Use at your own risk.
##
[CmdletBinding(SupportsShouldProcess=$false, ConfirmImpact='None') ]

Param(
	[Parameter(Mandatory=$false)]
	[string] $location  = "location",

	[Parameter(Mandatory=$false)]
	[string] $server    = $env:ComputerName
)

Set-Strictmode -Version 2.0

$startScript = Get-Date
Write-Verbose "Dump-Casinformation script starts $($startScript | Get-Date -Format u)"

$location = $location.ToUpper()

if( ( Get-Command Get-ExchangeServer -EA 0 ) -eq $null )
{
	Write-Error "This script must be executed within an Exchange Management Shell"
	return
}

"****** GLOBAL  GLOBAL  GLOBAL ******" 

	$cmd = 'OutlookProvider'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

		$r = Get-OutlookProvider

		$r | fl Name, CertPrincipalName, Server, TTL, 
			OutlookProviderFlags, 
			RequiredClientVersions, ExchangeVersion

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

#####

	$cmd = 'ExchangeServer'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

		$r = Get-ExchangeServer -Status

		$r | fl Name, Identity, Fqdn, Edition, 
			Site, OrganizationalUnit, ServerRole, 
			AdminDisplayVersion, ExchangeVersion, 
			Static*, Current*,
			Is*

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

"****** $location  $location  $location ******"

	$cmd = 'ClientAccessServer'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

		$r = Get-ClientAccessServer -Identity $server

		$r | fl Name, Identity, Fqdn,
			IsOutOfService,
			OutlookAnywhereEnabled,
			ClientAccessArray,
			ExchangeVersion,
			AlternateServiceAccountConfiguration,
			OutlookAny*, 
			AutoDiscover*

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

#####

	$cmd = 'OutlookAnywhere'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

		$r = Get-OutlookAnywhere -server $server

		$r | fl Server, ServerName, Name, Identity, 
			MetabasePath, Path, 
			AdminDisplayVersion, ExchangeVersion,
			SSLOffloading,
			InternalHostname, InternalClientAuth*,
			InternalClientsRequireSSL,
			ExternalHostname, ExternalClientAuth*, 
			ExternalClientsRequireSSL,
			IISAuthenticationMethods,
			XropUrl,
			ExtendedPro*

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

#####

	$cmd = 'RpcClientAccess'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

		$r = Get-RpcClientAccess -server $server

		$r | fl Name, Server, Identity,
			Responsibility,
			MaximumConnections,
			EncryptionRequired,
			BlockedClientVersions,
			ExchangeVersion

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

#####

	$cmd = 'ActiveSync'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

		$r = Get-ActiveSyncVirtualDirectory -server $server

		$r | fl Name, Server, VirtualDirectoryName, 
			WebsiteName, WebsiteSSLEnabled, CompressionEnabled,
			MetabasePath, Path,
			AdminDisplayVersion, ExchangeVersion,
			InternalUrl, InternalAuth*,
			ExternalUrl, ExternalAuth*,
			ActiveSyncServer,
			*AuthEnabled, ClientCertAuth,
			Mobile*,
			BadItemReportingEnabled, SendWatsonReport,
			Remote*,
			Extended*

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

#####

	$cmd = 'AutoDiscover'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

		$r = Get-AutodiscoverVirtualDirectory -server $server

		if( -not [String]::IsNullOrEmpty( $r.InternalUrl ) )
		{
			Write-Warning "InternalUrl should be empty, instead is $($r.InternalUrl)"
		}
		if( -not [String]::IsNullOrEmpty( $r.ExternalUrl ) )
		{
			Write-Warning "ExternalUrl should be empty, instead is $($r.ExternalUrl)"
		}

		$r | fl Server, Name, Identity, 
			MetabasePath, Path, 
			AdminDisplayVersion, ExchangeVersion,
			InternalUrl, InternalAuth*, 
			ExternalUrl, ExternalAuth*, 
			*Authentication, ExtendedPro*

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

#####

	$cmd = 'ECP'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

		$r = Get-EcpVirtualDirectory -server $server

		$r | fl Server, Name, Website, DisplayName, Identity, 
			MetabasePath, Path, 
			AdminDisplayVersion, ExchangeVersion,
			InternalUrl, InternalAuth*, 
			ExternalUrl, ExternalAuth*, 
			DefaultDomain, GzipLevel, *Enabled,
			*Authentication, ExtendedPro*

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

#####

	if( ( Get-Command Get-MapiVirtualDirectory -EA 0 ) -ne $null )
	{
		## cmdlet not available in Exchange 2010

		$cmd = 'MAPI'
		"*** $cmd  $cmd  $cmd ***"
		$cmdStart = Get-Date
		Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

			$r = Get-MapiVirtualDirectory -server $server

			$r | fl Server, Name, Identity, 
				MetabasePath, Path, 
				AdminDisplayVersion, ExchangeVersion,
				InternalUrl, InternalAuth*, 
				ExternalUrl, ExternalAuth*, 
				IISAuth*,
				ExtendedPro*

		$cmdEnd = Get-Date
		$cmdDelta = $cmdEnd - $cmdStart
		Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
		Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"
	}
	elseif( $server -ne $env:ComputerName )
	{
		Write-Warning "Get-MapiVirtualDirectory is not available on this computer (so this computer is probably running Exchange 2010). If you are accessing a server running Exchange 2013 or higher, this information will be missing."
		" "
	}

#####

	$cmd = 'OAB'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

		$r = Get-OabVirtualDirectory -server $server

		$r | fl Server, Name, Identity, RequireSSL,
			MetabasePath, Path, 
			AdminDisplayVersion, ExchangeVersion,
			InternalUrl, InternalAuth*, 
			ExternalUrl, ExternalAuth*, 
			PollInterval, OfflineAddressBooks,
			*Authentication, ExtendedPro*

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

#####

	$cmd = 'OWA'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

	## OWA is just too big to process effectively

		$r = Get-OwaVirtualDirectory -server $server

		$r | fl Server, ServerName, Name, Website, DisplayName, Identity, RequireSSL,
			MetabasePath, Path, 
			DefaultDomain, LogonFormat,
			AdminDisplayVersion, ExchangeVersion,
			InternalUrl, InternalAuth*, 
			ExternalUrl, ExternalAuth*, 
			VirtualDirectoryType, GzipLevel,
			Exchange2003Url, FailbackUrl, 
			LegacyRedirectType, RedirectToOptimalOWAServer,
			*Authentication, ExtendedPro*
		"...attribute list truncated, use 'Get-OwaVirtualDirectory -server $server | fl *' for full information"
		" "

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

#####

	$cmd = 'PowerShell'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

	## this will return 2 entries for each server.
	"Note: the PowerShell Virtual Directory results include two vDirs for each server."

		$r = Get-PowerShellVirtualDirectory -server $server

		$r | fl Server, Name, Identity, RequireSSL,
			MetabasePath, Path, 
			OwaVersion, AdminDisplayVersion, ExchangeVersion,
			InternalUrl, InternalAuth*, 
			ExternalUrl, ExternalAuth*, 
			VirtualDirectoryType, 
			*Authentication, ExtendedPro*

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

#####

	$cmd = 'WebServices'
	"*** $cmd  $cmd  $cmd ***"
	$cmdStart = Get-Date
	Write-Verbose "Dump-Casinformation command $cmd starts $($cmdStart | Get-Date -Format u)"

		$r = Get-WebServicesVirtualDirectory -server $server

		$r | fl Server, Name, Identity,
			MetabasePath, Path, 
			AdminDisplayVersion, ExchangeVersion,
			InternalUrl, InternalAuth*, 
			ExternalUrl, ExternalAuth*, 
			InternalNLBBypassUrl, GzipLevel, MRSProxyEnabled,
			*Authentication, ExtendedPro*

	$cmdEnd = Get-Date
	$cmdDelta = $cmdEnd - $cmdStart
	Write-Verbose "Dump-CasInformation command $cmd ends $($cmdEnd | Get-Date -Format u)"
	Write-Verbose "Dump-CasInformation command $cmd took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

"****** DONE  DONE  DONE ******"

$scriptEnd = Get-Date
$cmdDelta = $scriptEnd - $startScript
Write-Verbose "Dump-CasInformation script ends $($scriptEnd | Get-Date -Format u)"
Write-Verbose "Dump-CasInformation script took $($cmdDelta.TotalSeconds.ToString( 'N2' )) seconds"

Follow me on Twitter at @essentialexch 

Posted by michael | with no comments