Office 365 products you might not know about

The more I talk to people about how they are using their Office 365 subscription, the more I notice how people are missing out on quite a lot of value that is included as part of the service, sometimes this includes the obvious ones like Skype for Business and SharePoint but there are many others that people just don’t seem to know about. Here is a few of the ones I find most people know nothing about.

Microsoft Bookings
This is a great service for people on a Business Premium subscription (there is a way to enable this for E3 and E5 clients also) where you can activate a pubic bookings page for your clients. It hooks into your calendars to provide available booking times and also comes with a handy app where you can accept of decline bookings.

Additionally it allows you to group people with services and it also lets you set some pricing information.

To enable this for E3 and E5 customers first enable first release for all users in your admin center and then go to this link:

Microsoft Staff Hub
Staff hub is a time management system for businesses who have shift workers and deskless staff. Once again it has its own mobile app and allows managers to assign shifts while allowing staff to swap them. Additionally it has some lite document management and collaboration features. It is available on the K1, E1, E2, E3, and E5 plans.

Microsoft Flow
Microsoft flow is used for automating business processes, it can do everything from sending you a phone notification when someone updates a file, to saving attachments automatically to cloud storage and much, much more.

The best feature of Microsoft Flow is it has a free plan and you don’t even need Office 365, in fact you can use it for automating processes that have nothing to do with Office 365 at all. For example you could send twitter post to a Google sheet or post a twitter update each time you add a photo to instagram.

Thanks for reading.

PowerShell ISE + TFS

For a while now I have been using a combination of ISE and Visual Studio to maintain my powershell scripts. Why? because I wanted to be able to have proper version control for my team and also have the features supplied by ISE.

Enter Visual Studio Team Foundation Server 2013 Update 2 Power Tools. They come with a PSSnapin to maintain files on TFS. The Scripting Guys have a great detailed blog on TFS and the PowerShell SnapIn here.

Please note that 2013 is now available and the process is very much the same. In brief, you can signup for a free TFS service via Visual Studio Online. Then Install the TFS 2013 Client. Configure the client. Install Power Tools for TFS 2013.

Now you have got TFS, ISE and a bunch of cmdlets, you might be wondering, is this really going to be easier than managing it via the VS gui? Well with a few customizations to my ISE profile and it will be.

After installing the TFS Power Tools, add the following to your ISE profile. (Just modify the $Global:WorkspacePath Variable to suit your environment).

This will make sure you have the latest files from TFS each time you load ISE, and will add a menu under add-ons where you can check-out the current file, and check-in all pending files.

Stay posted as I will publish a standalone module for easier installation soon.

$Global:WorkspacePath = 'Path/to/VS/Workspace'
Add-Type -AssemblyName System.Windows.Forms
Add-PSSnapin -Name Microsoft.TeamFoundation.PowerShell

