Showing posts with label SharePoint Online. Show all posts
Showing posts with label SharePoint Online. Show all posts

Tuesday, 2 January 2018

Change Site Policy Deletion notification email template in SharePoint

The Site Policies in SharePoint are information management tool that helps you implement some site life cycle management. Whether this is dictated by internal house keeping rules or some external regulations that apply to your organisation, the Site Policy is the out of the box way to go if you want to "close" a site, delete it or both, automaticaly after certain period of time.
With site policies you have the option to notify the site owners in advance before the site is deleted. The mail looks like the one below.

Site Deletion Notice

The information in this email might not be suitable for your organization.
Fortunately there is a way to change the default Site Policy notification email body template and the email subject. This is not done in some XML template file like the Alerts template, maybe there is one, but I have not found it. There is SSOM and CSOM API that you can use to set custom email body template per policy.
The documentation of this is very poor and the best resource on this is the article Site Policy in SharePoint.
Unfortunately I have not managed to make this work server side or using PowerShell. I have tried with SharePoint 2013, SharePoint 2016 and SharePoint Online ssom and csom as well.
The only way I found it working is from console application using the CSOM approach.
The site policy post above is good and the code should work as it is, but it has some gaps.
There are three placeholders that we can use, placeholders for Site Url, Deletion Date and Mailbox Id.
However the placeholders with curly braces that are demonstrated in the post will not work.
I would like to save you some time testing especially if you are targeting SharePoint Online, as there you cannot manually run the "Expiration Policy" timer job.

The correct placeholders are below, without curly braces or any spaces.

SiteUrl: <!--SiteUrl-->
Deletion Date: <!--SiteDeleteDate-->
Mailbox ID: <!--TeamMailboxID-->


I hope you found this helpful!

Tuesday, 19 December 2017

Site collection app catalog available in SharePoint Online

The site collection app catalog is now generally available for all tenants in SharePoint Online.
 It allows you to make SharePoint Add-Ins and SPFx solutions available only in certain site collections. This is a great improvements because it allows more flexible deployment options, you can test a solutions only on site collection level before pushing it to the entire tenant and it can decentralize the management of add-ins and SPFx packages.
You can create Site Collection App catalog by using SharePoint Online Management Shell.
Note that you will still need to configure tenant app catalog. If you try to provision site collection app catalog without tenant app catalog you will get below exception.

Add-SPOSiteCollectionAppCatalog : Cannot invoke method or retrieve property from null object. Object returned by the
following call stack is null. "TenantAppCatalog
RootWeb
GetSiteByUrl
new Microsoft.Online.SharePoint.TenantAdministration.Tenant()


For more information on site collection app catalog check out below PnP Webcast:


Sunday, 17 December 2017

Fantastic 40 is back in SPFx form

Well, actually not, but I hope that the title of this post will make you want to check it out.
I would like to share a great project called "SPFx Fantastic 40 Web Parts". Except the name, this project has almost nothing to do with the old "Fantastic 40 Application Templates for SharePoint (WSS & MOSS)" because it is a collection of 40 SPFx Client Side Web Parts. It is open source project so you can use it as you find for useful. It was created by Olivier Carpentier, but the code is provided "as is" without support from Microsoft.
I am so excited because the web parts look great and they might be a solution to common needs that the out of the box modern webparts don't fulfill.
It is also a great learning resource and demonstration of what can be done with SPFx.
If it is not an issue for you to consume the web parts assets from CDNs you do not control you can download and deploy the *.sppkg as it is and it will work. I also tried most of the web parts on SharePoint Server 2016 with classic pages and they work and look great.
The web parts are grouped in seven categories and below you can find some samples.

Menu & Carousels & News Management: News Slider

The News Slider Web Part renders a simple news slider carousel to your page. You can manage your active news, manage the layout and easily render a cool slider to enhance your pages.


Social Tools: Tweets Feed

The Tweets Feed Web Part is a SharePoint client side web part built with the SharePoint Framework (SPFx). This web part generates a Twitter Feed on the page, based on the specified account. This Web Part uses the Twitter API to integrate the timeline.



Maps, Charts & Graphs: Bar Chart

The Bar Chart Web Part is a SharePoint client side web part built with the SharePoint Framework (SPFx). This web part insert a Bar Chart in your pages, and you can manage the bar chart settings as items, color, title, legends, etc. This web part uses chart.js lib.


Images Galleries & Tools: Grid Gallery

