May
31
2012

Server 8 and PowerShell published via OData

Repost from here...

 

There is a new feature of Windows Server 8 that will allow for access to PowerShell cmdlets and objects via OData served through ASP.NET. Doug Finke wrote a blog post for PowerShell Magazine on the topic. The article gives a good overview of what the Management OData feature is and how to configure it. In this blog post I will be showing off some of the steps involved in getting the service configured and what it looks like to consume the OData in PowerShell.

Setting Up the Management OData Feature

The first step in utilizing the Management OData in Windows Server 2012 is to enable the feature. You can either use Server Manager or the following cmdlet.

Install-WindowsFeature -Name ManagementOData

Once the feature has been installed you will need to install Visual Studio 2011 Beta on the Server machine. I hope there is better tooling around this but currently this is what is required in order to build the samples found on MSDN. Make sure to download the Management OData Schema Designer. For a whole lot information on the topic, download and read the whitepaper. It walks you through all the steps I’m about to, in more detail.

What Management OData Does

In simple terms (read Doug’s post for more info), the Management OData service provides RESTful endpoints that server up PowerShell objects. The schema designer is used to map cmdlets and their resulting objects to OData objects. These can then be served as JSON back through the endpoint to the client.  The Management OData Schema Designer is used to take existing modules, cmdlets and objects and map them to XML files that can then be consumed by the Management OData system and served to clients. Included with the examples are PowerShell scripts used to install the OData endpoints once they have been compiled.

Installing the Basic Endpoint Sample

Once Visual Studio 2011 has been installed open the BasicPlugin.sln solution. You can download the sample here. Once the solution is open, build it. Once the solution is built, run the SetupEndpoints.ps1 file to configure the endpoint on the local server. The file is part of the Basic Endpoint solution folder. You should now be able to navigate to the URL:

http://localhost:7000/MODataSvc/Microsoft.Management.Odata.svc

This should result in this:

To query particular resources, you can now query it like so http://localhost:7000/MODataSvc/Microsoft.Management.Odata.svc/Process. This will return all the processes on the server machine. The Content property contains all the XML for the objects.

Additionally, processes can be filtered using a URI-based syntax. For example:

http://localhost:7000/MODataSvc/Microsoft.Management.Odata.svc/Process?$filter=(Handles gt 1000)

By default the returned format will be XML. In order to return JSON, you have to use the following syntax.

http://localhost:7000/MODataSvc/Microsoft.Management.Odata.svc/Process?$format=JSON

Remember that you can utilize the new ConvertFrom-Json cmdlet to to convert the JSON to objects.

Pretty powerful stuff!

Adding new Cmdlets and Objects

The Management OData Schema Designer is used to add more entities to the OData service. Once installed there should be an icon placed on the desktop. Open the designer. The first step is to load a module that you want to model and expose in OData. For this example I will use the NetAdapter module. Type the module name into the text box and click Load New Module. Once the module is loaded you will see a big list of the types of entities the designer finds within the module. These coincide with the nouns of the cmdlets within the module. The verbs Get, Set, New, Remove will appear as check boxes next to the noun names. If a cmdlet is not defined the verb will be grayed out.

In order to map a new OData action and entity, check one of the verb for a noun. I selected the Get and NetAdapter verb and noun. Next click the “from cmdlet output” button. The cmdlet will appear in the displayed box. Clicking Add-Type will add the new OData entity. In order to successfully generate the MOF and XML needed to define the object, you will need to set a Key property. This is the uniquely defining property on the object. Name is already selected for NetAdapter.

Now you can click Generate Mof/Xml Schema. This will produce the mapping files that the Management OData service will use to translate between the REST request and the PowerShell cmdlet and resulting objects. Once saved, you can place this in C:\inetpub\wwwroot\modata.

Since the OData endpoint is constrained we need to play with the BasicPlugin a bit to get it to load the module we would like. In Visual Studio, I added the following lines to get the NetAdapter module to load into the runspace and to set the visibility of the proper cmdlets in the runspace. I just set them all to visible. Once built, copy the resulting DLL into the MOData folder and replace the one that is in there already. You may need to stop IIS first.

