Setting up a O365 Dev Tenant – Part 2 – Create Users from CSV file


Introduction

In this series of posts, I will explain how you can set up a Development O365 Tenant quickly. Using PowerShell scripts, at the end of this process, you will have:

  • A O365 Development Tenant with 25 “DEVELOPERPACK” Licenses.
  • 25 Users assigned with license
  • A total of 274 Users added to the Tenant
    • Set up for multiple offices
    • Organisational structured
  • All users will be MFA enabled
  • All users will have photos added to their accounts
  • Enabling Office 365 Auditing
  • Setting up the SharePoint Tenant Settings and enabling Public CDN for SPFX

Unfortunately, I have found it impossible to do the following via PowerShell scripts, and these would need to be done manually. I haven’t included this information within these blog post.

  • Create a Tenant App Catalog
  • Set Organisation Details
  • Set Login Branding
  • Set Tenant Branding

In my previous post, I walked you through the step of joining the Office 365 Development program, and set yourself up with a Office 365 Development Tenant with 25 Developerpack Licenses. In this post we are going to add users to the tenant from a CSV file.

Obtaining the code

I have stored my code on GitHub at the following URL for cloning. https://github.com/pmatthews05/SetupDevTenant.git

Folders

  • Data – Contains the CSV files used within the scripts
  • Settings – Contains the JSON settings for the Set-SharePointTenant.ps1 script.
  • UserImages – Contains all the photos to all the users being added to the tenant.

Scripts

  • Set-Office365Auditing.ps1 – Turns on the Audit logs, and assign people to view them.
  • Set-SharePointTenant.ps1 – Update Tenant Parameters and enabling Public CDN
  • Set-UserAccountsOnline.ps1 – Add users and assign licenses and import pictures
  • Set-UserPhotosInExchange.ps1 – Add User photos to Exchange
  • Set-UserPhotosInSharePoint.ps1 – Add user photos to SharePoint.
  • Set-UserProfilePhotosInSharePoint.ps1 – Assigns the user picture to their SharePoint profile.

Creating the users from a CSV file.

In the Data folder there is a csv called AzureADUsers.csv. It is best to open this file in Excel.

Before you run any code, you will need to fix up a couple of columns in lines 2 & 3 of the csv file.

  • On line 2, replace UserPrincipleName, GivenName, Surname, MailNickName (Columns B, C, E, F, G) with the account details you gave yourself when creating the Subscription in part 1.
  • One line 3, add your name to the manager of Dan Jump.
  • Save the CSV file.
  • (optional) Put your profile picture in the UserImages folder, with the filename [firstname lastname].jpg

All the names/address/telephone numbers are all made up, converted by me. The names might seem familiar to you, as they are the “Microsoft Users” Microsoft uses in their demos.

We are going to use the powershell script Set-UserAcountsOnline.ps1. Note: None of my scripts will connect you to a service. This will need to be done first before running.

I’m not going to go through the code as the script is quite easy to follow and to understand. If there is anything you are unsure about that is in the script, please leave a comment at the bottom of the post.

Running Set-UserAccountsOnline.ps1

This script uses the AzureADUser.csv file and the UserImages profile pictures. It will:

  • Create/Update all users in Azure AD.
  • Assign them a license (If any are left)
  • If new user assigns them a password, to be changed at next login
  • Enable their account for Multi Factor Authentication. (This can be disabled in the CSV file, by changing the MFAEnabled to false for the user)
  • Upload their picture to Azure AD

To run the script your PowerShell environment will need to have the following modules installed.

  • AzureAD
  • MSOnline

If you haven’t done this previously, run Powershell as Administrator. Then type:

Install-Module -Name AzureAD
Install-Module -Name MSOnline

Unfortunately, the only way I could enable MFA for users was to use the MSOnline module. There doesn’t seem to be a way of doing it purely using AzureAD.

As stated previously, you will need to connect first, before running the script. It allows you to control the signing in.

Connect-AzureAD
Connect-MsolService

Now you are connected, you can call the Set-UserAccountOnline.ps1 file.

.\Set-UserAccountsOnline.ps1 -Path:'.\data\AzureADUsers.csv' -tenantDomain:'[mytenant].onmicrosoft.com' -tempPassword:'[Give A Password]'