The grid gallery Web Part renders a pictures slideshow with grid of thumbnails. This web part implements unitegallery.js (a popular jquery script) as a client side web part for SharePoint.


Video & Audio: Media Player

The Media Player Web Part is a SharePoint client side web part built with the SharePoint Framework (SPFx). With it, you can insert a video in your pages that is a HTML 5 compatible video or audio files, a YouTube video or a Vimeo video. This web part uses Plyr.js lib.


Text Tools: Tabs

The Tabs Web Part is a SharePoint client side web part built with the SharePoint Framework (SPFx). This web part helps to create a tab (you manage, add, delete, edit or move a tab dynamically) and the web part editor can easily modify the tabs content thanks a HTML editor (WYSIWYG). This tab control is responsive, so the layout will adapt the render with the screen size.



Tools: Stock Info

The Stock Info Web Part is a SharePoint client side web part built with the SharePoint Framework (SPFx). This web part generates a stock graph picture for a specified stock. This web part uses the Yahoo Financial service.


Sunday, 22 January 2017

Conditionally Show/Hide and Require SharePoint fields with JavaScript

In my last project I worked on DMS system based on SharePoint Server 2016 and had a very common requirement for the document forms. The requirement was to have Document Status field, Approval Reason and Rejected Reason fields. It is logical to want to hide Approval Reason when the document status is Rejected or Rejected Reason when the document is Approved.The client also wanted to make the fields required when the corresponding document status is selected. 
All customizations were going to be deployed using the classic way, with farm solution. I also had Nintex Forms, but I was unable to use it in this case due to other technical constraints.
That's why I decided to solve this requirement using JQuery and JavaScript script that are deployed with the WSP solution and included in the mater page.
The end result was pretty good and this is why I decided to share the script and a way to safely deploy it in SharePoint Online without the need to edit the master page or the list forms. The script is really simple and can be used/deployed as it is or with minor changes by person with moderate JavaScript experience.
Few notes on the SPO environment that was used. There are 3 custom site columns with following Title, InternalNames and Type: 
- Title: Document Status, InternalName: DocumentStatus, Choice, radio buttons
- Title: Approval Reason, InternalName: ApprovalReason, Multi-line text, Not required by definition
- Title: Rejected Reason, InternalName: RejectedReason, Multi-line text, Not required by definition

You can see the script below:

There are two main functions, showOrHideFields that will be started when the page is loaded and will show or hide Approval Reason or Rejected Reason field depending on the value of the Document Status field in New,View and Edit forms. The second function is with the specific name PreSaveAction, it will be launched when Check In or Save button is clicked and will not allow the form to be saved if Approval Reason or Rejected Reason fields are empty and will pop-out an "error" message below the field, again depending on the Document Status field value.
Below you can see how the form looks like in New,Edit and View mode.

New, Edit and View forms

In order to deploy the script without modifying the forms or the master page we are going to use the SharePoint PnP PowerShell module for SharePoint Online. In order to "inject" the javascript links we are going to use Add-PnPJavaScriptLink command. In the example below I am uploading the JS file, adding ScriptLink to it and adding ScriptLink to Google hosted JQuery library.



Add-PnPFile -Path "C:\Users\Ivan\Documents\ShowHide.js" -Folder "Style Library/scripts/" `
    -Checkout -Publish -CheckInComment ""
 
Add-PnPJavaScriptLink -Name JQuery `
    -Url "https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"
 
