Monthly Archives: October 2014

NEW CodePlex project – SPUpgradeHelper

Today I am happy to announce https://spupgradehelper.codeplex.com/ is available for download.   This project aims to smooth the “double hop” upgrade from MOSS 2007 to SP2010 to SP 2013 by reading a CSV of database targets and automating the Cmdlets needed for a consistent, repeatable, high quality, and fast process.   Please leave a comment here or on CodePlex if you found this helpful.  Cheers!  shades_smile

 

Project Description

Migrating MOSS 2007 to SP 2013? This script takes a CSV of databases and runs upgrade Cmdlets in bulk (DB version/Mount/Dismount/Upgrade-SPSite/Claims auth)

Upgrading MOSS to SP2013 is a tedious process with many Cmdlets, especially if you have many databases. This script aims to help automate that process.

Given a CSV with SQL instance and content database names, this script offers Cmdlets to run upgrade steps across many databases all at once. No more TXT or XLS copy and paste madness. Simply populate the CSV, get familiar with UH* Cmdlets and upgrade with ease.

Key Features

  • Read CSV of databases
  • Load helper functions into memory
  • Enable admin to more easily run Cmdlets in bulk
  • Measure time duration for each step (# minutes)
  • Provide detailed LOG file of actions, result, and duration

Quick Start Guide

  • Extract “SPUpgradeHelper.ZIP” to any SharePoint machine in your farm
  • Run “SPUpgradeHelper.ps1” to load helper functions
  • Type in full path to your CSV file (i.e. “C:\TEMP\COLLAB.CSV”)

Function Names

  • UHCIaims – execute SPWebApplication function to upgrade Classic to Claims auth
  • UHCompatibiIity – execute Get-SPSite for “set” of databases to show GUI version (14/15)
  • UHDBVersion – execute TSQL for “set” of databases to show build number (12.0, 14.0, 15.0)
  • UHDismount – execute DisMount-SPContentDatabase for “set” of databases
  • UHMount – execute Mount-SPContentDatabase for “set” of databases
  • UHReadCSV – load CSV file into memory with upgrade “set”, SQL instance, and database names
  • UHUpgrade – execute Upgrade-SPSite for “set” of databases

NOTE – Upgrade “set” is meant for running parallel workstreams. For example, two servers with SP2010 and two servers with SP2013. That way overall upgrade can be expedited by running database set “A” through the first SP2010 and SP2013 server while database set “B” runs on the second server.

Microsoft Upgrade Process

WebAPI OData – 10 min video introduction (Visual Studio 2013)

I’ve been learning about OData and wanted to record a quick getting started video with how to create a new WebAPI project, add OData Controller, and send HTTP CRUD operations.  Below is an example using Adventure Works Departments with sample code, screenshots, and a 10 minute video introduction.   Please leave a comment if you found this helpful.  Thanks!  shades_smile

Video Content

  • Create MVC 4 web application
  • SQL Express – Adventure Works Departments
  • Add “AdventureWorks.edmx
    • Wizard driven
    • F4 set namespace
  • Add “DepartmentsController.cs
    • Create from EDMX class
    • Implements ODataController
    • Replace ID with KEY
  • Edit “WebApiConfig.cs
    • Comment out default route
    • Add OData route builder
  • Test with HTTP manually
  • Test with Chrome REST Postman
  • SQL Express – see changed data

Watch Video

WebAPI OData – 10 min video introduction (Visual Studio 2013) from Jeff Jones on Vimeo.

Sample Code

[WebApiConfig.cs]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Http.OData.Builder;
namespace MvcApplication1
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            //config.Routes.MapHttpRoute(
            //    name: "DefaultApi",
            //    routeTemplate: "api/{controller}/{id}",
            //    defaults: new { id = RouteParameter.Optional }
            //);
            ODataConventionModelBuilder build = new ODataConventionModelBuilder();
            build.EntitySet("Departments");
            config.Routes.MapODataRoute("odata", "odata", build.GetEdmModel());
            config.EnableQuerySupport(new QueryableAttribute()); 
            // Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable return type.
            // To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
            // For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
            //config.EnableQuerySupport();
            // To disable tracing in your application, please comment out or remove the following line of code
            // For more information, refer to: http://www.asp.net/web-api
            config.EnableSystemDiagnosticsTracing();
        }
    }
}

 

[DepartmentsController.cs]

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.OData;
namespace MvcApplication1.Controllers
{
    public class DepartmentsController : ODataController
    {
        private AdventureWorks2012Entities db = new AdventureWorks2012Entities();
        // GET api/Default1
        public IEnumerable GetDepartments()
        {
            return db.Departments.AsEnumerable();
        }
        // GET api/Default1/5
        public Department GetDepartment(short key)
        {
            Department department = db.Departments.Find(key);
            if (department == null)
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
            }
            return department;
        }
        // PUT api/Default1/5
        public HttpResponseMessage PutDepartment(short key, Department department)
        {
            if (!ModelState.IsValid)
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }
            if (key != department.DepartmentID)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest);
            }
            db.Entry(department).State = EntityState.Modified;
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
            }
            return Request.CreateResponse(HttpStatusCode.OK);
        }
        // POST api/Default1
        public HttpResponseMessage PostDepartment(Department department)
        {
            if (ModelState.IsValid)
            {
                db.Departments.Add(department);
                db.SaveChanges();
                HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, department);
                response.Headers.Location = new Uri(Url.Link("odata", new { key = department.DepartmentID }));
                return response;
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }
        }
        // DELETE api/Default1/5
        public HttpResponseMessage DeleteDepartment(short key)
        {
            Department department = db.Departments.Find(key);
            if (department == null)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }
            db.Departments.Remove(department);
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
            }
            return Request.CreateResponse(HttpStatusCode.OK, department);
        }
        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}