Replace [mytenant] with your tenant name. You can either put a password in -tempPassword parameter, or you can remove this parameter. By removing this parameter, you will be setting all your users to have the password P@55w0rd.

This script is idempotent, so you can run it again at any point if there was an issue. When you run out of licenses, the script continues creating the accounts, they just don’t get a license assigned to them.

MFA Setup

After the script has run, and you go to your tenant, you will be asked to re-authenticate again, and provide details for MFA.

  • Click Next
  • I recommend using the Mobile App, and Receive notifications for verification. Click Set up
  • Follow the instructions to install the Microsoft authenticator app for your mobile.
  • Once set up, you need to provide a mobile number. Fill this in and click Next.
  • On the next page, you will be given an app password. Take note of this somewhere. These are useful if you need to run a script on this account, but want to skip MFA, you can just put this password in instead. Click Finished.

View Users in Admin portal

In the Admin portal https://admin.microsoft.com under users, you will see the users have been imported. Some with licenses and some without.

  • Any user you select, will give you their details and picture.

In this blog post we have created users from a CSV file, set them up with MFA and a default password. Assigned the first 25 users a license. In the next blog post I will be showing you the PowerShell script to import Pictures into Exchange.

Setting up a O365 Dev Tenant – Part 1 – Getting the Tenant


Introduction

In this series of posts, I will explain how you can set up a Development O365 Tenant quickly. Using PowerShell scripts, at the end of this process, you will have:

  • A O365 Development Tenant with 25 “DEVELOPERPACK” Licenses.
  • 25 Users assigned with license
  • A total of 274 Users added to the Tenant
    • Set up for multiple offices
    • Organisational structured
  • All users will be MFA enabled
  • All users will have photos added to their accounts
  • Enabling Office 365 Auditing
  • Setting up the SharePoint Tenant Settings and enabling Public CDN for SPFX

Unfortunately, I have found it impossible to do the following via PowerShell scripts, and these would need to be done manually. I haven’t included this information within these blog post.

  • Create a Tenant App Catalog
  • Set Organisation Details
  • Set Login Branding
  • Set Tenant Branding

Join Office 365 Development Program.

Microsoft allows anyone with a Microsoft account to get a development Office 365 Subscription by joining the Office 365 developer program.

https://developer.microsoft.com/en-us/office/dev-program

  • Click Join Now
  • Sign in with your Microsoft Account. (Hotmail, live, outlook etc)
  • Select your Country/Region and enter your Company name. Tick the terms and conditions and then click Next
  • Fill out the next page and then click JOIN.

Get an Office 365 Subscription

After you have joined the Development program, you will be present with a screen to allow you to set up a subscription.

  • Click Set Up Subscription
  • Fill out the form and click Continue:
    • Country/Region
    • Create UserName
    • Create Domain
    • Password
    • ConfirmPassword
  • Add your phone number on the next screen, then click Set-up
  • Once created, you can then click Go to Subscription
  • After you have signed in the link takes you to https://www.office.com.

  • Click on Admin takes you to the admin centre. Expanding … Show all in the left hand navigation and then expanding Admin Centers you can see you have a full E3 license tenant.

Assign yourself a license.

This is going to be a manual step, and can skip this if you wish, as my next blog will show you how to do this via code.

  • Expand Users, and select Active Users. You will see one user in the tenant. You. Currently this account doesn’t have a license. Select the user.
  • The panel that appears on the right, click the link Edit for Product licenses.
  • Select the Location and turn On the Office 365 E3 Developer Licenses. Click Save.
  • Your user now has a production assigned to them. Click Close.

How long is my Office 365 Subscription valid for?

You may have noticed when you created your subscription it states there are 91/92 days left. Microsoft use to give their members a subscription for 1 year. They have now reduced this to 90 days, however, there is a new clause that states:

As a program member, you can get a free Office 365 developer subscription with 25 user licenses to use to build your solutions. This subscription will remain active for as long as you’re actively developing and deploying solutions.’

I’m not sure how often you need to be deploying and developing on the subscription to keep it open. All the FAQ are found in the link below.