Add-PnPJavaScriptLink -Name ShowHide `
    -Url "https://mod44444.sharepoint.com/Style%20Library/scripts/ShowHide.js"


Once the commands are executed the scripts will be loaded in all classic pages in the web(default scope for this command).
Unfortunately this really useful way of injecting JavaScript will not work with the modern pages and the new Library and List experience. More info on this huge gap can be found in this post.

I hope that this was helpful!

Tuesday, 6 September 2016

Set Managed Metadata field value with PowerShell and CSOM

In the previous post I demonstrated an easy way to migrate managed metadata term store objects to SharePoint Online with PowerShell.
Now when you have migrated the terms you might need to migrate some documents and set the metadata fields in SharePoint Online. In the same project I had to migrate around 600 documents to SPO including the metadata which had 6 managed metadata fields, 4 of them were multi-valued.
In this post I will share a powershell snipped to make TaxonomyFieldValueCollection and use it as value for field of type Managed Metadata.
I am showing this method because I got some mixed results when I used simple string as value. It is hard for me to explain why simply updating with taxonomy string did not worked in all cases.
For example, if the document was created in Office Web Apps I was unable to set the fields using a simple string. You can try using the string method and then cross-check  if everithing is set, because if you feed only metadata string(multi-valued) or just guid(for single-valued) you might not get any error, but the field will be left blank.
The challenge for me in the "TaxonomyFieldValueCollection" approach was to create TaxonomyField object instance, because I had to use the generic client context method CastTo and PowerShell don't work well with generic methods. This is why I decided it is worth sharing this example. You can see the code below.

Now a couple of words about the string that is used. In the example above I am setting multi-valued MM field with collection of two terms. The string is in format "<int>;#<label>|<guid> " with ;# delimiter between the terms. The integer is the item id of the term in the Taxonomy Hidden List, if you are using the term for first time or you do not know this id you can use the default value "-1".
The label part speaks for itself, this is the label of the term and the most important part is the guid of the term. If something is wrong with the format of the string you will see below error message.

"The given value for a taxonomy field was not formatted in the required <int>;#<label>|<guid> format."


This method is working every time for all items. I hope that this was helpful!

Sunday, 21 August 2016

Migrate SharePoint 2010 Term Store to SharePoint Online with PowerShell

Last week I worked with a customer on migrating one SharePoint 2010 site to a new SharePoint Online.
I can qualify the site as Knowledge Base designed for optimal discoverability of the documents that are uploaded. To achieve a good discoverability you will need some good metadata describing the resources. Many times the metadata that is used is actually managed metadata that needs to be migrated/recreated in SharePoint Online.
If you have 10 or 20 terms it will not be an issue to recreate them, but if you have 400 for example it will not be very practical to manually recreate all terms.
There are many powershell scripts out there to export/import terms, but the success rate and the complexity might vary. This is why I would like to share how I did it and it worked out pretty well for me.
For the purpose we are going use the custom cmdlets provided for free by Gary Lapointe.
For demonstration purposes I will export one term set group with one term set that has limited number of terms. You can check it out below, it also has some parent/child terms.

SharePoint 2010 Term Store

In order to export the term set group you will need to deploy the WSP that will add the custom SharePoint Server 2010 commands. By doing so you will add the additional 2010 commands directly to the Microsoft.SharePoint.PowerShell snap-in.
To export taxonomy object as xml we are going to use Export-SPTerms. You will need to supply some taxonomy object as input parameter, this will be a taxonomy session if you want to export everything, for more examples see the cmdlet help. You can see how the Legal term set group looks as  xml below.

Input XML

As you can see all essential information that is needed is exported, even some that will be an issue if you are importing the terms to a different environment or SharePoint Online. This is the Owner or every attribute that represents on-prem identity that you might have. The import command will also try to set this properties with the same values and it will fail because the identity as it was exported cannot be found. The way to workaround this is just to set different value for Owner that will be a valid Online identity. Now it is up to you to decide if you want to do this tradeoff and migrate the objects with different Owner than the source. Below are two lines (3 to make it fit better) that will take the content of the exported XML and will set new Owner for each XML node where the Owner attribute is not empty and later the same XML object can be used for input of the import command.

[xml]$termXML = Get-Content "C:\Legal.xml"
($termXML.SelectNodes("//*")) | Where {$_.Owner -ne $null} | `
ForEach-Object {$_.SetAttribute("Owner", "i:0#.f|membership|admin@MOD******.onmicrosoft.com")}

To import the taxonomy objects in SPO you will need to download and install the SharePoint Online Custom Cmdlets
This will actually install a new module called  Lapointe.SharePointOnline.PowerShell.
The command that we are going to use for the import is Import-SPOTaxonomy. For InputFile parameter we are going to use the variable from the above lines after we have set all identity attributes. If you are importing an object that is not a top level term store you should specify ParentTermStore(can get it with Get-SPOTermStore), if not you should switch on the parameter "Tenant". Before all that, you should connect to a site in your target tenant using Connect-SPOSite. Below are the lines to import the Legal term set group.

Connect-SPOSite -Url "https://mod******.sharepoint.com"
Import-SPOTaxonomy -InputFile $termXML -ParentTermStore (Get-SPOTermStore)

And this is it. Our Legal term set group is recreated and available in the entire tenant. One nice thing is that the GUIDs will be copied as well.

SharePoint Online Term Store

I hope that this was helpful and big thanks to Gary Lapointe for writing this great tools! The same approach should work for SharePoint 2013, but I have not tested this.