Downloads

Screenshots

image

image

image

image

image

PowerMap – 6 min video introduction (Excel 2013)

I heard a lot about PowerMap and wanted to record a quick getting started video with how to download, screenshots, and play a sample XLSX workbook.  

Background

The “Power” family of Business Intelligence features is getting a lot of attention at conferences, SharePoint Saturday, Channel 9, and social media.   Personally I hadn’t worked hands-on with the features before.   The idea is simple and practical – everyone uses Excel for data, so why not leverage that as the BI design surface?

Video Content

  • PowerMap screenshots
  • Download PowerMap EXE for Excel 2013
  • Download sample XLS PowerMap workbooks
  • How to play the Chicago sample XLSX

If you found it helpful please leave a comment below.  Enjoy!  shades_smile

Downloads

 

Watch Video

PowerMap – 6 min video introduction (Excel 2013) from Jeff Jones on Vimeo.

Screenshots

image

image

image

image

image

image

image

image

Colorize PowerShell Transcript with CSS

I use Start-Transcript and Stop-Transcript on nearly all PS1 files I create.   It’s a great way to troubleshoot and easily make detailed log files.   PowerShell gives us Write-Host -ForegroundColor to make console output look snazzy with color.   However, TXT output is flat by comparison without color coding.

Below I have sample code for adding <STYLE> and <DIV> tags to PowerShell Write-Host.  This example uses Get-Process to highlight any process using more than 50MB RAM in yellow and 500MB RAM in red.

After renaming TXT to HTM, we can see with Internet Explorer highlights from Transcript logs after the script runs.  Cool beans!  Please leave a comment if you found this helpful.   shades_smile

 

Code

# functions
function Get-TranscriptFilePath {
	try {
		$externalHost = $host.gettype().getproperty("ExternalHost",[reflection.bindingflags]"nonpublic,instance").getvalue($host, @())
		$externalhost.gettype().getfield("transcriptFileName", "nonpublic,instance").getvalue($externalhost)
    } catch {
		Write-Warning "This host does not support transcription."
    }
}
# main script
Start-Transcript
$proc = Get-Process |? {$_.WS -gt 10MB}
foreach ($p in $proc) {
	$ws=$p.WS
	$pn=$p.ProcessName
	$c="White"
	if ($ws -gt 50MB) {$c="yellow"}
	if ($ws -gt 500MB) {$c="red"}
	$html="
$ws,$pn
" Write-Host $html -ForegroundColor $c } $tran = Get-TranscriptFilePath Stop-Transcript # format TXT transcript as HTM $filename = $tran.Replace(".txt",".htm") $header = "`n`n`n`n`n" $txt = Get-Content $tran $footer = "`n`n" "$header $txt $footer" | Out-File $filename # launch IE to view HTM start iexplore.exe $filename

 

 

Screenshots

image

color

How to Follow a site URL with PowerShell in SharePoint 2013

Background

While migrating from SharePoint 2010 to 2013 we needed to take MyLinks and replace with new Social Site Following.   Given a CSV with user names and site URLs I researched PowerShell options for programmatically making users follow sites.   Now we can pre-populate Follow Sites before launch.   The below code did the trick.  shades_smile

 

Code

Function AddSiteToUserFollow ($siteUrl, $userName) {
	# Load site and user profile service
	$site = Get-SPSite $siteUrl
	$serviceContext = Get-SPServiceContext($site)
	$profileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext)
	if ($profileManager.UserExists($userName)) {
		# Load user profile
		$userProfile = $profileManager.GetUserProfile($userName)
		
		# Load social following manager
		$followingManager = New-Object Microsoft.Office.Server.Social.SPSocialFollowingManager($userProfile, $serviceContext)
		
		# Prepare following object
		$actorInfo = New-Object Microsoft.Office.Server.Social.SPSocialActorInfo
		$actorInfo.ContentUri = $site.Url
		$actorInfo.AccountName = $userProfile.AccountName
		$actorInfo.ActorType = "Site"
		
		# Add to followed sites
		$followingManager.Follow($actorInfo)
	}
}
AddSiteToUserFollow "http://sp15" "demo\sptest1"

 

Screenshots

image

image

References

Support

If the target user does not yet have a MySite created you could see the below error.  The fix is simply to have them visit MySite – or – to upgrade their site collection from 2010 to 2013.

Exception calling “Follow” with “1” argument(s): “No personal site exists for the current user, and no further information is available. Internal type name: Microsoft.Office.Server.UserProfiles.SocialDataStoreException. Internal error code: 1.”

Return to Top ▲Return to Top ▲