All posts by Chen V

About Chen V

I am a SharePoint 2010 IT Pro. My area of interests are Workflows, Automation, Office 365 , Identity and Access Management, PowerShell and Windows Servers.

Part 1 – Getting Started With CSOM | SharePoint Online | PowerShell

Introduction

CSOM is no more a secret for SharePoint IT Professionals and developers. MS released a new version of SharePoint Online CSOM and it’s available in Nuget which makes our life bit easier. All we need is to run Install-Package “Microsoft.SharePointOnline.CSOM” -Verbose 2016-05-24_14-32-44 We can use VS Community Edition for building binary modules or use Visual Studio Code editor to build scripts or script modules. After installing the packages I found my Nuget folder under the path “C:\Program Files\NuGet\” and the required DLL are located in “C:\Programfiles\NuGet\Packages\Microsoft.SharePointOnline.CSOM.16.1.5026.1200\lib\net45“. The DLL files are listed below. This is a part 1 so we will just use Two DLL’s for now to get very basic information.

  • Microsoft.Office.Client.Policy.dll
  • Microsoft.Office.Client.TranslationServices.dll
  • Microsoft.Office.SharePoint.Tools.dll
  • Microsoft.Online.SharePoint.Client.Tenant.dll
  • Microsoft.ProjectServer.Client.dll
  • Microsoft.SharePoint.Client.dll
  • Microsoft.SharePoint.Client.DocumentManagement.dll
  • Microsoft.SharePoint.Client.Publishing.dll
  • Microsoft.SharePoint.Client.Runtime.dll
  • Microsoft.SharePoint.Client.Runtime.Windows.dll
  • Microsoft.SharePoint.Client.Search.Applications.dll
  • Microsoft.SharePoint.Client.Search.dll
  • Microsoft.SharePoint.Client.Taxonomy.dll
  • Microsoft.SharePoint.Client.UserProfiles.dll
  • Microsoft.SharePoint.Client.WorkflowServices.dll

Using Import-Module “Path to DLL” I loaded the binaries to my PowerShell session so the Visual Studio Code will help us in intellisense. Thus, we do code at ease! The below function will establish the connection to SharePoint Online at tenant level. To understand the SharePoint Online site hierarchy refer the below image (Just as a References)a30d1b67-e6ff-4fea-bc44-5f00ba7fcc33 PowerShell Code to Connect With SharePoint Online Tenant

function Connect-xSPOTenant {
    [CmdletBinding()]
    param(
        [uri]
        $Url,
        
        [System.Management.Automation.CredentialAttribute()]
        $Credential
    )
    
    begin {
    }
    
    process {
        if($Script:Credential -eq $null) {
            $Credentials = Get-Credential -Message "SharePoint Online Credential" 
            $Script:Credential = [Microsoft.SharePoint.Client.SharePointOnlineCredentials]::new($Credentials.UserName , $Credentials.Password)
            $SPOClientContext = [Microsoft.SharePoint.Client.ClientContext]::new($Url);
            $SPOClientContext.Credentials = $Credential;
        }
        
    }
    
    end {
    }
}

Just halt here! We didn’t use the conditional scopes to validate. If you pass incorrect values to variables no exception will be thrown. If you are planning to commit this in Binary module then refer this blog post. After establishing the connection we can query different site collections to get list information. For example, refer the following function which connects to one of the Site Collections and retrieve List Information.

function Get-xSPOList {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [uri]
        $Url
    )
    
    begin {
    }
    
    process {
        $SPOClientContext = [Microsoft.SharePoint.Client.ClientContext]::new($Url)
        $SPOClientContext.Credentials = $Credential
        $ListCollection = $SPOClientContext.Web.Lists
        $SPOClientContext.Load($ListCollection)
        $SPOClientContext.ExecuteQuery()
        $SPOClientContext.Dispose()
        foreach($List in $ListCollection) {
            $List | Select Title , ItemCount
        }
    }
    
    end {
    }
}