Monday, 27 June 2016

Useful file handling commands in the SharePoint PnP PowerShell module

Last week I got a request from one of our customers to help them to move some of the files from one SharePoint library to another in different web. Sounds an easy and quick task, but the catch was that the library had ~ 12000 documents and 1500 folders and the customer also wanted to keep the Created, Created by, Modified and Modified by column values. The number of items that had to be moved was ~ 3500. Pointless to say that with such number of items the Explorer view is not working, the new OneDrive client does not support sync from SharePoint libraries yet and I still had to figure out how to effectively copy the metadata. The way to accomplish this is with PowerShell or with 3rd party migration tool. Since the customer had only this requirements and not migration of the version history for example, my weapon of choice was powershell.
In this post I wont to share a couple of SharePoint PnP PowerShell cmdlets that greatly helped me in writing my migration script. Bluesource is also a contributor to the PnP project, thanks to my colleague Pieter Veenstra.
The first thing I want to share is a new feature that came with the June 2016 release. It is the option to map SharePoint site as PSDrive. This is done at the begging using Connect-SPOnline with CreateDrive parameter. You will then get a PSDrive called SPO and a PSProvider also called SPO. See how it looks below.

As mentioned above you will connect to the site and you will be looking at the root web. The sub webs will be shown as folders. You can do many standard things in this PSDrive like listing items, copy, move and more. One thing I was unable to do is listing the items in lists with 5000+ items, it seems that the view threshold limitation is kicking in. The other thing is copy items from SPO drive to the local file system, this is because you cannot copy items from one drive to another if the PSProviders are different. Also it would have been nice if this SPO drive is persistent and you can access it in Windows Explorer, this is not available and I am not sure if it is possible. 
I did not used this in my script, but I think that it is nice to have and you can learn more about this and other improvements in the June 2016 Community Call.

Get-SPOFile - This is one very useful command that will help you to download a file by supplying the server relative url to it. This was easy task after I retrieved all items and their FileLeafRef and FileDirRef fields using the technique from my previous post

Ensure-SPOFolder - With this command you can get a folder by giving a web relative url to the folder. If the folder does not exist it will create it even if the folder is nested in other non-existing folders, they will be created as well.

Add-SPOFile - With this command you can upload a file by supplying web relative url of the folder, the file will be uploaded with the same name. If the folder does not exist this command will create it before uploading the file. Really nice command!

I hope that this was helpful!

Monday, 15 February 2016

Copy List Views in SharePoint and SharePoint Online with PowerShell

In the last couple of weeks I am working with a customer that mainly uses SharePoint Server as DMS(Document Management System). I had to move a large number of documents from one library to another   due to  corruption in some of the files caused by excessive use of unique permissions (~ 32 000).
In this post I will not talk about why you should limit the usage of unique permissions especial in big libraries, it's a long story.
As part of the work I had to copy many list views to the new library. I am not a fan of "Save as Template" approach, my solution was to use a PowerShell script and copy the views programmatically.
The script did its job and I thought that it will be nice to have something like this for SharePoint Online.
I was unable to find any ready script that can do this, so I wrote one.
Both scripts are doing basically the same thing, getting the source and destination webs, getting the source and destination lists, getting the source and destination View collections and create new view in the destination using properties from the source.
It was a bit tricky to load what I need and not making the script extremely slow with CSOM, since we cannot use simple lambda expressions syntax in PowerShell. My solution to this was to use a function written by the SharePoint automation superstar  Gary Lapointe. You can check it out in his article for ItUnity, where he explains how to workaround the limitation in PowerShell concerning the lambda expressions. I highly recommend to read the entire series dedicated on using PowerShell against SharePoint Online.
You can download both scripts (on premises and online) below. Please, Test, Rate and use Q&A section in the Gallery!


 Download On Premises script from: TechNet Gallery

Download SharePoint Online script from: TechNet Gallery

Monday, 16 November 2015

Display and Fill related item fields in workflow task with Nintex for Office 365 Nintex

A couple of weeks ago I posted an article called "Move away from the Workflow Tasks with Nintex for Office 365". The goal of this post was to show some key techniques that you can use if you want to automate a process without using tasks and do all the work in the form so users can see what they are approving.
However, if you want or need to stick with the tasks there is way to display and fill fields from the related item in the task form.
To do this you will need to edit the task template from the workflow designer. This feature was introduced with July 2015 release of Nintex Forms for Office 365.
To illustrate how this is working I am going to use the familiar simple Vacation Request form. See how it looks in the form designer below.

