Connect to Exchange Online with PowerShell

To connect to Exchange Online in Office 365 open a PowerShell prompt and simply run the following commands:

if ($cred -eq $null)
{
	$cred = Get-Credential
}

Write-Host "Connecting to Exchange Online"

$exchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $cred -Authentication Basic -AllowRedirection

Import-PSSession $exchangeSession | Out-Null

Write-Host "Connected to Exchange Online"

Find this script also on Git

Remove the Product Key in Office 2013/2016

Very simple. Just use ospp.vps that could be found in C:\Program Files\Microsoft Office\OfficeNN or C:\Program Files (x86)\Microsoft Office\OfficeNN.

See description in https://community.spiceworks.com/how_to/48973-remove-and-re-add-license-key-for-office-2013-on-office-365

Load Script with web relative URL

It is good practice to have often used functions in a separate file that could be loaded by any html- or JavaScript-file that needs the functions. I often create htm-files with html and Javascript and use the Content Editor Webpart on a webpart page or wiki page to use these small customizations. In SharePoint Online I was struggling with loading script files with a relative path like “../scripts/functionLib.js”, what should be fine, when my page is stored in the Site Pages library, like in this snippet:


<script src="../Scripts/functionLib.js"></script>

What happens? When having the file in a library in SharePoint Online, anything removes the two dots (“..”). The result: the script could not be found, the page does not behave as expected any more. The dots are not removed directly after I store the file in the library, it happens one or two days later.

So, I have to load the script file in any other way, but I still want to be able to use site or web relative paths. To solve this problem, the functions SP.SOD.registerSod and SP.SOD.executeFunc will do the trick. Here is a simple example for a script, where I additionally use the function SP.SOD.registerSodDep:


<div>
MyParameter:&nbsp;<span id="myParameter"></span>
</div>

<script type="text/javascript">

SP.SOD.registerSod('functionLib.js', _spPageContextInfo.webServerRelativeUrl + '/scripts/functionLib.js');
SP.SOD.registerSod('jquery.js', 'https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js');

SP.SOD.registerSodDep('functionLib.js', 'jquery.js');

SP.SOD.executeFunc('functionLib.js', null, function() {
	var myParam = getUrlParameter("myParam");

	$('#myParameter').html(myParam);
});

</script>

This example uses the webServerRelativeUrl member of _spPageContextInfo, where SharePoint stores the relative URL of the current web site. When the script is stored in the root site of the site collection, the member siteServerRelativeUrl could be used instead.

Disable Site Creation in OneDrive (aka. MySites)

From one of our customers I was asked, if it is possible to disable the creation of subsites in OneDrive. Hm. We should not change any permissions of the user in his OneDrive site. So, by his permissions, he will always be able to create new sites. But we can remove all the web templates from the site collection. He still is able to open the “Create new site” dialog, but he is not able to choose a template for his site.

All could be done by some PowerShell. I have split the scripts into three parts. The first one is DisableSiteCreation.ps1. This will do the changes for a given url.

param (
    [Parameter(Mandatory=$true)][string] $Url
)

Write-Host "$Url.Url..."

$web = Get-SPWeb $Url

if ($web.Provisioned -eq $false)
{
    Write-Host -ForegroundColor Yellow "The web at $Url is not provisioned. Skip the web"
}
else
{
    $col = New-Object System.Collections.ObjectModel.Collection[Microsoft.SharePoint.SPWebTemplate]

    $cultures = $web.SupportedUICultures
    foreach ($culture in $cultures)
    {
        $lcid = $culture.LCID
        # Write-Host $culture.LCID

        $w = Get-SPWeb $Url
        $w.SetAvailableWebTemplates($col, $lcid)
        $w.Update()

        Write-Host "Removed web templates in $Url for LCID $lcid"
    }
}

The second script is RunOnSiteCollections.ps1 that is used to call the first script. The path to the first script (“DisableSiteCreation.ps1”) is passed as a parameter to this script.

param (
    [Parameter(Mandatory=$true)][string] $WebApp,
    [Parameter(Mandatory=$true)][string] $Script,
    [string] $FilterTemplate,
    [string] $FilterConfiguration
)

$wa = Get-SPWebApplication $WebApp -ErrorAction SilentlyContinue

