Keeping Script Files in sync


A lot of what I do is around PowerShell Scripting. Yes I’m a SharePoint guy, but I do a LOT of PowerShell. In the past, I had issues keeping copies of my scripts in sync when I would work on one on my host machine, and then copy it to one of my SharePoint 2010 VMs and make some changes, then copy back, etc. It was painful and I knew there was a better way, I just didn’t take the time to setup a better solution.

I now have a new laptop, which I might add is awesome. Without going on too much of a tangent about the machine itself, I will say it’s an HP EliteBook 8570W, i7 with 32GB of RAM and a 750GB HDD. It does pretty well for SharePoint testing and development. On this box though, it’s even more possible to have multiple VMs running at once. Since i was building this from scratch on top of Windows 8 Professional + Hyper-V, I decided to leverage SkyDrive to make keeping my script files in sync easier and much less burdensome.

Here’s what I have done, it’s really quite simple:

  1. Signed up for a SkyDrive Account at www.skydrive.com
  2. Setup the Desktop App on my Windows 8 Host, it syncs a bunch of stuff – but the main folder we’re talking about is called “PowerShell Scripts”
  3. Stored all of my PowerShell Scripts in the SkyDrive folder instead of a local folder. Note: It still has a local “offline” folder in C:\Users\Username\SkyDrive
  4. Setup the Desktop App on my Virtual Machines to sync JUST the PowerShell Scripts folder.

Once I have this setup, I can be working on my SP2010 Enterprise VM and I have access to all of my scripts and XML, and if I make an update to one of them and save it – it’s almost instantaneously available to any other environment leveraging my SkyDrive account. I can then jump over to a SP2013 VM or my host, and have the same copy I was just working on.

I thought I should share this, as I think there are others out there who could benefit from this kind of approach – so far it’s working great for me.

RD

Creating a Windows Scheduled Task using PowerShell


I find myself creating Windows Scheduled Tasks very often to run a PowerShell Script on a scheduled basis, and while PowerShell V3 will provide Cmdlets to manage the Scheduling Engine – V2 does not. I typically just create the Scheduled Tasks manually, but that is becoming less desirable as I work on larger SharePoint engagements with multiple environments, each with lots of servers. After doing a little online research, I found a few posts which were pretty close – the one which got me 80% of the way was this great post, which ironically was created for the same reason as mine – warming up SharePoint.

I took the code from the post mentioned above and tweaked it for my purposes, and I ended up with a fairly reusable script which takes a few parameters and also uses Get-Credential to actually accept credentials without the need to type the password in plain-text.

The way this script is composed is to create a task on the current machine by default, but by providing the optional HostName parameter – the script can be run against remote machines. It also will schedule it to start running starting tomorrow at 5:00 AM – it will then run daily at 5:00 AM.

Here’s the script!

<#
.Synopsis
	This PowerShell Script is used to create a scheduled task on a specified machine.
.Description
	This PowerShell Script uses the Schedule.Service COM Object to create a Windows
    Scheduled Task on a specified machine. Using the parameters provided, you can 
    specify the name, hostname, program to launch and the location of the script or file you're
    opening. The person executing the script will have to provide credentials.
.Example
	C:\PS>.\Create-ScheduledTask.ps1 -Description 'SharePoint Warmup' -ScriptPath C:\Scripts\Start-SPWarmUp.ps1
	This example creates a scheduled task on the current machine to run a script at C:\Scripts\Start-SPWarmup.ps1
    daily at 5:00 AM. The script will run with highest privileges.
.Notes
	Name: .\Create-ScheduledTask.ps1
	Author: Ryan Dennis
	Last Edit: 7/02/2012
	Keywords: Create Scheduled Task
.Link
	http://www.sharepointryan.com, http://twitter.com/SharePointRyan
#>

[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)][System.String]$Description,
[Parameter(Mandatory=$false)][System.String]$HostName=$ENV:ComputerName,
[Parameter(Mandatory=$false)][System.String]$Program="PowerShell.exe",
[Parameter(Mandatory=$true)][System.String]$ScriptPath
)

# Date Variables #
$date = (Get-Date 05:00AM).AddDays(1)
$taskStartTime = $date | Get-Date -Format yyyy-MM-ddTHH:ss:ms

# Get the credentials #
$creds = Get-Credential
$UserName = $creds.UserName
$Password = $creds.GetNetworkCredential().Password

# Build the Argument based on Prefix, Suffix and ScriptPath parameter #
$argPrefix = '-command "& {'
$argSuffix = '}"'
$programArguments = $argPrefix+$ScriptPath+$argSuffix