Vacation Request

You can see a field with label "Manager Comment" this field is visible only when the form is not in "New Mode", it should contain some information from the manager that will approve the request.
In Nintex Workflow for Office 365 there is no Request Data action, but we can include the field that requires information on task completion in the task form.
If you open a Assign a Task(or Start a Task Process) action configuration page you will see "Edit Task Form" in the action ribbon. By clicking it you will get to the familiar Nintex Forms Designer interface.


In the picture above I am editing the default Nintex task content type and once you open the form designer the initial form includes task columns and item column. Initially all related item columns are disabled, but you can change that for any column you want and make it editable in the task, you can also connect control from the task form to related item column. With some rearrangement you ca see how the Vacation Request approval task looks below.


There is something you should consider if you have many tasks with modified forms. This is something I hit with a customer and later reproduced. This is not yet confirmed by Nintex.
When you edit a task form, the task will be created with Nintex content type(ending with GUID), even if you have specified your own custom content type. This is not an issue because the Nintex content type will have the same columns as the original. However you will have to edit all Task Action forms in the workflow regardless of what content type they are using. If for example two tasks have the same initial custom content type, they will be created under the same Nintex content type after you edit both forms.
This is still not a big deal. However, every time you edit a task form two hidden variables are created and you can easily end up hitting the variable count limitation in the Workflow Manager which is 50 in SharePoint Online. See below error when you try to publish a workflow that has more than 50 variables.

Error publishing workflow. Workflow XAML failed validation due to the following errors: Activity 'DynamicActivity' has 52 arguments, which exceeds the maximum number of arguments per activity (50). HTTP headers received from the server - ActivityId: 9e1fc3bc-5a7c-4821-9605-d595acea851d. NodeId: . Scope: . Client ActivityId : 29ca419d-d068-2000-213e-aae9dfcc2677. The remote server returned an error: (400) Bad Request.



This is not an issue with Nintex, this is just the way the things are working in the background.
In my humble opinion Microsoft should rethink this limitation in SharePoint Online!

I hope that this was helpful!

Monday, 11 May 2015

Write and Get User Profile Properties in SharePoint Online with CSOM in PowerShell. The Scripts!

Last year (1st December) I published an article called Write and Get User Profile Properties in SharePoint Online with CSOM in PowerShell . In this article I demonstrated the latest capabilities that were introduced with the latest CSOM SDK. There were new APIs that made possible to work with User Profile properties in SharePoint Online(for now). I published PowerShell snippets for writing user profile properties and retrieving them. At that time I was unable to figure out how to write MultiValue properties, the snippets were just for the demo, with hard coded property and they were not very reusable.
Since this article became very popular and people were happy that they can do this from PowerShell, I decided to write generalized script for writing any Single or Multi value user profile property.
The Name(not the DisplayName) of the property to be edited is provided as script parameter. As I said the script is generalized, not tested with all available out of the box properties in SharePoint Online. Some of the properties have specific requirement for the value. For example the About Me property should be html(<br> instead new line), the Birthday should be some parsable datetime format and so on. If the format is not correct in most cases you will receive some meaningful exception from CSOM.
The script I will provide requires SharePoint Server 2013 Client Components SDK(x64) or SharePoint Online Client Components SDK(x64) that are released September 2014 or newer.
The script is written like native cmdlet, it accepts pipeline values for the AccountName, you can combine it with a CSV or simple text file that contains a list with account names, include it in your own scripts and modify it as you find for useful. Test the script to account that is not C-level manager :-) and if it works use it for bulk property updates for "live" accounts for example. 

To demonstrate how the script for writing user profile properties is working I am going to change the profile of  Anne Wallace (AnneW@spyankulov.onmicrosoft.com). She is the President of Contoso. I am going to change her Work Phone(Single Value), her Skills(Multi Value) and her Birthday(short date Single Value). See how her profile page looks like before changes.


Below are the PowerShell lines I have used to change this properties, I have made it in couple of lines in order to fit it better in the blog. Fore more examples see the Help section of the scripts.