if ($wa -eq $null)
{
    Write-Host -ForegroundColor Red "There is no web application in url $WebApp"
}
else
{
    foreach ($site in $wa.Sites)
    {
        $web = $site.RootWeb

        $runOnWeb = $true

        if (($FilterTemplate -ne "") -and ($FilterConfiguration -ne ""))
        {
            if (($FilterTemplate.ToUpper() -ne $web.WebTemplate.ToUpper()) -or ($FilterConfiguration -ne $web.Configuration))
            {
                $runOnWeb = $false
            }
        }
        
        if ($runOnWeb -eq $true)
        {
            & $Script $web.Url
        }
    }
}

The last one DisableSiteCreationInMySites.ps1 is just a wrapper to call the functionality. This last script will get the url of the MySite host and will remove all the web templates from the site collections in this web application. Run this one to disable the site creation in OneDrive sites.

param (
    [Parameter(Mandatory=$true)][string] $WebApp
)

. .\RunOnSiteCollections.ps1 -WebApp $WebApp -Script .\DisableSiteCreation.ps1 -FilterTemplate SPSPERS -FilterConfiguration 10

To revert these changes, the following scripts could be used, whereby the script EnableSiteCreationInMySites.ps1 should be called.

The counterpart to the DisableSiteCreation.ps1 is EnableSiteCreation.ps1:

param (
    [Parameter(Mandatory=$true)][string] $Url
)

Write-Host "$Url.Url..."

$web = Get-SPWeb $Url

if ($web.Provisioned -eq $false)
{
    Write-Host -ForegroundColor Yellow "The web at $Url is not provisioned. Skip the web"
}
else
{
    $web.AllowAllWebTemplates()
    $web.Update()

    Write-Host "All web templates are allowed in $Url"
}

The counterpart to DisableSiteCreationInMySites.ps1 is EnableSiteCreationInMySiteHost.ps1:

param (
    [Parameter(Mandatory=$true)][string] $WebApp
)

. .\RunOnSiteCollections.ps1 -WebApp $WebApp -Script .\EnableSiteCreation.ps1 -FilterTemplate SPSPERS -FilterConfiguration 10

 

Create a TFS Backlog Item with PowerShell

Inspired by this blog post by the AIT GmbH, the following PowerShell script is created to simply create a new product backlog item in a team project in Team Foundation Server.

param (
	[string] $Url,          # the uri to the team project collection
	[string] $ProjectName,  # the name of the team project
	[string] $PBITitle,     # the title for the new product backlog item
	[string] $Area          # the area path, could be empty
)

#Load Reference Assemblies 
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client") 
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.WorkItemTracking.Client") 
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.ProjectManagement") 

$TeamProjectCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($Url) 
$TeamProjectCollection.EnsureAuthenticated()

# Service instances 

$css4 = 
   ($TeamProjectCollection.GetService([type]"Microsoft.TeamFoundation.Server.ICommonStructureService4")) -as 
     [Microsoft.TeamFoundation.Server.ICommonStructureService4] 

$teamService = 
   ($TeamProjectCollection.GetService([type]"Microsoft.TeamFoundation.Client.TfsTeamService")) -as 
     [Microsoft.TeamFoundation.Client.TfsTeamService] 

$teamConfigService = 
   ($TeamProjectCollection.GetService([type]"Microsoft.TeamFoundation.ProcessConfiguration.Client.TeamSettingsConfigurationService")) -as 
     [Microsoft.TeamFoundation.ProcessConfiguration.Client.TeamSettingsConfigurationService] 

$store = 
   ($TeamProjectCollection.GetService([type]"Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore")) -as 
     [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]

$project = $store.Projects[$ProjectName]

if ($project -eq $null)
{
	throw "The team project $ProjectName could not be found"
}


# Get the work item type

$WorkItemType = "Product Backlog Item"  # this will only work with projects with the Scrum template

$wit = $project.WorkItemTypes[$WorkItemType] 

if (!$wit) { 
        throw "The WorkItemType $WorkItemType could not be found in project $ProjectName" 
} 


# Create Product Backlog Item (PBI) 

Write-Host "Creating Product Backlog Item" 

$pbi = New-Object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem ($wit) 

$pbi.Title = $PBITitle 

# $pbi.Description = "Hello World."

if ($Area -ne "")
{
	$pbi.AreaPath = $Area
}

$pbi.Save()

Write-Host -ForegroundColor Green "Done."

Show Web Properties with JavaScript

Especially in SharePoint Online it could be difficult to make the content of the property bag for a site visible. With JavaScript in SharePoint 2013 or SharePoint Online this task could be done very easy. Just save the following script to a file and upload this file to the SiteAssets library in the SharePoint site. Create a new page in the SharePoint site and put a Content Editor webpart onto this page. Set the script url in the properties of the Content Editor webpart to the url to the uploaded script file. After saving and reloading the page click the button to display the web properties.