$service = New-Object -ComObject "Schedule.Service"
$service.Connect($Hostname)
$rootFolder = $service.GetFolder("\")
$taskDefinition = $service.NewTask(0)
$regInfo = $taskDefinition.RegistrationInfo
$regInfo.Description = $Description
$regInfo.Author = $UserName
$settings = $taskDefinition.Settings
$settings.Enabled = $True
$settings.StartWhenAvailable = $True
$settings.Hidden = $False
$triggers = $taskDefinition.Triggers
$trigger = $triggers.Create(2)
#$startTime = "2006-05-02T22:00:00"
$trigger.StartBoundary = $taskStartTime
$trigger.DaysInterval = 1
$trigger.Id = "DailyTriggerId"
$trigger.Enabled = $True
$Action = $taskDefinition.Actions.Create(0)
$Action.Path = $Program
$Action.Arguments = $programArguments
$Principal = $taskDefinition.Principal
# Principal.RunLevel -- 0 is least privilege, 1 is highest privilege #
$Principal.RunLevel = 1
$rootFolder.RegisterTaskDefinition($Description, $taskDefinition, 6, $UserName, $Password, 1)

I’m now a Microsoft Virtual Technology Specialist (vTSP) in SharePoint!


It’s official, I’ve been nominated and accepted by Microsoft as a Virtual Technology Specialist (vTSP) in SharePoint! I’m extremely excited to be considered among the elite in the Microsoft Partner Community, and I look forward to learning more about the program and helping businesses find value using Microsoft SharePoint.

One of the first questions I had during this process was “What is a VTS?”

I’ve located this text, which explains the VTS Program:
The Microsoft Virtual Technology Specialist Program (VTSP) is a select group chosen from the elite in Microsoft’s partner community, whose focus is to augment Microsoft’s internal Technology Specialist team. Their primary role is to communicate the value of Microsoft Solutions to customers and to provide architectural guidance for Enterprise Integration solutions. The Microsoft VTSP program was designed to create a deeper relationship with Microsoft Partners, the Product Teams at Microsoft Corporate, and Regional Microsoft Offices, in order to provide highly skilled solution specialists to Microsoft customers. It is designed to enable a high performance team of partner-based resources to deliver pre-sale activities and resources to empower customers and help them meet their solution and integration needs.

Great, so now we know what the VTS program is about, but what’s also really cool is I now get access to information on the Microsoft corporate network such as knowledge bases, technical articles, training materials, and other resources. I also get guest access to Microsoft facilities, and priority for participating in Microsoft marketing events.

Overall this is a great opportunity for me as well as for ICC, who now has 3 Virtual Technology Specialists in the SharePoint Practice. I’m proud to be a part of this elite community, and I look forward to all that comes from it.

Ryan

Use Find/Replace in PowerShell to fix InfoPath Forms after moving to a new Site Collection


I know I know, that’s a really long title.

I found some pretty good PowerShell how-to’s which described how to use Get-ChildItem to iterate through a folder – checking each item for a string and then using -replace to fix the string.

However, my situation was slightly different as I wanted to walk through a SharePoint 2010 Form Library – fixing InfoPath forms which had been moved into a new site collection and thus did not open in the browser and would not save due to the old reference to the XSN.

I’m sure there may be better methods on moving XSNs and XMLs across site collections where maybe this wouldn’t be necessary, but if you’re reading this I’ll assume you don’t care.

In any case, what I ended up doing (after obviously re-publishing the XSN template to the new site collection so new IP forms wouold work) is:

  1. Copy all of the forms using Windows Explorer (we didn’t care about versions or modified by metadata, just the content type metadata) into the new Form Lib.
  2. Run the following PowerShell code to fix each XML file – replacing the link to the old XSN with a link to the new XSN (Note: I ran this on my desktop, NOT on the server as I didn’t need any SP cmdlets):
$files = Get-ChildItem \\sitecollection\davwwwroot\FormLib -Filter "*.xml"
foreach ($file in $files) {
(Get-Content $file.fullname) | ForEach-Object {$_ -replace "http://sitecollection/library/OLDXSNPATH.xsn", "http://newsitecollection/library/NEWXSNPATH.xsn"} | Set-Content $file.fullname
}

Voila, all IP forms are now fixed and will not only open in the browser as expected – but I can now update these forms in the new location as they now point to the correct XSN template.

If you’re a frequent reader you’re probably thinking “Wow, normally he gives us these nice functions with parameters, help, etc.”… You’re right, this time it was a quick and dirty chunk of code. However, chances are I’ll need to do this again – so I did actually write a function for future needs:

function Reset-InfoPathTemplateLink {
Param(
[string]$FilePath,
[string]$FileExtension,
[string]$OldPath,
[string]$NewPath
)
$files = Get-ChildItem $FilePath -Filter $FileExtension
	foreach ($file in $files) {
		(Get-Content $file.fullname) |
		ForEach-Object {$_ -replace $OldPath,$NewPath} |
		Set-Content $file.fullname
	} #end foreach
} #end function

To run the function, first dot-source it and then use the following syntax:

Reset-InfoPathTemplateLink -FilePath "\\sitecollection\subsite\formlib" -FileExtension "*.xml" -OldPath "http://sitecollection/doclib/template.xsn" -NewPath "http://newsitecollection/newdoclib/newtemplate.xsn"

As always, happy PowerShelling!

Making SharePoint 2010 PowerShell Scripts Backward-Compatible with 2007


Recently I received an e-mail from one of my ICC SharePoint team-mates asking for some PowerShell code to create and then delete a lot of list items. Like 20000. Well, I already had code to create the list items – so it was easy to add a single line to call the delete() method immediately after the item was added.

I provided the updated function to my teammate, only to find out later that they wanted to do this in MOSS 2007 – not SP2010. Oops! I hadn’t considered that, I assumed since they were asking me for PowerShell code that they were dabbling in a 2010 environment. Not the case. However, this particular person is one of our better SharePoint Developers and quickly modified the code to work in MOSS.

After having that discussion, it became clear to me that it would be REALLY cool to create PowerShell Scripts and Functions that would work both in V4 as well as V3. And the journey began..

After some experimentation, googling, trial & error – it became apparent that this isn’t rocket science. In SharePoint 2010 we use the following PowerShell cmdlet to add the SharePoint Snap-in:

Add-PSSnapIn Microsoft.SharePoint.PowerShell

There’s no equivalent for MOSS environments, but we can load the SharePoint assemblies by using the following line of code:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

Once we’ve done that, we can do other things such as get the farm build version and tell us what version we’re working with:

function Get-SPFarmBuildVersion {
# using the [microsoft.sharepoint.administration.spfarm] line to get the local farm regardless of version #
# this works in 2007 and 2010 #
$farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
$farmBuild = $farm.BuildVersion.ToString()

      if ($farmBuild.StartsWith("12")) {
      Write-Host "This is WSS or MOSS"
      } 
      
      elseif ($farmBuild.StartsWith("14")) {
      Write-Host "This is SP2010"
      }

}

How cool is that!?

Now that we know that simple tidbit, we can easily create our scripts and functions with backward compatibility in mind by doing something like if/elseif as seen above. Developers may chime in with a better way, I’m sure there are better – perhaps more elegant approaches to this same theory – but this works for me for now.

Anyhow, back to the story – my developer friend Aaron wanted to create list items in MOSS using PowerShell. Here’s the code to do that – and it’s backwards compatible!

function Add-MultipleListItems {
[CmdletBinding()]
Param(
[string]$ListName=(Read-Host "Please enter the name of the list you wish to add to."),
[string]$Amount=(Read-Host "Please enter a number of list items to create."),
[string]$Choices=(Read-Host "Please enter choices separated by semicolons, enclosed in double quotes."),
[string]$ListItem=(Read-Host "Please enter a string for the title of each list item (Example: Added by Powershell)"),
[string]$WebUrl=(Read-Host "Please enter a URL of a SharePoint site (Example: http://intranet)")
)

$choicesForCategory = $Choices -split ";"

$assemblies = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
$farmBuild = $farm.BuildVersion.ToString()
	
	if ($farmBuild.StartsWith("14")) {
		Start-SPAssignment -Global
		$mylist = (Get-SPWeb -identity $webUrl -AssignmentCollection $StartSpAssignment).Lists[$listName]
		Write-Host "Creating $amount list items in $listName" -ForegroundColor Green
		$i = 1
		do 
		{
		    $newItem = $mylist.Items.Add()
		    $newItem["Title"] = $listItem
		    $newItem["Category"] = $choicesForCategory | Get-Random
		    $newItem.Update()
		    $i++
		}
		while ($i -le $amount)

		Write-Host "Finished!" -ForegroundColor Green
		Stop-SPAssignment -Global
	}
	elseif ($farmBuild.StartsWith("12")) {
		$SPWeb = Get-SPWeb $webUrl
		$mylist = $SPWeb.Lists[$listName]
		Write-Host "Creating $amount list items in $listName" -ForegroundColor Green
		$i = 1
		do 
		{
		    $newItem = $mylist.Items.Add()
		    $newItem["Title"] = $listItem
		    $newItem["Category"] = $choicesForCategory | Get-Random
		    $newItem.Update()
		    $i++
		}
		while ($i -le $amount)
		$SPWeb.dispose()
		Write-Host "Finished!" -ForegroundColor Green
	}
}

If you read this line by line, you’ll notice that they are fundamentally identical. Really the only difference is in V3 we don’t get any cmdlets, so we don’t have Start-SPAssignment and Stop-SPAssignment. This just means we have to make sure we’re disciplined to use dispose() methods for any SPWeb and SPSite objects.

With all that in mind, my goal from here on out is to try and make any and all PowerShell code backwards compatible.

RD