########################## Change Work Phone ###############
.\Set-SPOUserProfileProperty.ps1 -PropertyName WorkPhone `
-AccountName 'AnneW@spyankulov.onmicrosoft.com' -Value "+44 555 555 55" `
-SPOAdminPortalUrl 'https://spyankulov-admin.sharepoint.com' `
-UserName ****@spyankulov.onmicrosoft.com -Password *******

########################## Change The Skills ###############

.\Set-SPOUserProfileProperty.ps1 -PropertyName 'SPS-Skills' `
-AccountName 'AnneW@spyankulov.onmicrosoft.com' -Value "SharePoint","PowerShell","Tech Support"-SPOAdminPortalUrl 'https://spyankulov-admin.sharepoint.com' `
-UserName ****@spyankulov.onmicrosoft.com -Password ******* -MultiValue
######################### Change The Birthday ###############                                         
.\Set-SPOUserProfileProperty.ps1 -PropertyName 'SPS-Birthday' `
-AccountName 'AnneW@spyankulov.onmicrosoft.com' -Value "5/9/1988" `
-SPOAdminPortalUrl 'https://spyankulov-admin.sharepoint.com' `
-UserName ****@spyankulov.onmicrosoft.com -Password *****


Lets see how Anne's profile looks like below, after the change.



We can see that the properties are changed accordingly. How cool is that :).

In the package you can download below I also included a script that can retrieve the user profile properties in PowerShell. Please, download the script,test,use and rate!
I am writing a fair amount of Help to my scripts, please read it.

 Download the script from: TechNet Gallery
(You may experience issue with Chrome, use Firefox/IE to open the link)

Thursday, 9 April 2015

Script to get All Webs in SharePoint Online site collection

Do you ever wanted to have a SharePoint Online cmdlet to get all webs in a site collection? Something like this can be useful if you want to see all webs and if there is something that you do not want or need. Or if you want to get all webs as original type and do further operations with them. Well as you know there is no such out of the box cmdlet in SharePoint Online Management Shell.
We however can use the dlls from SharePoint Server 2013 Client Components SDK and write our own powershell script to do that.
There however are many scripts from the community that are described as "get all webs in site collection". One of the most popular scripts that is popping out if you do some googling, is the script of Corey Roth (MVP). The script is nice but actually getting all webs in a SharePoint site collection with CSOM is a bit tricky, since we do not have (at least I do not know) a shortcut for getting really all webs, something like SPSite.AllWebs when you run powershell in on-premises.
What Cory's script is doing is to get the top level web from the client context, then get its sub webs and then it is returning the Title and the Url of the subwebs like I have shown in the screenshot below.


As you can see we are not getting much. The site I am doing this demo on is a site with two subwebs, below them we have some more subwebs, below them more and we cannot see them.
So I create a script that can output all webs in SharePoint Online site collection very similar to what SPSite.AllWebs is doing in on-premises and the output is Microsoft.SharePoint.Client.Web. See how it looks like.

All SharePoint Online Webs

As you can see the output is different. I have quite a lot of webs in my site, there is even an AppWeb.
See part of my code.


BEGIN{
function Get-SPOSubWebs{
        Param(
        [Microsoft.SharePoint.Client.ClientContext]$Context,
        [Microsoft.SharePoint.Client.Web]$RootWeb
        )


        $Webs = $RootWeb.Webs
        $Context.Load($Webs)
        $Context.ExecuteQuery()

        ForEach ($sWeb in $Webs)
        {
            Write-Output $sWeb
            Get-SPOSubWebs -RootWeb $sWeb -Context $Context
        }
    }
    Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll" | Out-Null
    Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll" | Out-Null
}
PROCESS{

    $securePassword = ConvertTo-SecureString $PassWord -AsPlainText -Force
    $spoCred = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName, $securePassword)
    $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteUrl)
    $ctx.Credentials = $spoCred

    $Web = $ctx.Web
    $ctx.Load($Web)
    $ctx.ExecuteQuery()

    Write-Output $Web

    Get-SPOSubWebs -RootWeb $Web -Context $ctx
}



All I have done is to get the root web, return it as result, get the subwebs, then get the subwebs of each subweb that is found (and so on) and then return each web as result. The key job is done by a function defined in BEGIN blok (not very pretty, but works) Get-SPOSubWebs . The function is running itself for each web found, I think that this can be defined as recursion.
At the end we have all webs in the site collection. In order to run the script you will need SharePoint Server 2013 Client Components SDK (x64) installed and optionally if you want to see the data in powershell console loaded type display template(included with the script). For more information see my article Display CSOM objects in PowerShell.

 Download the script from: Technet Gallery
(You may experien issue with Chrome, use Firefox/IE to open the link)