function Checkin-Files
    Add-TfsPendingChange -Add -Item $Global:WorkspacePath -Recurse
    $pendingFiles = Get-TfsPendingChange -Item $Global:WorkspacePath -Recurse
    if ($pendingFiles -ne $null)
        $TFSCheck = [System.Windows.Forms.MessageBox]::Show(($pendingFiles | Select-Object -ExpandProperty localitem),'Do you want to check in these files?', 'TFS Check in','YesNo')
        if ($TFSCheck -eq 'Yes') 
            $TFSResult = New-TfsChangeset -Item $Global:WorkspacePath -Recurse 
            if ($TFSResult -ne $null){
            $null = [System.Windows.Forms.MessageBox]::Show(('Change Set ID: ' + $TFSResult.ChangesetID + "`r`nOwner: " + $TFSResult.Owner + "`r`nCreation Date: " + $TFSResult.CreationDate), 'TFS Check in')
            {$null = [System.Windows.Forms.MessageBox]::Show('No changes to check in!', 'TFS Check in')}
        $null = [System.Windows.Forms.MessageBox]::Show('No files to be checked in!', 'TFS Check in')

Write-Host -Object 'Updating TFS Workspace' -ForegroundColor Green
Update-TfsWorkspace -Item 'C:\Users\Peter\Documents\Visual Studio 2013\Workspace - Techpath\TechPath Base Class Library\TPBCL' -Recurse
New-Alias -Name 'cf' -Value Checkin-Files
$tfsRoot = $psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('Team Foundation Server',$null,$null) 
$tfsRoot.Submenus.Add('Check out current file',  
        $TFSResult = Add-TfsPendingChange -Edit -Item $psISE.CurrentFile.FullPath | Select-Object -Property Version, ChangeType, ServerItem
        if ($result -ne $null)
            $null = [System.Windows.Forms.MessageBox]::Show(('Version: ' + $TFSResult.Version + "`r`nChangeType: " + $TFSResult.ChangeType + "`r`nFile: " + $TFSResult.ServerItem), 'TFS File Checkout')
            $null = [System.Windows.Forms.MessageBox]::Show('File could not be checked out', 'TFS File Checkout')
, 'Ctrl+Alt+E') | Out-Null
$tfsRoot.SubMenus.Add('Check in Pending Changes',  
, 'Ctrl+Alt+S') | Out-Null

PowerShell: Customising the pipeline inputs

Sometime when using cmdlets there might be cases where you would assume two cmdlets would work together naturally and they just don’t. A good example of this is anything that takes -ComputerName as an input parameter (E.g test-connection) and the Get-ADComputer command.

In this example if you run Get-ADComputer -filter * you get a list of all computers in the current domain, say you now want to run Test-Connection to check machines are online, you might think you could just go:
Get-ADComputer -filter * | test-connection
Unfortunately you will be greeted with a wall of red text, this is because Get-ADComputer does not output an object with a ComputerName property, instead it has either Name or DNSHostName. You might now decide to output it all to a text file and ping them manually but your using PowerShell and there are easy ways do do this on the fly.

Method 1: Create a custom property on the ADComputer object with the name “ComputerName” so the next command receives what it expects:

Get-ADComputer -Filter * | 
   select @{n="ComputerName"; e={$_.DNSHostName}} | 

Method 2: Specify the name of the ADComputer object’s property in the next cmdlets input:

Get-ADComputer -Filter * | Test-Connection -ComputerName {$_.DNSHostName}

Using either of the above methods works with any cmdlet that accepts ComputerName as a pipeline input parameter.

Holy Trinity of PowerShell

The three most important cmdlets to remember in PowerShell, in fact the old ones you really need to remember are: Get-Command, Get-Help and Get-Member

Get-Command will give you a list of all installed cmdlets in PowerShell as well as their type, version and source. Additionally it has parameters for -verb or -noun so you can get a full list of all “Get-” cmdlets by typing: get-command -verb get or a list of all cmdlets that deal with services by typing: get-command -noun service

Get-Help is used once you know the command you are after, but you want to know more about how it works or what inputs or output to expect. You can provide some handy parameters to the get-help cmdlet such as -full to show the full help with examples, -showwindow to show the full help output in a searchable windows forms interface or -online to show the full detailed help on the technet website.

Get-Member is by far the cmdlet that I use the most, it gives you any methods and properties the current object in the pipeline has as well as specifying the type of the object your dealing with. This includes any properties that may not be visible by just outputting the object to the screen.

A notable gotcha for this is that if your passing get-member an array of strings (or any collection of any objects) it will give you the properties and methods of a string not an array. To determine if you are dealing with an array use the .gettype() .NET method.

PS C:\> $a = "Hello World"
PS C:\> $b = "hello", "world" 

PS C:\> $a | Get-Member
TypeName: System.String ..

PS C:\> $b | Get-Member
TypeName: System.String ...

PS C:\> $a.GetType() | select name, basetype
Name        BaseType 
String      System.Object

PS C:\> $b.GetType() | select name, basetype
Name        BaseType 
Object[]    System.Array