Now you should be able to query to the location:

http://localhost:7000/MODataSvc/Microsoft.Management.Odata.svc/NetAdapter

Note that the resource identifier (e.g. NetAdapter) is case sensitive in the beta!

Remember there is also an Invoke-RestMethod cmdlet that can be used to query the entities. Using this method, it looks something like this.

I think this is insanely powerful functionality. It seems that the tooling isn’t quite 100% yet and requires quite a bit of setup to get running but the possibilities are endless. Creating RESTful services from PowerShell modules will be a cinch! I really encourage you to read the whitepaper about this that I mentioned earlier. It contains a ton of information.

 

 

 

 

 

 

 

 

 

 

 

May
30
2012

Using Powershell to play MP3 Audiobooks on Windows Phone

Re-post from here...

 

So about a year ago I changed my phone to a Windows mobile 7. Overall I’m really happy with it. Without setting the world alight with evangelistic wars, I think that if the Metro interface had come before the iPhone arrived then it’s likely it would be the dominant UI.

Anyway, one major problem I had arrived when it came to playing audiobooks. The issue was that I had three challenges when playing on the windows phone.

a) For space considerations, I wanted “read” chapters to be removed from the phone automatically

b) Some books have hundreds of chapters, but some have 12 chapters each >1hr in length. It was almost impossible to pick up where you left off. Therefore I needed to use the podcast “resume” functionality in Zune

c) This led to problem #3 – when I converted the “genre” using MP3Tagedit to podcast, they would frequently end up in a jumbled play-order depending on their date, so I needed to then ensure that Chapter1 had an earlier timestamp than chapter 2 etc. to force the right order in WP7. I could eventually get what I wanted but it was very painful and involved a bunch of manual steps as well as using BulkEditor and MP3Tagedit.

I found part of the answer in an excellent post over here, where it referenced TAGLIB# . So I launched powershell for the first time and wrote the script below.

Now, if you target it at a folder such as…

William Shakespeare/

Merchant of Venice/

Chapter1.mp3 ……

Hamlet/

Chapter1.mp3…..

It will loop through the subdirectories… renaming the files in sequence… setting the appropriate MP3 tag entries… Author = William Shakespeare/Album = BookDirectoryName/Chapter = Title.. and setting timestamp to force Zune to recognise chapter 1 as being before chapter 2..

So, here’s the code I used:

Function Zune
{
	[CmdletBinding()]
	param(
		[Parameter(Position=0, Mandatory=$True)]
		[ValidateNotNullOrEmpty()]
		[System.String]
		$Library,
 
		[Parameter(Position=1)]
		[ValidateNotNull()]
		[System.String]
		$Genre
	)
 
	cls
	
	$OriginDir = Get-Location
	
	#========Does the libary exist and contain books?=============================================
 
	if ((Test-Path -Path $Library) -eq $false) {
		write-output "$Library Does Not Exist"
		Break
	}

	$Books = Get-ChildItem $Library | where {$_.psiscontainer}
	if (!$books) {
		Write-Output "$Library Contains no books"
		Break
	}

	$librarylocation = Get-Item $Library
 
	fault Parameters===========================================================
	if (!$Genre)
	{
		$Genre="Podcast"
		Write-Host "Genre is blank, using Default $Genre" -foregroundcolor DarkGreen
	}
	else
	{
		Write-Output "Genre is " + $Genre
	}
	
	#========Load Assemblies======================================================================
	$TaglibLocation = (Resolve-path ($ProfileDir + "\taglib\taglib-sharp.dll"))
	Add-Type -Path ($TaglibLocation)
	Write-output "Taglib Loaded Successfully"
 
	#========Set up Recursive Subdirectories======================================================
	Write-Output "Processing the Library $Library"
	$NumberBooks = $books | Measure-Object | Select-Object -ExpandProperty Count
	Write-Output "Total Number of Runs is $Numberbooks"
 
	if ($NumberBooks -gt 0)
	{
		Set-Location $Librarylocation
		foreach ($book in $Books)
		{
			Write-Host "==================================================" -foregroundcolor Blue
			Write-Output "$("Processing the Book ")$($book.name)$(" in ")$($librarylocation)"
			$Chapters = Get-ChildItem $book.name *.mp3 | Sort-Object Name
			$NumberChapters = $Chapters | Measure-Object | Select-Object -ExpandProperty Count
			
			if ($NumberChapters -gt 0)
			{
 
				if ($NumberChapters -le 100) 
				{
					$pad = 2
				}
				elseif ($NumberChapters -le 255) 
				{
					$pad = 3
				}
				elseif ($NumberChapters -ge 256) 
				{
					Write-Output "$("Too Many chapters in the book :")$($book.name)"
					Break
				}

				Write-Output "$("Processing ")$($NumberChapters)$(" Chapters")"
				
				#========Set up Recursive Subdirectories==========================================
				$book.fullname
				set-location $book.fullname
				$i=1
				foreach ($chapter in $Chapters)
				{
					$title = "{0:D$pad}" -f ($i) +"$(" of $NumberChapters- ")$($book.name)"
					$Filename = $title + '.mp3'
					$chapter.fullname
					$media = [TagLib.File]::Create($chapter.Fullname)
					$media.Tag.Title = $title
					$media.Tag.Performers = "Narrator"
					$media.Tag.AlbumArtists = (get-culture).Textinfo.Totitlecase($Library.tolower())
					$media.Tag.Composers = "Composer"
					$media.Tag.Album = $book.name
					$media.Tag.Comment = $chapter.Name
					$media.Tag.Genres = $Genre
		
					if (!$media.Tag.Year) {$media.tag.year = (Get-date).Year}
					$media.Tag.Track = $i
					$media.Tag.Artists = (get-culture).Textinfo.Totitlecase($Library.tolower())
					
					$media.Save()
 
					$chapter.Creationtime = (Get-Date).date.addminutes($i)
					$chapter.LastWriteTime = (Get-Date).date.addminutes($i)
					#$chapter.LastAccessTime = (Get-Date).date.addminutes($i)
					$chapter.LastWriteTimeUTC = (Get-Date).date.addminutes($i)
					$chapter.CreationtimeUTC = (Get-Date).date.addminutes($i)
					#$chapter.LastAccessTimeUTC = (Get-Date).date.addminutes($i)
					Write-Output "$("Renaming ")$($chapter.name)$(" to ")$($Filename)"
					Rename-Item $chapter.fullname $filename
 
					$i++
				}
			}
			else
			{
				Write-Output "$("No Chapters inside the book :")$($book)"
			}
		
		Set-Location $Librarylocation
		}

		#===============================================================================================
		#========================     end of loop on books =============================================
		#===============================================================================================
	}
	else
	{
		Write-Output "$($"No Books inside the Library "$($Library))"
	}
 
	#===================================================================================================
	#========================     end of function     ==================================================
	#===================================================================================================
	Set-Location $OriginDir
}

Export-ModuleMember -Function Zune
Oct
14
2011

Simple PowerShell script to compress SQL Bacup files

# Handle BAK and TRN files
get-childitem -recurse |
where { $_.extension -match ".(bak|trn)" -and -not (test-path ($_.fullname -replace "(bak|trn)", "7z")) } |
foreach { &7za a -t7z -mx=9 ($_.fullname -replace "bak", "7z") $_.fullname }

# Delete any BAK or TRN that has a matching 7z file
get-childitem -recurse |
where { $_.extension -match ".(bak|trn)" -and
(test-path ($_.fullname -replace "(bak|trn)", "7z")) } |
foreach { del $_.fullname }

Dec
15
2009

Installing Office Web Apps 2010 Beta 2