The below figure illustrates the output.
2016-05-24_15-39-18 In our next blog we will add some more functions and show case creating modules.

SharePoint Online PowerShell Tip: Customize Your Script to Meet Client Requirements

sharepoint

Introduction

Recently I was building a PowerShell Module for one of our customers SharePoint farm. Of course, it’s completely customized. The SharePoint Online farm has multiple site collections with different languages. Any organization located in across geographical area needs this set up. So, in one our cmdlet we retrieve site information including language (In My Lab all the site are in en-US but the code works for other LocaleID ).

Get Started

Though, I was in middle of building binary module, I just paused it for a while and used SharePoint Online Management module for testing.
1Good let’s import the module to meet our need

Import-Module -Name Microsoft.Online.SharePoint.PowerShell -Verbose

2 Well, module is imported with warning due to unapproved verbs and that’s not going to impact. Now, let’s connect to SharePoint Tenant by using the below cmdlet

Connect-SPOService https://contoso-admin.SharePoint.com -Credential "Chendrayan@Contoso.onmicrosoft.com"

and by using the Cmdlet Get-SPOSite we can get the information we required like shown below

Get-SPOSite | Select StorageQuota , LocaleID

3 I just selected two properties to avoid exposing domain information. 1033 is LCID of en-US (English United States). Let’s play with our native Windows PowerShell

[System.Globalization.CultureInfo]::GetCultureInfo

4
which accepts three overloads let’s grab the first one to show the display name of the LocaleID (A property (output) of Get-SPOSite)

[System.Globalization.CultureInfo]::GetCultureInfo(1033)

5 Fine, we know the way to do! Let’s include in our Get-SPOSite cmdlet to get desired output.

Get-SPOSite | Select StorageQuota , @{Name="Language" ; Expression = {([System.Globalization.CultureInfo]::GetCultureInfo([int]$_.LocaleId))}}

6 or we can get display name by executing the below code

Get-SPOSite | Select StorageQuota , @{Name="Language" ; Expression = {([System.Globalization.CultureInfo]::GetCultureInfo([int]$_.LocaleId)).DisplayName}}

7 Clear, in the binary module I added the below piece of code to get desired output for our content managers

obj.Properties.Add(new PSNoteProperty("Language", System.Globalization.CultureInfo.GetCultureInfo((int)oWeb.Language).DisplayName));

and the sample code is below

using System;
using System.Collections.Generic;
using System.Management.Automation;
using Microsoft.SharePoint.Client;
namespace xSharePointOnline
{
    [Cmdlet(VerbsCommon.Get, "xSPOSite")]
    public class GetxSPOSite : PSCmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
        public Uri Url
        {
            get;
            set;
        }

        [Parameter(Mandatory = false)]
        [Credential]
        public PSCredential SPOCredential
        {
            get;
            set;
        }
        protected override void ProcessRecord()
        {
            using (ClientContext SPOClientContext = new ClientContext(Url))
            {
                SPOClientContext.Credentials = new SharePointOnlineCredentials(SPOCredential.UserName, SPOCredential.Password);
                WebCollection oWebCollection = SPOClientContext.Web.Webs;
                IEnumerable<Web> oWebs = SPOClientContext.LoadQuery(oWebCollection);
                SPOClientContext.ExecuteQuery();
                PSObject obj = new PSObject();
                foreach (var oWeb in oWebs)
                {
                    obj.Properties.Add(new PSNoteProperty("Title", oWeb.Title));
                    obj.Properties.Add(new PSNoteProperty("Created", oWeb.Created));
                    obj.Properties.Add(new PSNoteProperty("NoCrawl", oWeb.NoCrawl));
                    obj.Properties.Add(new PSNoteProperty("Language", System.Globalization.CultureInfo.GetCultureInfo((int)oWeb.Language).DisplayName));
                    WriteObject(obj, true);
                }
            }
        }
    }
}

Note: I have included help and format files in this code!

VS Binary Module Tip: PowerShell 5.0 SDK Reference Assemblies – Nuget