<style>
.webPropertyRow {
	padding-left: 4px;
	padding-right: 4px;
}
</style>

<script type="text/javascript" language="javascript">

	var clientContext = null;
	var web = null;
	var properties = null;

	function runCode() {
        this.clientContext = new SP.ClientContext.get_current();

        if (this.clientContext != undefined && this.clientContext != null) {
            this.properties = clientContext.get_web().get_allProperties();
			
			this.clientContext.load(this.properties);
			
			this.clientContext.executeQueryAsync(
				Function.createDelegate(this, this.gotWebProperties), 
				Function.createDelegate(this, this.onQueryFailed));
		}
	}
	
	function gotWebProperties() {
		document.getElementById("webProperties").innerHTML = "&nbsp;";
		
		var table = document.createElement("table");
		table.setAttribute("border", "1");
		
		var allProperties = this.properties.get_fieldValues();
		
		for (property in allProperties) {
			var row = document.createElement("tr");
			
			var cell1 = document.createElement("td");
			cell1.setAttribute("class", "webPropertyRow");
			
			cell1.innerHTML = property;
			
			var cell2 = document.createElement("td");
			cell2.setAttribute("class", "webPropertyRow");
			
			cell2.innerHTML = allProperties[property];
			
			row.appendChild(cell1);
			row.appendChild(cell2);
			
			table.appendChild(row);
		}
		
		document.getElementById("webProperties").appendChild(table);
	}
	
    function onQueryFailed(sender, args) {
        alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
    }

</script>

<input id="Button1" type="button" value="Show Web Properties" onclick="runCode()" />
	
<br />
<div style="margin-top:20px;">&nbsp;</div>

<div id="webProperties"></div>

SharePoint – Add Navigation with JavaScript

With a short JavaScript it is possible to add navigation nodes in a SharePoint site (quicklaunch and top navigation bar). This is useful, when an internal link should be added. Adding internal links is not possible using the user interface in the browser in SharePoint. In the following example we define the new nodes in an array at the beginning. The code block takes this array and sets the navigation. It makes sense to remove the existing entries at the beginning. An example for removing could be found here.

<script type="text/javascript" language="javascript">

    var navArray = [
	{ title:"Home", url:"/sites/gms-portal", isExternal:false },
	{ title:"Microsoft", url:"http://www.microsoft.com", isExternal:true },
	{ title:"Google", url:"http://www.google.com", isExternal:true },
	{ title:"Bing", url:"http://www.bing.com", isExternal:true }
    ];

    function checkArray() {
		for (var i = 0; i < navArray.length; i++) {
			var navObject = navArray[i];
			
			alert(navObject.title);
		}
	}
	
    var navigationNodeCollection = null;
    var nnci = null;
	
	function addNavigationNodes() {
        var clientContext = new SP.ClientContext.get_current();

        if (clientContext != undefined && clientContext != null) {
            var web = clientContext.get_web();

            // Get the Quick Launch navigation node collection.
            // this.quickLaunchNodeCollection = web.get_navigation().get_quickLaunch();
            
            // Get the Top Navigation navigation node collection.
			this.navigationNodeCollection = web.get_navigation().get_topNavigationBar();

			for (var i = 0; i < navArray.length; i++) {
				var navObject = navArray[i];
				var navTitle = navObject.title;
				var navUrl = navObject.url;
				var navIsExternal = navObject.isExternal;

				// Set properties for a new navigation node.
				this.nnci = new SP.NavigationNodeCreationInformation();
				nnci.set_title(navTitle);
				nnci.set_url(navUrl);
				nnci.set_isExternal(navIsExternal);
            
				// Create node as the last node in the collection.
				nnci.set_asLastNode(true);
				this.navigationNodeCollection.add(nnci);
			}

			clientContext.load(this.navigationNodeCollection);
			clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
		}
	}
	
    function runCode() {
		addNavigationNodes();
    }

    function onQuerySucceeded() {
        alert("Nodes are added to the navigation.");
    }

    function onQueryFailed(sender, args) {
        alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
    }

</script>

<input id="Button1" type="button" value="Set Navigation" onclick="runCode()" />
	
<br />
<div style="marginTop:20px;">&nbsp;</div>

<input id="Button2" type="button" value="Check Navigation Array" onclick="checkArray()" />

Take the script and save it as a file. Copy this file to the SiteAssets library in your SharePoint site. Create a new page and add a Content Editor webpart on the page. In the properties of the webpart enter the complete url to the script file you saved in the SiteAssets library.