Microsoft SharePoint (Unreleased) for Android.

Some of us can't stand iOS but there's no denying it's the primary mobile platform and it has a SharePoint. Microsoft's just released an early access version of the Andoird app on the Play Store.
The SharePoint app includes all the things you'd expect to find - managing team sites, document storage, Office 365 integration, and collaboration features with other team members. Documents can be edited with the Office Mobile apps (Word, Excel, PowerPoint, and OneNote), making for easy updating on your Android phone or tablet. The app is powered by Microsoft Graph, which "makes it faster to get to content and people you work with".
 Microsoft SharePoint (Unreleased)- screenshot thumbnail   Microsoft SharePoint (Unreleased)- screenshot thumbnail   Microsoft SharePoint (Unreleased)- screenshot thumbnail   Microsoft SharePoint (Unreleased)- screenshot thumbnail   Microsoft SharePoint (Unreleased)- screenshot thumbnail   Microsoft SharePoint (Unreleased)- screenshot thumbnail


PowerShell function to create Azure ARM VM with Public IP

This function is intended to reside within the logic of a larger script for deploying an entire resource group. The function should ideally be fed from a JSON or XML configuration file.
It takes a whole bunch of inputs relating to Azure VMs and provisions a VM within a resource group and VNet with a public IP. The Network Security Group settings need to be amended as required.
I've extracted some of the required variables, but these could also be passed as parameters.