PowerShell 5.0 SDK Reference Assemblies is available in NuGet. If you are building binary modules you can add the references from Visual Studio just by executing the below command in NPM Console

Install-Package Microsoft.PowerShell.5.ReferenceAssemblies -Verbose

In the NPM UI we can get it in one go – By Clicking Install
1
Cool, its easy to add the reference to our project and start building the binary module easily. When we build cmdlet we need to run and test the functionality and each time we execute it’s necessary to close the PowerShell Console Host or ISE host if the binary module is loaded and we can’t rebuild the solution. It will end up in Dll in use error. We can easily over come this by following the below steps.

  • Right Click the Solution from Solution Explorer.
  • Choose Properties and in the Right Pane Select Debug
  • Click Select External Program. Navigate to “C:\Windows\System32\WindowsPowerShell\v1.0\”.
  • Select PowerShell.exe
  • Select the option Enable native code debugging

3
So after building the code simple click “Start” in Visual Studio which open the PowerShell Console.
Additionally, we can load our module with the help of Command line arguments.

-NoExit -Command "Import-Module .\PowerShellDemo.dll"

4
Enjoy PowerShell 🙂 🙂 🙂

PowerShell Tip: Extract Images from DOCX File

Summary

It’s an immense pleasure to explore $objects! We do IT Automation tasks using C# or PowerShell. It depends on the requirement and business needs. Recently our team was engaged to extract images from the DOCX files. It’s possible by simply renaming it to ZIP and extract to get the images folder which is not optimal solution at our client environment. So, we used OPEN XML SDK 2.5 (Supported for Office 2016) to accomplish the task.

Requirement

Extract all the images from the given DOCX File. Example Get-Image -FilePath “C:\Temp\Document.Docx”