https://docs.microsoft.com/en-us/office/developer-program/office-365-developer-program-faq

In this blog post we have set up an O365 Development Tenant with 25 “DEVELOPERPACK” Licenses and assigned one license to ourselves. In the next blog post I will be showing you the PowerShell scripts I have written to help quickly set up my tenant.

 

SharePoint Online site keeps refreshing on Microsoft Edge


A weird thing started happening on my Microsoft SharePoint site yesterday. Every time I hit a page/site the browser continuously refreshed over and over. I had no problems if I did this in Chrome, or even Internet Explorer, only in Microsoft Edge. Originally I thought it was a problem with the site, because if I went to a different tenant I didn’t encounter the same issue.

So, I then booted up another pc that had Microsoft Edge on it, and viewed my site expecting it to refresh over and over, but it didn’t. Therefore, it seemed to be a problem with my Edge explorer.

I first tried to clear all settings. Clicking on the 3 ellipses and then select Settings > Clear browsing data. I ticked everything to be cleared, and then clicked Clear. Unfortunately, this didn’t work for me, and made my Edge explorer crash on opening.

Extreme measures were required. I needed to complete reset Microsoft Edge. Now if you are following these steps, I must warn you, you might lose your favourites, history and settings. I recommend that you create a full backup or create a system restore point. Although I didn’t bother with a system restore or backup. My favourites were still there afterwards, I’m not sure if this is a syncing thing between my devices though, as I’ve just noticed that my favourites are the same on two machines.

Fixing Microsoft Edge

  • Close all open Microsoft Edge browsers.
  • Open file explorer and navigate to the following location (You will need to show hidden files):
    • C:\users\<YourUserName>\AppData\Local\Packages
  • The folder called Microsoft.MicrosoftEdge_8wekyb3d8bbwe first right click it and select Properties, and remove the check from the Read-Only option, click Apply and then OK
  • Now try and delete the folder. If you get Access Denied prompts, just select Continue. Any files you cannot delete just skip for now.
  • Go inside the Microsoft.MicrosoftEdge_8wekyb3d8bbwe folder and try deleting any remaining folders, you might be not able to delete the AC folder.
  • Restart your computer.
  • Now we are going to obtain the Microsoft Edge package and reregister it. Open Windows PowerShell by right clicking it
    and Run as administrator
  • Type the following to get to your user cd c:\Users\<YourUserName> press enter.
  • Now type