I just deployed the Office Web Applications 2010 Beta. I followed the deployment document Microsoft has provided: http://www.microsoft.com/downloads/details.aspx?familyid=4AC8442D-0974-4902-84FE-1ADE382AB2A1&displaylang=en, but I ran into an error in the Activate the Office Web Apps services and feature by using Windows PowerShell chapter.

This is what the document says in the Create the service applications and the service application proxies paragraph:

After the service instances have been started, the service applications and the service application proxies which connect the Web front-ends to the service applications must be created. Create the service applications and the service application proxies by running the following script:

$appPool = Get-SPIisWebServiceApplicationPool –Name “SharePoint Web Services Default”
New-SPWordViewingServiceApplication –Name “WdView” –AppPool $appPool |
New-SPWordViewingServiceApplicationProxy –Name “WdProxy”
New-SPPowerPointServiceApplication –Name “PPT” –AppPool $appPool |
New-SPPowerPointServiceApplicationProxy –Name “PPTProxy”
New-SPExcelServiceApplication –Name “Excel”
-SPIisWebApplicationPool $appPool |

There are three errors in this script:

  1. The -Name parameter is invalid for the Get-SPIisWebServiceApplicationPool command. This should be -Identity.
  2. The -AppPool parameter is invalid for the New-SPWordViewingServiceApplication, New-SPPowerPointServiceApplication and New-SPExcelServiceApplication commands. This should be –ApplicationPool.
  3. The New-SPExcelServiceApplication is missing a command after the pipe (|).

I skipped the New-SPExcelServiceApplication command, cause my Excel Services Application Service was already created during the Farm Configuration Wizard. So the correct script is:

$appPoolName = "SharePoint Web Services Default"
New-SPWordViewingServiceApplication –Name "WdView" -ApplicationPool $appPoolName | New-SPWordViewingServiceApplicationProxy –Name "WdProxy"
New-SPPowerPointServiceApplication –Name "PPT" -ApplicationPool $appPoolName | New-SPPowerPointServiceApplicationProxy –Name "PPTProxy"
New-SPExcelServiceApplication –Name "Excel" -SPIisWebServiceApplicationPool $appPoolName

If you are looking for a single complete script that handles it all -- here you go

$machinesToActivate = @( gc env:computername )

$serviceInstanceNames = @("Word Viewing Service", "PowerPoint Service", "Excel Calculation Services")
foreach ($machine in $machinesToActivate) {
	foreach ($serviceInstance in $serviceInstanceNames) {
		$serviceID = $(Get-SPServiceInstance | where {$_.TypeName -match $serviceInstance} | where {$_.Server -match "SPServer Name="+$machine}).ID
		Start-SPServiceInstance -Identity $serviceID 
	} 
} 

$appPoolName = "SharePoint Web Services Default"
New-SPWordViewingServiceApplication –Name "WdView" -ApplicationPool $appPoolName | New-SPWordViewingServiceApplicationProxy –Name "WdProxy"
New-SPPowerPointServiceApplication –Name "PPT" -ApplicationPool $appPoolName | New-SPPowerPointServiceApplicationProxy –Name "PPTProxy"
New-SPExcelServiceApplication –Name "Excel" -SPIisWebServiceApplicationPool $appPoolName

$webAppsFeatureId = $(Get-SPFeature -limit all | where {$_.displayname -eq "OfficeWebApps"}).Id
Get-SPSite –limit ALL |foreach { Enable-SPFeature $webAppsFeatureId –url $_.URL } 

And for a bonus if you want to enable the new Developer Dashboard in SharePoint 2010, here is the powershell script to do it

$svc=[Microsoft.SharePoint.Administration.SPWebService]::ContentService

$ddsetting=$svc.DeveloperDashboardSettings

$ddsetting.DisplayLevel=[Microsoft.SharePoint.Administration.SPDeveloperDashboardLevel]::OnDemand

$ddsetting.Update()

And here is the STSADM command
stsadm -o setproperty -pn developer-dashboard -pv OnDemand

Some additional information can be found here