[string]$resGroup = "IIOS" [string]$location = "australiaeast" [int]$script:ipStart = 4 [string]$saName = $($saName -creplace '[^a-zA-Z0-9]','').ToLower() [string]$vnName = $($resGroup +"-vNet1") function Create_VMRole([string]$vmName, [string]$vmSize, [string]$vmDesc, [int]$dataDiskSize, [string]$PublisherName, [string]$Offer, [string]$Skus) { while(($vmName.length -gt 15) -or !$vmName){ Write-Host "Virtual Machine Name '$vmName' is too long or empty" "Yellow" [string]$vmName = read-host "Please enter a valid Virtual Machine Name..." Write-Host "`nYou entered '$vmName'`n" "Yellow" } Write-Host "[CREATING] $vmDesc Virtual Machine '$vmName'" $vmAvailabilitySet = $($vmName +"AvailabilitySet") if(!($vmSet = Get-AzureRMAvailabilitySet -Name $vmAvailabilitySet -ResourceGroupName $resGroup)) { Write-Host "[CREATING] AvailabilitySet '$vmAvailabilitySet' for $vmDesc Virtual Machine '$vmName'" $vmSet = New-AzureRMAvailabilitySet -Name $vmAvailabilitySet -ResourceGroupName $resGroup -Location $location } $vnet = Get-AzureRMVirtualNetwork -Name $vnName -ResourceGroupName $resGroup Write-Host "[CREATING] PublicIpAddress for '$vmName'" $pip = New-AzureRMPublicIpAddress -Name $($vmName +"-PublicIP1") -ResourceGroupName $resGroup -Location $location -AllocationMethod Dynamic $nicIP = $("10.0.0." + $script:ipStart++) Write-Host "[CREATING] NetworkInterface with PrivateIP '$nicIP' for '$vmName'" $nic = New-AzureRMNetworkInterface -Name $($vmName +"-NIC1") -ResourceGroupName $resGroup -Location $location -SubnetId $vnet.Subnets[0].Id -PublicIpAddressId $pip.Id -PrivateIpAddress $nicIP $vm = New-AzureRMVMConfig -VMName $vmName -VMSize $vmSize -AvailabilitySetId $vmSet.Id $storageAcc = Get-AzureRMStorageAccount -ResourceGroupName $resGroup -Name $saName if($dataDiskSize -gt 0) { $vhdURI = $($storageAcc.PrimaryEndpoints.Blob.ToString() + "vhds/" + $vnName +"-"+ $vmName +"-Data1.vhd") Add-AzureRMVMDataDisk -VM $vm -Name "Data1" -DiskSizeInGB $dataDiskSize -VhdUri $vhdURI -CreateOption empty } $vm = Set-AzureRMVMOperatingSystem -VM $vm -Windows -ComputerName $vmName -Credential $vmCred -ProvisionVMAgent -EnableAutoUpdate #-TimeZone $TimeZone $vm = Set-AzureRMVMSourceImage -VM $vm -PublisherName $PublisherName -Offer $Offer -Skus $Skus -Version "latest" $vm = Add-AzureRMVMNetworkInterface -VM $vm -Id $nic.Id $osDiskUri = $($storageAcc.PrimaryEndpoints.Blob.ToString() + "vhds/" + $vnName +"-"+ $vmName +"-System.vhd") $vm = Set-AzureRMVMOSDisk -VM $vm -Name "System" -VhdUri $osDiskUri -CreateOption fromImage $newVM = New-AzureRMVM -ResourceGroupName $resGroup -Location $location -VM $vm return $newVM } Example usage: Create_VMRole "dbVM1" "Standard_D3_V2" "SQL Server Primary" 300 "MicrosoftSQLServer" "SQL2014SP1-WS2012R2" "Standard"


PowerShell function to Get Term from SharePoint Term Store

Very useful if you're updating taxonomy fields.
Takes 3 inputs; the Site URL, the Taxonomy field object and the Term string.
The function gets a reference to the site's term store and then executes the GetTerm method, (more info here: TermSet.GetTerms) and finally return a Term object.

function GetTerm([string]$siteUrl, [Microsoft.SharePoint.Taxonomy.TaxonomyField]$oField, [string]$termStr) { if(!$oField.IsTermSetValid) { Write-Host "[ERROR] $($oField.Title) IsTermSetValid is FALSE" return $null; } $taxonomySession = Get-SPTaxonomySession -Site $siteUrl; if(($taxonomySession -eq $null) -or ($taxonomySession -eq $null)) { Write-Host "[ERROR] Taxonomy Session is null, Metadata Service App Proxy default storage location for column specific term sets is CHECKED and the user account has access" return $null } $oTermStore = $taxonomySession.DefaultSiteCollectionTermStore; if($oTermStore -eq $null) { $oTermStore = $taxonomySession.TermStores[0]; } if($oTermStore -eq $null) { Write-Host "[ERROR] Unable to get a valid Term Store.`nEnsure $(whoami) is a Term Store administrator.`nEnsure the Managed Metadata Service Proxy property 'This service application is the default storage location for column specific term sets.' is checked." Red exit } $oTermSet = $oTermStore.GetTermSet($oField.TermSetId); [System.Guid]$gTermId = $oField.AnchorId; [int]$LCID = 1033; if(($gTermId.ToString() -eq "00000000-0000-0000-0000-000000000000") -or ($gTermId -eq $null)) { $oTerm = $oTermSet.GetTerms($termStr, $LCID, $true)[0]; if($oTerm -eq $null) { Write-Host "[ERROR] Term value: $termStr not found in TermSet $($oTermSet.Name)" Red } } else { $oAnchorTerm = $oTermSet.GetTerm($gTermId); $oTerm = $oAnchorTerm.Terms[$termStr]; if($oTerm -eq $null) { Write-Host "[ERROR] Term value: $termStr not found in $($oTermSet.Name);$($oAnchorTerm.GetPath())" Red } } return $oTerm }
Below is an example of how to use the function and the returned Term object to set the default value for a field.

$oField = $oWeb.Fields.GetField("Document Type") $oTerm = GetTerm $oWeb.Site.Url $oField "Proposal" if($oTerm -ne $null) { $taxonomyValue = New-Object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue($oField); $taxonomyValue.TermGuid = $oTerm.Id.ToString(); $taxonomyValue.Label = $oTerm.Name; $oField.DefaultValue = $taxonomyValue.ValidatedString; $oField.PushChangesToLists = $true $oField.Update($true) Write-Host "Setting Default Value $($oField.TypeAsString):$($oField.Title) : '$($taxonomyValue.ValidatedString)'" Yellow }


PowerShell function to Copy the properties of Document Set, including Welcome Page and Allowed Content Types

While ostensibly simple the process to copy allowed content types and welcome page properties took a while to figure out.
The function takes 2 input; the source Content Type (CT) for the Document Set (DS), and the target CT for the DS. It assumes both have been created and referenced.
It then gets a reference to the source DS Template, and loops through its properties to copy them to the target CT.
There are a couple of 2 second sleeps in there bcos in the environment where I was using it sometimes the re-referencing wouldn't work without them.

function Copy_DocSet_Properties([Microsoft.SharePoint.SPContentType]$oTargetDSCT, [Microsoft.SharePoint.SPContentType]$oSourceDSCT) { Write-Host "`t Setting '$($oTargetDSCT.Name)' Welcome page properties" if($oSourceDSCT -ne $null) { #https://msdn.microsoft.com/en-us/library/microsoft.office.documentmanagement.documentsets(v=office.14).aspx # AllowedContentTypes Property Microsoft.Office.DocumentManagement.DocumentSets.AllowedContentTypeCollection AllowedContentTypes {get;} # DefaultDocuments Property Microsoft.Office.DocumentManagement.DocumentSets.DefaultDocumentCollection DefaultDocuments {get;} # Equals Method bool Equals(System.Object obj) # GetHashCode Method int GetHashCode() # GetType Method type GetType() # SharedFields Property Microsoft.Office.DocumentManagement.DocumentSets.SharedFieldCollection SharedFields {get;} # ToString Method string ToString() # Update Method void Update(bool bPushDown) # WelcomePageFields Property Microsoft.Office.DocumentManagement.DocumentSets.WelcomePageFieldCollection WelcomePageFields {get;} # WelcomePageView Property Microsoft.SharePoint.SPView WelcomePageView {get;set;} $oSourceDSCTT = [Microsoft.Office.DocumentManagement.DocumentSets.DocumentSetTemplate]::GetDocumentSetTemplate($oSourceDSCT) $oTargetDSCT1 = $oTargetDSCT.ParentWeb.ContentTypes[$oTargetDSCT.Id] $oNewDST = [Microsoft.Office.DocumentManagement.DocumentSets.DocumentSetTemplate]::GetDocumentSetTemplate($oTargetDSCT1) $oNewDST.AllowedContentTypes.Clear() foreach($ctId in $oSourceDSCTT.AllowedContentTypes.GetEnumerator()) { $DSACT = $oTargetDSCT.ParentWeb.ContentTypes[$ctId] if(!$DSACT) { $DSACT = Create_CTFS $oTargetDSCT.ParentWeb $($oWebSource.ContentTypes[$ctId].Name) } if($DSACT) { $oNewDST.AllowedContentTypes.Add($DSACT.Id) Write-Host "`t Added Allowed Content Type '$($DSACT.Name)'" } else { Write-Host "[ERROR] Content Type '$($oWebSource.ContentTypes[$ctId].Name) ($ctId)' doesn't exist, so it can't be added to the Document Set" "red" } } $oNewDST.Update($false) sleep 2 $oTargetDSCT1 = $oTargetDSCT.ParentWeb.ContentTypes[$oTargetDSCT.Id] $oNewDST = [Microsoft.Office.DocumentManagement.DocumentSets.DocumentSetTemplate]::GetDocumentSetTemplate($oTargetDSCT1) foreach($oSFld in $oSourceDSCTT.WelcomePageFields.GetEnumerator()) { $oField = $oTargetDSCT1.Fields[$oSFld.Title] if($oField) { $oNewDST.WelcomePageFields.Add($oField) Write-Host "`t Added Welcome Page Field '$($oField.Title)'" } else { Write-Host "[ERROR] Unable to get reference to Source Field '$($oSFld.Title)' in content type $($oTargetDSCT1.Name)" } } $oNewDST.Update($false) sleep 2 $oTargetDSCT1.Update($false) $oTargetDSCT1 = $oTargetDSCT.ParentWeb.ContentTypes[$oTargetDSCT.Id] foreach($doc in $oSourceDSCT.XmlDocuments) { [xml]$xmldoc = $doc # "http://schemas.microsoft.com/office/documentsets/sharedfields" # "http://schemas.microsoft.com/office/documentsets/defaultdocuments" # "http://schemas.microsoft.com/office/documentsets/welcomepageview" # "http://schemas.microsoft.com/sharepoint/events" # "http://schemas.microsoft.com/office/documentsets/allowedcontenttypes" # "http://schemas.microsoft.com/office/documentsets/welcomepagefields" # "http://schemas.microsoft.com/sharepoint/v3/contenttype/forms" # "http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url" if(($xmldoc.FirstChild.Attributes[0].Value -notmatch "welcomepagefields") -and ($xmldoc.FirstChild.Attributes[0].Value -notmatch "events") -and ($xmldoc.FirstChild.Attributes[0].Value -notmatch "forms")) { Write-Host "`t Updating '$($xmldoc.FirstChild.Attributes[0].Value)'" try { $oTargetDSCT1.XmlDocuments.Delete($xmldoc.FirstChild.Attributes[0].Value) #$oTargetDSCT1.Update($false) $oTargetDSCT1.XmlDocuments.Add($doc) } catch { Write-Host "[ERROR] In Copy_DocSet_Properties '$($oTargetDSCT.Name)' unable to Set Welcome Page Properties" "red" Write-Host "[ERROR] $($_.Exception)" "red" Write-Host "[ERROR] $($_.ScriptStackTrace)" "red" } } } $oTargetDSCT1.Update($false) } }


Generic PowerShell Function to Create a Document Set SharePoint 2013 On-Prem

The function accepts 4 paramters the Site Url, the Library name where the Document Set will be created, the Content Type of the Document Set and finally the Name of the Document Set.
To set properties for the Document Set, and simple modification would be to include a key pair Hash table, the function declares an empty one ($dsProps) 

Example would be:
[hashtable] $dsProps = @{"Key" = "Value";"Key" = "Value";}


The function:
function createDocumentSets([string]$SiteUrl, [string]$LibraryName, [string]$CTName, [string]$dsName) { [Microsoft.SharePoint.SPWeb]$oWeb = Get-SPWeb $SiteUrl [Microsoft.SharePoint.SPList]$oLibrary = $oWeb.Lists[$LibraryName] if($oLibrary.Title -eq $LibraryName) { [Microsoft.SharePoint.SPContentType]$oCT = $oLibrary.ContentTypes[$CTName] if($oCT.Name -eq $CTName) { [Hashtable]$dsProps = @{} [Microsoft.Office.DocumentManagement.DocumentSets.DocumentSet]$newDs =
Write-Host "`tCreating '$dsName' ($CTName) in Library '$LibraryName' @ '$SiteUrl'" } else { Write-Host "[ERROR] Getting Content Type '$CTName' on Library '$LibraryName' @ '$SiteUrl'" } } else { Write-Host "[ERROR] Getting Library '$LibraryName' @ '$SiteUrl'" } $oWeb.Dispose() }


Windows Azure Management Script

When I first started writing these scripts the Windows Azure Management Portal was less than stable. Many times just starting a VM would cause it to get stuck in a starting state, disks could get lease blocked, if you tried to start more than one VM at a time you'd get an error saying ~ x-ms-requestid that requires exclusive access... so in brief using the Windows Azure Management Portal was challenging for production IaaS resources.

So I wrote some PowerShell functions to help me out. The most challenging part was the lack of working procedures and the constant flux of the Azure SDK, which presented itself in changed properties, differing methods and unexpected behaviour...
The management portal is significantly more stable now.
The script has a IaaS focus and consequently will only be of help in managing Virtual Machines and their Storage, but with a little more work can be extended to .

This script is on GitHub (Manage-Azure) and I encourage any with an interest to contribute to it's further development.

Anyway as of October 2013 the script works, so lets go in to their setup and use case.

  1. Ensure you have a Microsoft Azure account.
  2. Download the scripts from: Manage-Azure
  3. Extract them to a path on your local machine.
  4. Open a PowerShell window > import the script ". C:\Your-Location\Manage-Azure.ps1":

    Here you can see my PS execution policy doesn't allow me to run the script, and after I chnage the policy the script tells me I need to download the Azure Cmd line tools (bcos I'm testing on a completely vanilla Win7 VM).
  5. Unfortunately there's no way around downloading the SDK/Cmd line tools dependencies :(.
  6. Once the script is imported you have 2 functions available to you:
    1. Manage-AStorage
    2. Manage-AVM
  7. Type in the function and a list of operations will popup.
  8. If a "publishsettings" file isn't in the script location, it will ask you to choose the directory where it's located... download yours if you haven't already.
  9. Choose your subscription if you have more than 1.


This function provides Storage operations.

  1. Copy Blob.
    This operation enables copying blobs from container to container, and from storage account to storage account. If the storage accounts are in different data centers the copy operation can take a significant amount of time... eg: 127GB from Ireland to Hong Kong ~ 35minutes.

  2. Delete Blob.
    Delete 1 or more blobs.

  3. Add Disk
    Just like the 
    Windows Azure Management Portal, it creates a disk from a blog reference.
  4. Remove Disk
    Just like the 
    Windows Azure Management Portal
  5. Break Lease
    Sometimes a blob will have a lease by an object that doesn't exist, this used to be a big problem before October 2013 when VM's would frequently get stuck on starting, now not so much.
  6. New Snapshot
    Takes a blob snapshot.
  7. Restore Snapshot
    Restores a blob snapshot.


This function provides VM operations.

  1. Start
    Starts 1 or more VM's; this operation does one at a time and waits for them to start.

    Note: They don't always start at the same speed.
  2. Stop
    Stops 1 or more VM's; 
    this operation does one at a time and waits for them to stop.
  3. Backup
    This operation will stop a VM if started, copy the disk blobs attached to the VM to the backup container specified in the PS script ($BackupPath) and Start the VM if it was originally started.

    Note: In this screenshot you can observe iios-1 was "stopped" while iios-2 was "started" and " vhd-backups" didn't exist, so was created.

    Note: Here iios-2 has 2 attached disks.
  4. Restore
    This operation will stop a VM if started, Export the VM config, detach all disks, delete the disk blobs, remove the VM, let you select a blob to restore from the backup container, copy it to the vhds container, create disks for the blobs, recreate the VM from the config file and Start the VM if it was originally started.

  5. Export VM XML
    This just saves the VM configuration to an XML file.
  6. New VM XML
    This creates a VM from an XML configuration file. This comes in handy if a Restore operation fails for some reason, or you Delete the VM while keeping the attached disks.

Known Issues - Improvements

  1. Operation behaviour is inconsistent; you can perform a restore and it will fail at some point, one time and then do exactly the same operation and it works the next. The seeming randomness of it also makes it challenging to resolve.

    Note: Here you can see while trying to perform a Restore the script fails when recreating the VM from the config file. It's easy to recover from this point by just calling the "New VM XML" operation, but then this also fails when starting the VM even though the VM starts successfully... pretty annoying.
  2. Reuse of functions; I've been a little lax in reusing functions like copy/delete blob are used by multiple operations in Manage-AVM, but I don't use the Manage-AStorage functions.
  3. Better error handling; most Azure operations have an Id with returned properties indicating success/failure in most cases I don't use these.
  4. Logging needs improving, I capture Start and Stop VM logs but that's about it.


    SharePoint Foundation 2013 customize Stand-alone server installation

    SharePoint installs have two server type install options; either Complete, or Stand-alone.

    The Complete option lets you specify SQL server, accounts, and a couple other options. The problem with this option is that it requires a domain.
    The Stand-alone option doesn't let you specify anything and will install and configure the Service Apps using the built-in SQL Express 2008.

    What if you want to setup a Single Server, specify a local SQL Server, say SQL 2012 Dev edition and manually configure the service apps?

    1. Select Complete.
    2. Wait for it to finish, but don't launch SharePoint Products Configuration Wizard 
    3. Run the SharePoint 2013 Management Shell: C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe  -NoExit  " & ' C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\CONFIG\POWERSHELL\Registration\\sharepoint.ps1 ' "
    4. Paste the following command using names appropriate for your environment: New-SPConfigurationDatabase -DatabaseName "SPF2013_Config" -DatabaseServer "SPF2013SQL" -AdministrationContentDatabaseName "Content_CA"
    5. Run the SharePoint Products Configuration Wizard: "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\BIN\psconfigui.exe"
    6. Let it complete.
    7. Now the problem is you will not be able to add any Managed Accounts through the UI, to fix this you need to edit the following registry key: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\15.0\WSS]
    8. Change "ServerRole"="APPLICATION" to "ServerRole"="SINGLESERVER". Making this change will also break anything that uses psconfig, unless you have setup the SQL server to have a SharePoint instance, eg: server\SharePoint, that's how the Stand-alone SQL instance is configured so the psconfig checks if that instance is running. You'll only need psconfig to repair SP or install new service packs, and it's easy to change ServerRole back.
    This should also work with SPF 2010.