Get-AppXPackage -AllUsers -Name Microsoft.MicrosoftEdge | Foreach {Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml" -Verbose}
  • If successfully you will not see any red message in the powershell window, just a yellow one saying operation complete for C:\Windows\SystemApps\Micorosft.MirosoftEdge.8wekyb3d8bbwe\AppXManifest.xml”
  • Reboot your machine on more time. Afterwards Microsoft Edge will be restored and I found that I didn’t have a refreshing issue with my SharePoint online site anymore.

Updating an expired Client Secret of SharePoint Add-in


Updated with Azure-AD / AZ Cli here https://cann0nf0dder.wordpress.com/2019/11/25/updating-an-expired-client-secret-of-a-sharepoint-add-in-using-azure-ad-az-cli/ 

Been working with SharePoint Add-in tokens for a while now, but this week has been the first time I’m still working with an add-in longer than a year in one environment. My application start throwing the error message:

Error:Token request failed., InnerMessage:System.Net.WebException: The remote server returned an error: (401) Unauthorized.”

I knew that they did expire after a year, but never really thought about (until now) how to go about renewing them. Luckily Microsoft documentation nowadays is a lot better than it used to be. I found this walk through https://msdn.microsoft.com/en-us/library/office/dn726681.aspx. In case Microsoft takes the link down, or changes the URL, I will explain the steps below. I have also changed the code slightly for the Report on Client ID expiry dates, as the Microsoft one didn’t return the results I expected.

Report on Client ID expiry dates.

  • Open Windows Powershell and run the following cmdlet:
Connect-MsolService
  • A login prompt will appear, here enter the tenant-administrator credentials for the Office 365 tenancy where the add-in was registered with AppRegNew.aspx
  • You can generate a report that list each add-in in the tenant with the date that the secret expires with the following PowerShelll code.

    $applist = Get-MsolServicePrincipal -all  |Where-Object -FilterScript { ($_.DisplayName -notlike "*Microsoft*") -and ($_.DisplayName -notlike "autohost*") -and  ($_.ServicePrincipalNames -notlike "*localhost*") }
    $output = "";
    foreach ($appentry in $applist)
    {
        $principalId = $appentry.AppPrincipalId
        $principalName = $appentry.DisplayName
    
        $results =  Get-MsolServicePrincipalCredential -AppPrincipalId $principalId -ReturnKeyValues $false | Where-Object { ($_.Type -ne "Other") -and ($_.Type -ne "Asymmetric") }
        if($results.count -gt 0)
        {
             $output += "PrincipalId:`t$principalId`n"
             $output += "PrincipalName:`t$principalName`n"
             $output += "Keys`n" foreach($result in $results) { 
             $output += "Type:`t" + $result.Type + "`n" 
             $output += "Value:`t" + $result.Value + "`n" 
             $output += "KeyId:`t" + $result.KeyId + "`n" 
             $output += "StartDate:`t" + $result.StartDate + "`n" 
             $output += "EndDate:`t" + $result.EndDate + "`n" 
             $output += "Usage:`t" + $result.Usage+ "`n" $output += "`n"        
         } 
          $output += "-----------------------------------------------`n" } 
    } 
          $output | Out-File "c:\temp\appsec.txt" 
    
    • The above code first filters out Microsoft’s own applications, add-ins still under development (and a now-deprecated type of add-in that was called autohosted).
    • Filters out non-SharePoint add-ins and add-in like workflow.
  • Open the file at c:\temp\appsec.text to see the report.

An example of the report below:

Note: The PrincipalID is your Client ID

Generate a new secret for another year.

To generate a new secret, you just need to run the following PowerShell script:

$clientId = &lt;#Replace with your ClientID of the add-in&gt;
$bytes = New-Object Byte[] 32
$rand = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rand.GetBytes($bytes)
$rand.Dispose()
$newClientSecret = [System.Convert]::ToBase64String($bytes)
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Sign -Value $newClientSecret
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Verify -Value $newClientSecret
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Password -Usage Verify -Value $newClientSecret
$newClientSecret

The output of the above PowerShell file will give you a new Client Secret, take note of this:

Generate a new secret for 3 years.

It is possible to create a secret that will last 3 years, the PowerShell script is very similar to the above script, but now it has a start and end date.

$clientId = &lt;#Replace with your ClientID of the add-in&gt;
$bytes = New-Object Byte[] 32
$rand = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rand.GetBytes($bytes)
$rand.Dispose()
$newClientSecret = [System.Convert]::ToBase64String($bytes)
$dtStart = [System.DateTime]::Now
$dtEnd = $dtStart.AddYears(3)
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Sign -Value $newClientSecret -StartDate $dtStart -EndDate $dtEnd
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Symmetric -Usage Verify -Value $newClientSecret -StartDate $dtStart -EndDate $dtEnd
New-MsolServicePrincipalCredential -AppPrincipalId $clientId -Type Password -Usage Verify -Value $newClientSecret -StartDate $dtStart -EndDate $dtEnd
$newClientSecret

Updating the remote web application using the new secret

  • If you are redeploying from Visual Studio you will need to update the client Secret in your Web.Config
  • If you are just updating directly in Azure, you can just go to the configurations and update the new secret there.

If you are using the default TokenHelper.cs file in your project, and it’s not the prerelease version then you can add a second app setting called SecondaryClientSecret. Here you would put in your old secret, and in the ClientSecret put in the new one. This is so if your token is going to expire soon, the application will still work as it will try one first then the other.

Time to propagate Client Secret to SharePoint

According the Microsoft link you should wait at least 24 hours to propagate the ClientSecret. However, I found as soon as I changed the secret I could use it straight away. After changing your Client Secret if you run the ‘Report on Client ID expiry dates’ powershell again, those dates didn’t update for me until the following day.

I ran the report the following day, and as you can see below, Demo App 1 which was shown in the screen shot above now has 3 new keys with new dates.

Deleting expired client secrets

As expired client secrets do not seem to get removed, it is recommended to delete them. The code below will grab any keys that are expired for a ClientID and delete them.

$clientId = &lt;#Replace with your ClientID of the add-in&gt;;
$keys = Get-MsolServicePrincipalCredential -AppPrincipalId $clientId -ReturnKeyValues $false
$dtNow = [System.DateTime]::Now
foreach($key in $keys)
{
 if($key.EndDate -lt  $dtNow)
 {
   write-host $key.KeyId "Expired"
   Remove-MsolServicePrincipalCredential -KeyIds @($key.KeyId) -AppPrincipalId $clientId
 }
}

Adding to Property Bag and Index using PowerShell in SharePoint Online


The other day, I very lazily, used SharePoint Designer to add a few items to the property bag. Soon as I done it, someone then said they wanted to search upon the values inside the property bag. Unfortunately this isn’t something that can be done via SharePoint Designer. After spending 5 minutes or so searching online for a pre-made PowerShell code, I could only find the solution for On-Prem.

The only piece of code I found to do this to SharePoint Online was through the Office Developer PNP CSOM code. I really didn’t want to create a Visual Studio project just to use the PNP Core. (See http://dev.office.com/patterns-and-practices for more information Office Dev PNP, there is so much information and cool videos there, there really is no need to re-hash anything in my own blog.) So I decided to just create PowerShell script which is pretty close to being like for like copy of the PNP Core method:

Web.AddIndexedPropertyBagKey(this Web web, string key)

Before I go into the code, I will explain how SharePoint knows which property bag items need to be indexed for search. Just by adding the item doesn’t make it visible to search, it has to be added to another property bag item called “vti_indexedpropertykeys“. The value of this property bag item is a pipe delimited Base64String. (Example: RABhAHUAZwBoAHQAZQByAA==|UwBvAG4A|cwBvAG4A| ). As you can see, it’s not just as simple as adding the string name to the vti_indexedpropertykeys value.

To encode a single value in PowerShell the following line of code works:

[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($value))

To decode a single value from Base64String to text the following PowerShell code works:

[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($EncodedValue))

The below code will ask you for the Web Url, your username, password (Secure string), propertybag key name and value. The code will first add or update the value in the property bag, and if it hasn’t been added to the index it will add it.

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")| Out-Null
$indexPropertyKeyConst = "vti_indexedpropertykeys"

function GetIndexedPropertyBagKeys($ctx)
{
  $results = @()
  $web = $ctx.Web;
  $ctx.Load($web.AllProperties)

  try
  {
  $ctx.ExecuteQuery();
  }
  catch{
   Write-host "Error accessing property bag " $_.Exception.Message -ForegroundColor Red
   exit 1
  }

  $indexPropertyBase64 = $web.AllProperties[$indexPropertyKeyConst];
  $separator = "|"
  $option = [System.StringSplitOptions]::RemoveEmptyEntries

  if(![string]::IsNullOrEmpty($indexPropertyBase64))
  {
    $resultsBase64 = $indexPropertyBase64.Split($separator, $option)

    foreach($r in $resultsBase64)
    {
     $results += [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($r))
    }
  }
    #comma not a mistake, required to ensure correct type is returned on empty or single array value.
    return ,$results
}

function GetEncodedValueForSearchIndexProperty($keysArray)
{
  $encode64Keys = [String]::Empty
  foreach($key in $keysArray)
  {
   $encode64Keys += [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($key))
   $encode64Keys += "|"
  }

  return $encode64Keys;
}

function AddIndexedPropertyBagKey($ctx, $propertyKey)
{
  [bool]$result = $false;
  [bool]$addValue = $false;
  $keys = GetIndexedPropertyBagKeys($ctx)

  if($keys -cnotcontains $propertyKey)
  {
     $addValue = $true;
  }

 if($addValue)
 {
   try
   {
   $keys += $propertyKey
   $keysBase64String = GetEncodedValueForSearchIndexProperty($keys)
   $ctx.Web.AllProperties[$indexPropertyKeyConst] = $keysBase64String
   $ctx.Web.Update()
   $ctx.ExecuteQuery()
   $result = $true
   }
   catch
   {
    Write-host "Error adding $propertyKey to index. " $_.Exception.Message -ForegroundColor Red
    exit 1
   }
 }

  return $result
}

function CreateUpdatePropertyBag($ctx, $propertyKey, $propValue)
{
  $web = $ctx.Web;
  $ctx.Load($web.AllProperties)
  try
  {
    $ctx.ExecuteQuery();
    $web.AllProperties[$propertyKey] = $propValue;
    $web.Update();
    $ctx.ExecuteQuery();
  }
  catch{
   Write-host "Error adding $propertyKey to property bag " $_.Exception.Message -ForegroundColor Red
   exit 1
  }
}

$webUrl = Read-Host -Prompt "Enter the WebUrl"
$username = Read-Host -Prompt "Enter your Email login"
$password = Read-Host -Prompt "Password for $username" -AsSecureString
$propKeyToIndex = Read-Host -Prompt "Enter the property key name to index"
$propValue = Read-Host -Prompt "Enter the $propKeyToIndex value"

$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)

CreateUpdatePropertyBag $ctx $propKeyToIndex $propValue;
AddIndexedPropertyBagKey $ctx $propKeyToIndex;

Write-Host "Complete" -ForegroundColor Green

You can download the source code directly from my OneDrive.

Thank you to my colleague Paul Perry with his help around PowerShell arrays.

PowerShell – Loop through Sites, Content Types, Lists.


Many times before I have had to write a bit of PowerShell to loop through a SharePoint site fixing something up. When I start writing this script I normally start from scratch each time. Basically I need to perform the same basic looping functionality, and then either at the List level, content type level maybe even at the item level I need to perform some change.

For example, I might need to delete a certain field from every list of type event, or I need to set the default value on a column, or perhaps you just want to loop through the site to see what is where. Whatever the reason is, if the change needs to be made in many places, I will need to loop through the environment.

The order typically I loop through would be:

  • Web
  • Content Type
  • Lists
  • Content Types in Lists
  • Fields in Content Types
  • Items

Depending on what I was trying to achieve, I would either add, or remove extra sections going forward.

The code below is the basic template I would use to get me going.

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

#Functions
#Process the web Site, checking the Content Type at the web first before checking the Content Types at the list level.

function processWeb($web){
 #web
 $ctx.load($web)
 #Content Type
 $cts = $web.ContentTypes
 $ctx.load($cts)
 #Lists
 $lists = $web.Lists
 $ctx.Load($lists)
 $ctx.ExecuteQuery()
 #Display Web 
 write-host "Web Name:" $web.Title 
 processContentTypes($cts)
 processLists($lists)
}

#Loop through each content type
function processContentTypes($cts)
{
  foreach($ct in $cts)
  {
    Write-Host "Content Type:" $ct.Name             
    $ctx.Load($ct.FieldLinks);
    $ctx.ExecuteQuery();

    processFieldsLinks($ct.FieldLinks)
  }
}

#Loop through fields
function processFieldsLinks($fieldsLinks)
{
  foreach($f in $ct.FieldLinks)
  {
      Write-Host "Field " $f.Name 
  }
}

#Loop through each item
function processItems($items)
{
 foreach($item in $items)
 {
  if($item.FieldValues["Title"] -eq $null)
  {
   write-host "Item FileLeafRef: " $item.FieldValues["FileLeafRef"]
  }
  else
  {
   write-host "Item Title: " $item.FieldValues["Title"]
  }
 }
}

#Loop through each list in the site
function processLists($lists){
 foreach($list in $lists)
 {
  $ctx.load($list)
  $ctx.load($list.ContentTypes)
  $query = [Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery(10000, 'UniqueId','ID','Created','Modified','FileLeafRef','Title')

  $listItems = $list.GetItems($query);
  $ctx.load($listItems)
  $ctx.ExecuteQuery();

  write-host "BaseTemplate: " $list.BaseTemplate " List: "$list.Title
  processContentTypes($list.ContentTypes)
  processItems($listItems)
 }
}

#Get the URL, and an account to log in with. (Needs permissions across the sites to make changes)
$webUrl = Read-Host -Prompt "Enter the URL of your Root site" 
$username = Read-Host -Prompt "Enter your Email login"
$password = Read-Host -Prompt "Password for $username" –AsSecureString

#Create Context and load sites.
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)

$rootWeb = $ctx.Web
$childWebs = $rootWeb.Webs
$ctx.Load($rootWeb)
$ctx.Load($childWebs)
$ctx.ExecuteQuery();

#Process the rootweb
processWeb($rootWeb)

#process the child webs
foreach($childWeb in $childWebs)
{
 processWeb($childWeb);
}

#complete
Write-Host "******Completed*********" -ForegroundColor Green

Just to give an example of how I might adjust this, if I wanted to only loop through lists that are Site Pages, with BaseTemplate of 119, I would change the processLists() function to only process the list if the BaseTemplate is 119.

#Loop through each list in the site
function processLists($lists){
 foreach($list in $lists)
 {
   $ctx.load($list)
   $ctx.ExecuteQuery()

   if($list.BaseTemplate -eq 119)
   {
     $ctx.load($list.ContentTypes)
     $query = [Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery(10000, 'UniqueId','ID','Created','Modified','FileLeafRef','Title')
     $listItems = $list.GetItems($query);
     $ctx.load($listItems)
     $ctx.ExecuteQuery();

     write-host "BaseTemplate: " $list.BaseTemplate " List: "$list.Title
     processContentTypes($list.ContentTypes)
     processItems($listItems)
   }
 }
}

Link to file in OneDrive: http://1drv.ms/1V5Q8j8

Handling Azure PowerShell with multiple Azure Subscriptions


The other day I was trying to create an Azure WebSite for a client, I ran the commands, everything was successful. However when I went to the client Azure tenant, there was no sign of the web site! But I could hit the URL, which proved the site existed. I was dreading creating a Microsoft Support call, but as it was near the end of the day I went home. Deal with it in the morning. That evening, I was doing some work at home, and logged into my own personal Azure Tenant, and there was the Website I created for the client.

It turns out,

It doesn’t matter if you are logged into your Azure Website.

It doesn’t matter if you’ve just added the new account to Azure PowerShell.

It doesn’t matter if you’ve not used your old Azure Tenant in a while.

It Does matter what Azure PowerShell has as your Default, or Current Subscription at the time of calling your Azure cmdlets.

What do I mean?

Well before I explain Current/Default Subscription, let’s get those who have never used PowerShell Azure with their Azure account set up.

  1. Open your Azure PowerShell window
  2. Type
    Add-AzureAccount

    press enter.

  3. You will be asked to sign into your Azure account.

  1. Once done, PowerShell window will display your Id, Type of account, Subscriptions, Tenants.

If you now type

Get-AzureSubscription

it will state that your Subscription is the Default and Current. Default means every time you open your PowerShell window, it will already have this subscription loaded. Current means, if you have multiple subscriptions the one marked as current is the one you are performing your cmdlets against.

Selecting the Azure Subscription to work against.

So what to do? I’ve added a second, free trial azure subscription to powershell azure by calling the

Add-AzureAccout


cmdlet again. I’ve then called

Get-AzureSubscription


again to list all subscriptions. (Please note, you might have one account that have several subscriptions, it’s not a one to one relationship, just happens to be for me).

As you can see from the above image, the Subscription Name “Windows Azure MSDN – Visual Studio Ultimate” is both the default and the current subscription.

I can make the Subscription Name “Free Trial” my current subscription by calling (Note: Subscription name is case sensitive)

Select-AzureSubscription –SubscriptionName "Free Trial"

NOTE: (Just learnt this myself!!) If you have version 0.8.14 of PowerShell Azure you can call by Subscription ID instead. This will help for people with Multiple Subscriptions with the same name. Not sure which version this was implemented but in 0.8.3 it wasn’t there. Find out your version by typing (get-module azure).version

Select-AzureSubscription –SubscriptionId [SubscriptionID]

Now if you type

Get-AzureSubscription

you can see that my Free Trial is now Current.

To make the Free Trial default, all I need to do is add –Default to the Select-AzureSubscription cmdlet

Select-AzureSubscription  -SubscriptionName "Free Trial" –Default

Hopefully now you won’t end up making changes to the wrong Azure instance like I did originally.