Solution

  • OPEN XML SDK 2.5
  • Windows PowerShell
  • Visual Studio 2015 (C# Class Library – Binary Module)
  • PowerShell Code

    #using namespace DocumentFormat.OpenXml;            
    [void][System.Reflection.Assembly]::LoadWithPartialName('DocumentFormat.OpenXML')            
    Function Get-Image            
    {            
        Param            
        (            
            [Parameter(Mandatory,            
                       ValueFromPipeline=$true,            
                       ValueFromPipelineByPropertyName=$true,            
                       Position=0,            
                       HelpMessage="Please Enter the Full Path")]            
            [string]            
            $FullName            
        )            
        Process            
        {            
            $Document = [DocumentFormat.OpenXml.Packaging.WordprocessingDocument]::Open($FullName,$false)            
            foreach($Image in $Document.MainDocumentPart.ImageParts)            
            {            
                 [uri]$Uri = $Image.Uri            
                 $ImageFileName = $Uri.ToString().Split("/").Where({$_},'Last',1)            
                 [System.IO.Stream]$Stream = $Document.Package.GetPart($Uri).GetStream()            
                 [System.Drawing.Bitmap]$BitMap = [System.Drawing.Bitmap]::new($Stream)            
                 $BitMap.Save("C:\Temp\Images\" + $ImageFileName)            
            }            
        }            
                   
    }            
                
    Get-Item 'C:\Temp\Demo.Docx' | Get-Image

    Cool, here is the C# Binary cmdlet code to do the same

    [Cmdlet(VerbsCommon.Get,"Image")]
    public class GetImage : Cmdlet
    {
        [Parameter(Mandatory = true,
                   Position = 0)]
        public string FilePath;
        protected override void ProcessRecord()
        {
            //base.ProcessRecord();
            FileStream filestream = System.IO.File.OpenRead(FilePath);
            FileInfo fileinfo = new FileInfo(FilePath);
            WordprocessingDocument document = WordprocessingDocument.Open(filestream, false);
            var images = document.MainDocumentPart.ImageParts;
            foreach(ImagePart image in images)
            {
                Uri uri = image.Uri;
                string mainimage = uri.ToString().Split('/').Last();
                Stream imgstream = document.Package.GetPart(uri).GetStream();
                Bitmap bitmapImg = new Bitmap(imgstream);
                bitmapImg.Save(@"C:\Temp\" + mainimage);
            }
        }
    }

    Conclusion

    We prefer to pick a tool which costs less to deliver the solution. So, at my client place we opted for PowerShell to achieve few automation task with respect to Data Manipulation. To loop through multiple files just used the below snippet:

    Get-ChildItem 'C:\Temp\ProjectFolder\' | Get-Image

    Enjoy PowerShell!

    Note: You can modify the filename of the image as required. In our case it needs to be the same because its customized.

    Tip: Create New List in SharePoint Online Using Exception Handling Scope and Set Properties | PowerShell | CSOM

    Summary

    While presenting about SharePoint Online Client Side Object Model a question popped up “Hey! How can I create a new list if it’s not exist and change properties if exists?”. Not a difficult one. However, we need to build codes with best practise suggested by Microsoft. In short answer is available here.

    Requirement

    • Create a List if it’s not existing.
    • Change Properties if it exists.
    • Allow to create multiple lists in one run
    • Allow users to choose the list template.

    Solution

    Build a binary cmdlet using C# and meet your needs by parameterizing the code. Look at the code below.

    using System;
    using System.Management.Automation;
    using Microsoft.SharePoint.Client;
    namespace xSharePointOnline
    {
        [Cmdlet(VerbsCommon.New, "SPOList")]
        public class NewSPOList : PSCmdlet
        {
            [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
            public Uri SPOurl;
     
            [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ValueFromPipeline = true)]
            public string SPOListName;
     
            [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
            public ListTemplateType SPOListTemplateType;
     
            [Parameter(Mandatory = true)]
            [Credential]
            public PSCredential SPOCredential;
     
            protected override void ProcessRecord()
            {
                base.ProcessRecord();
                using (ClientContext SPOClientContext = new ClientContext(SPOurl))
                {
                    SPOClientContext.Credentials = new SharePointOnlineCredentials(SPOCredential.UserName, SPOCredential.Password);
                    ExceptionHandlingScope Scope = new ExceptionHandlingScope(SPOClientContext);
                    using (Scope.StartScope())
                    {
                        using (Scope.StartTry())
                        {
                            List oList = SPOClientContext.Web.Lists.GetByTitle(SPOListName);
                            oList.Update();
                        }
                        using (Scope.StartCatch())
                        {
                            ListCreationInformation oListInformation = new ListCreationInformation();
                            oListInformation.Title = SPOListName;
                            oListInformation.TemplateType = (int)SPOListTemplateType;
                            List oList = SPOClientContext.Web.Lists.Add(oListInformation);
                        }
                        using (Scope.StartFinally())
                        {
                            List oList = SPOClientContext.Web.Lists.GetByTitle(SPOListName);
                            oList.Hidden = true;
                            oList.Description = "PowerShell Rocks!";
                        }
                    }
                    SPOClientContext.ExecuteQuery();
                }
            }
        }
    }

    How to use it?

    • Create a C# Class Library
    • Copy and paste the code. If required change the parameters as required.
    • Build it to get the DLL (Binary)
    • Using Import-Module load the binary DLL E.G. Import-Module C:\Location\Solution.DLL
    • Run the cmdlet New-SPOList -SPOUrl -SPOListName -SPOListTemplateType -SPOCredential

    Here the SPOListTemplate populate the enum values of List Template – So, we can choose the one we need by tab completion

    How it works?

    We have used Exception handling scope which makes one call to the server and that means a lot of performance improvement. All three Try, catch and finally executes at one time.
    Try
    This code simply checks the existence of the list and if exists it will update the hidden and description properties.
    Catch
    If try catch throws exception it will create a new list – If in case of the list not existing.
    Finally
    Update the lists properties.

    Screen Shot

    2746.123

    SharePoint Online PowerShell Tip: Conditional Scope – Binary Module

    SharePoint Online PowerShell Tip to throw exception while connecting to Tenant.

    Introduction

    Recently one of our customer requested us to share a code which should throw an exception while connecting to SharePoint Online if in case of credential invalid or not connecting to tenant site. Well, they have a binary module delivered by some suppliers which is pretty straight forward and simple. If all set to good code will execute if not exception thrown in other cmdlet. The module they use has bunch of cmdlets which are more or less mock up of SharePoint Online cmdlets. Connect-SPOService throws exception if credential are invalid and they need custom error message for the custom built cmdlet Connect-SPOTenant

    Requirement

    • Connect-SPOTenant : The sign-in name or password does not match one in the Microsoft account system
    • Connect-SPOTenant : Current site is not a tenant administration site.

    PowerShell Code

    Below is the PowerShell code shared by our client.

    function global:Connect-SPOSite {
     [CmdletBinding()]
     param (
     [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
     $Url
     )
     
     begin {
     [System.Reflection.Assembly]::LoadFile("C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll") | Out-Null
     [System.Reflection.Assembly]::LoadFile("C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll") | Out-Null
     }
     process {
     if ($global:spoCred -eq $null) {
     $cred = Get-Credential -Message "Enter your credentials for SharePoint Online:"
     $global:spoCred = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($cred.UserName, $cred.Password)
     }
     $ctx = New-Object Microsoft.SharePoint.Client.ClientContext $Url
     $ctx.Credentials = $spoCred
     
     if (!$ctx.ServerObjectIsNull.Value) {
     Write-Host "Connected to site: '$Url'" -ForegroundColor Green
     }
     return $ctx
     }
     end {
     }
    }

    Solution

    Indeed, the above code will not throw any exception because the ClientContext Object is instantiated and will not validate the credential but returns the ClientContext Object as output. More over Microsoft.SharePoint.Client.ClientContext doesn’t have ServerObjectIsNull property. No matter we supply wrong password the host shows Connected to Site!

    Conditional Scope

    C# code with conditional scope is below. This is just a demo so we haven’t used any scope methods or properties

    using System;
    using System.Management.Automation;
    using Microsoft.SharePoint.Client;
    using Microsoft.Online.SharePoint.TenantAdministration;
    namespace xSharePointOnline
    {
     [Cmdlet(VerbsCommunications.Connect, "SPOTenant")]
     
     public class ConnectSPOTenant : PSCmdlet
     {
     [Parameter()]
     public Uri SPOUrl;
     
     [Parameter()]
     [Credential]
     public PSCredential SPOCredential;
     protected override void ProcessRecord()
     {
     using (ClientContext SPOClientContext = new ClientContext(SPOUrl))
     {
     SPOClientContext.Credentials = new SharePointOnlineCredentials(SPOCredential.UserName, SPOCredential.Password);
     Tenant oTenant = new Tenant(SPOClientContext);
     ConditionalScope Scope = new ConditionalScope(SPOClientContext, () => oTenant.ServerObjectIsNull.Value != true);
     SPOClientContext.ExecuteQuery();
     }
     }
     }
    }

    Screen Shot

    1

    2

    Bulk Import Folders and Files in SharePoint Document Library | Part 1

    This noon I was on a call with SharePoint Development team to discuss few activities which is pretty straight forward approach for SharePoint IT Professionals. However, we need to inform about the activity we carry out in the farm to both IT Pro’s and developers. By doing this we simply avoid confusion. And, as always in any farm bulk upload of files and folders may cause one or other issues like threshold, performance high risk of uploading vulnerable files etc.

    Our client came with 5000 odd folders which has almost 9000 files approximately and the requirement is to push it to document folder. For example, files underneath the folder should be organized as a single folder and files needs to be stored in it. The appended a request “Share the cmdlet so we can add more in future!”

    Everything is easy if we build one! There is no out of the box solution. We can’t drag and drop easily as we know the limitations in document library. So, I built a small cmdlet in Visual C# which serves the purpose.

    The folder structure is very simple, Main Folder which contains N number of folder and each folder may have some files. No Folders underneath the folders.

    using System;
    using System.IO;
    using System.Management.Automation;
    using Microsoft.SharePoint.Client;
    namespace SP2010Admin
    {
        [Cmdlet(VerbsData.Import, "Document")]
        public class ImportDocument : Cmdlet
        {
            [Parameter(Mandatory = true)]
            public Uri SPUrl;
            [Parameter(Mandatory = true)]
            public string SPDocLib;
            [Parameter(Mandatory = true,
                       ValueFromPipeline = true,
                       ValueFromPipelineByPropertyName = true)]
            public string FolderPath;
            protected override void ProcessRecord()
            {
                ClientContext ctx = new ClientContext(SPUrl);
                Web web = ctx.Web;
                ctx.Load(web);
                ctx.ExecuteQuery();
    
                List DocLib = web.Lists.GetByTitle(SPDocLib);
                string[] filenames = Directory.GetFiles(FolderPath);
                DirectoryInfo dInfo = new DirectoryInfo(FolderPath);
                FolderCollection folders = DocLib.RootFolder.Folders;
                ctx.Load(folders);
                ctx.ExecuteQuery();
                bool exists = false;
                foreach (Folder efolder in folders)
                {
                    if (efolder.Name.Equals(dInfo.Name))
                    {
                        foreach (string filename in filenames)
                        {
                            string[] names = filename.Split('\\');
                            WriteObject("Existing " + filename);
                            byte[] data = System.IO.File.ReadAllBytes(filename);
                            FileCreationInformation fci = new FileCreationInformation();
                            fci.Content = data;
                            fci.Url = names[names.Length - 1];
                            efolder.Files.Add(fci);
                            exists = true;
                        }
                    }
                }
                if (!exists)
                {
                    Folder tFolder = folders.Add(dInfo.Name);
                    foreach (string filename in filenames)
                    {
                        string[] names = filename.Split('\\');
                        WriteObject("Uploading file " + filename);
                        byte[] data = System.IO.File.ReadAllBytes(filename);
                        FileCreationInformation fci = new FileCreationInformation();
                        fci.Content = data;
                        fci.Url = names[names.Length - 1];
                        tFolder.Files.Add(fci);
                        exists = true;
                    }
                    ctx.ExecuteQuery();
                }
            }
        }
    }
    

    Usage:

    Import-Module "c:\users\ChenV\documents\visual studio 2015\Projects\SP2010Admin\SP2010Admin\bin\Debug\SP2010Admin.dll" -Verbose
    (Get-ChildItem "C:\Users\ChenV\ProjectFolder\Test\" -Recurse).FullName | Import-Document -SPUrl http://www.contoso.com/web -SPDocLib "Doc Lib" -Verbose

    In Part 2 we will discuss about the best practice and will enhance this code much better! Enjoy PowerShell 🙂 🙂 🙂

    Thanks to Author Rashu, Rahul in TechNet Wiki

    Get-SPOAppInfo | SharePoint Online | PowerShell

    During SharePoint Online discussion a question popped up “How to get all installed application information in SharePoint Online?” a simple answer is Get-SPOAppInfo cmdlet! But, wait we are partially correct but read this documentation https://technet.microsoft.com/en-us/library/fp161398.aspx. The below image illustrates the parameters of Get-SPOAppInfo both set to be false and it’s not $TRUE – This cmdlet needs either Name or Product ID! So, we can’t use this cmdlet to retrieve all the apps installed in the given Tenant!

    Issue:

    So we can Get installed apps information by using below

    Enough! We are not going to use this. Let’s use Client Side Object Model in PowerShell and solve the issue.

    Output

    Code:

    Import-Module C:\SPPowerKit\Microsoft.SharePoint.Client.dll
    Import-Module C:\SPPowerKit\Microsoft.SharePoint.Client.Runtime.dll
    #Import-Module C:\SPPowerKit\Microsoft.SharePoint.Client.UserProfiles.dll
    Import-Module C:\SPPowerKit\Microsoft.Online.SharePoint.Client.Tenant.dll
    function Get-SPOAppInformation
    {
        param(
        [Parameter(Mandatory=$true)]
        [string]$SPOUrl,
    
        [Parameter(Mandatory=$true)]
        [System.Management.Automation.CredentialAttribute()]$SPOCredential
        )
    
        $ClientContext = [Microsoft.SharePoint.Client.ClientContext]::new($SPOUrl)
        $ClientContext.Credentials = [Microsoft.SharePoint.Client.SharePointOnlineCredentials]::new($SPOCredential.UserName,$SPOCredential.Password)
        $Tenant = New-Object Microsoft.Online.SharePoint.TenantAdministration.Tenant -ArgumentList $ClientContext
        $Tenant.Context.Load($Tenant)
        $Tenant.Context.ExecuteQuery()
        $Appinfo = $Tenant.GetAppInfoByName([string]::Empty)
        $Tenant.Context.Load($Appinfo)
        $Tenant.Context.ExecuteQuery()
        $Appinfo
        $ClientContext.Dispose()
    }
    
    Get-SPOAppInformation -SPOUrl "https://contoso-admin.sharepoint.com" -SPOCredential "TenantAdmin@contoso.onmicrosoft.com"

    Bit more to organize it, we can use PSObject!

    Import-Module C:\SPPowerKit\Microsoft.SharePoint.Client.dll
    Import-Module C:\SPPowerKit\Microsoft.SharePoint.Client.Runtime.dll
    #Import-Module C:\SPPowerKit\Microsoft.SharePoint.Client.UserProfiles.dll
    Import-Module C:\SPPowerKit\Microsoft.Online.SharePoint.Client.Tenant.dll
    function Get-SPOAppInformation
    {
        param(
        [Parameter(Mandatory=$true)]
        [string]$SPOUrl,
    
        [Parameter(Mandatory=$true)]
        [System.Management.Automation.CredentialAttribute()]$SPOCredential
        )
    
        $ClientContext = [Microsoft.SharePoint.Client.ClientContext]::new($SPOUrl)
        $ClientContext.Credentials = [Microsoft.SharePoint.Client.SharePointOnlineCredentials]::new($SPOCredential.UserName,$SPOCredential.Password)
        $Tenant = New-Object Microsoft.Online.SharePoint.TenantAdministration.Tenant -ArgumentList $ClientContext
        $Tenant.Context.Load($Tenant)
        $Tenant.Context.ExecuteQuery()
        $Appinfocollection = $Tenant.GetAppInfoByName([string]::Empty)
        $Tenant.Context.Load($Appinfocollection)
        $Tenant.Context.ExecuteQuery()
        foreach($Apps in $Appinfocollection)
        {
            $Results = New-Object psobject -Property ([Ordered]@{
            Name = $Apps.Name
            ProductID = $Apps.ProductID
            Source = $Apps.Source
            })
            $Results
        }
        $ClientContext.Dispose()
    }
    
    Get-SPOAppInformation -SPOUrl "https://contoso-admin.sharepoint.com" -SPOCredential "TenantAdmin@contoso.onmicrosoft.com"

    2016-01-20_10-22-29

    SharePoint 2013 Foundation | Central Administration | File Not Found

    Yet another day with SharePoint 2013 foundation troubleshooting. I was building a SharePoint 2013 Foundation farm on Windows 2008 R2 Standard with Service Pack 1. The same box has SQL Server 2012! Lesson learnt “Do not do installation on the fly!” J

    I was very much sure the box I chose for installing SharePoint 2013 foundation meets the software and hardware requirements. Indeed, scalability and performance was not considered at this point of time.

    First and foremost, Symantec Antivirus blocked the configuration wizard at step 4.

    Solution: Uninstalled the Symantec Antivirus Software and fixed the access issue.

    Then we moved one step ahead and completed the Set up and was bit happy to see the below window

    Then, while launching Central Admin we got the glossy and silky error J

    Sorry, Something Went Wrong

    File Not Found

    Followed the below steps

    Turned on Custom Error mode – No luck! No clue why including stack trace $true browser didn’t show the error message

    Removed Central Admin and Recreated new one – No Luck!

    Un-Provisioned the Central Admin and Central Admin Database using below code – No Luck!

    Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction 0
    Function Remove-CentralAdmin
    {
        $CA = Get-SPWebApplication -IncludeCentralAdministration | ? {$_.IsAdministrationWebApplication -eq $true}
        if($CA -eq $null)
        {
            "Central Admnistration is missing!"
        }
        foreach($App in $CA)
        {
            $App.UnprovisionGlobally($true)
            foreach($DB in $App.ContentDatabases)
            {
                $DB.Unprovision()
            }
            $App.Delete()
        }
    }
    
    Remove-CentralAdmin

    Checked the IIS host header and virtual directory settings! All set to good!

    Finally, the below simple snippet saved my day!

    Get-SPEventLog -StartTime <DateTimeStamp> -EndTime <EndTimeStamp> | ? { $_.Correlation -eq "CorrelationID"
    $_.Level -eq 'Unexpected'} | Select Message

    Message:

    System.IO.FileNotFoundException: Could not load file or assembly ‘MySql.Web.v20, Version=6.9.3.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d’ or one of its dependencies.

    The system cannot find the file specified.

    Simply Awesome!

    Again, I modified the Web.Config File custom errors and stack trace, restarted the IISAdmin service and bounced the server – Just to keep things neat and clean! Bingo, it worked – I got the same error message


    The line number 287 in the system machine configuration file is below

    <providers>

    Line 287: <add name=”MySqlSiteMapProvider” type=”MySql.Web.SiteMap.MySqlSiteMapProvider, MySql.Web.v20, Version=6.9.3.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d” connectionStringName=”LocalMySqlServer” applicationName=”/” />

    </providers>

    Solution: Commented this line!

    <providers>

    <!– <add name=”MySqlSiteMapProvider” type=”MySql.Web.SiteMap.MySqlSiteMapProvider, MySql.Web.v20, Version=6.9.3.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d” connectionStringName=”LocalMySqlServer” applicationName=”/” /> –>

    </providers>

    Now, the Central Admin opened up fine J

    Keep SharePointing and Enjoy PowerShell !

    Converting JSON Time Format in SharePoint Document Library Metadata


    The SharePoint farm has a Document Library and a List which are linked for some requirements, while querying information we received some data which are not in proper format. Challenge is to split, remove characters and count the total which is little tedious

    Example- The Field3 in CSV yields [“\/Date(1444995273796)\/”,”\/Date(1444996968515)\/”]

    It’s a JSON format and the time format is EPOCH (UNIX format) – With Reference to the link MSDN My advice to customer was to use below code snippet to break the mystery!

    To get the count: (Unfortunately, this is what customer need!) – No Fun in it because this kills the actual flavor of SharePoint development and data.

    (@"
    ["\/Date(1444995273796)\/","\/Date(1444996968515)\/"]
    "@ | ConvertFrom-Json).Count
    
    #Output
    #2

    The reason for the SPContext Code by developer is unknown. So here comes the ConvertFrom-JSON power in Windows PowerShell Version 3+ 🙂 🙂 🙂

    To get the information about the data in JSON: [We will deep dive in Part 2 of this Blog]

    @"
    ["\/Date(1444995273796)\/","\/Date(1444996968515)\/"]
    "@ | ConvertFrom-Json
    
    #Output
    
    #Friday, October 16, 2015 11:34:33 AM
    #Friday, October 16, 2015 12:02:48 PM

    The file which is generated as report is done using C# and CSOM – So, in next blog post (Part 2) we will cover Binary Module using C# Class Library, PowerShell Module, String Manipulation and Data consolidation.

    Enjoy PowerShell !