Tag Archives: Chen V

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

    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 !

    Microsoft MVP Virtual Conference 2015

    MVPvConf

    This is a great free event that Microsoft and the MVPs are putting on, May 14th & 15th. Join Microsoft MVPs from the Americas’ region as they share their knowledge and real-world expertise during a free event, the MVP Virtual Conference.

    The MVP Virtual Conference will showcase 95 sessions of content for IT Pros, Developers and Consumer experts designed to help you navigate life in a mobile-first, cloud-first world. Microsoft’s Corporate Vice President of Developer Platform, Steve Guggenheimer, will be on hand to deliver the opening Key Note Address.

    Why attend MVP V-Conf? The conference will have 5 tracks, IT Pro English, Dev English, Consumer English, Portuguese mixed sessions & Spanish mixed sessions, there is something for everyone!

    Come learn from the best and brightest in the tech world today. All of the sessions will all be delivered by the Americas’ Region Microsoft MVPs. These MVPs are experts who present at premiere conferences, independent community events and local user groups all over the globe.

    This is a technical conference focused on helping attendees to learn and develop skills for everything from everyday technical work to wackier weekend projects. Whether it is on the IT Pro, Dev or Consumer side of things, you can bet that the content of MVP V-Conf will be cutting edge, exciting and relevant.

    Be sure to register quickly to hold your spot and tell your friends & colleagues.

    The conference will be widely covered on social media, you can join the conversation by following @MVPAward and using the hashtag #MVPvConf.

    Start-DSCConfiguration WMF 5.0 Multiple Nodes Issue

    I am one among the many beginners in PowerShell Desired State Configuration. Recently I saw the TechNet Forum Post so thought of sharing my experience

    Error

    The WinRM client cannot process the request. The object contains an unrecognized argument: “JobId”. Verify that the spelling of the argument name is
    correct.
        + CategoryInfo          : MetadataError: (root/Microsoft/…gurationManager:String) [], CimException
        + FullyQualifiedErrorId : HRESULT 0x803381e1
        + PSComputerName        : DSC2

    How to Produce this in WMF 5.0?
    Configuration DesiredStateConfigurationDemo
    {
        Param([String[]]$ComputerName)
        Import-DscResource -ModuleName xWindowsUpdate
        Node $computerName
        {
            
            xHotFix InstallWMF5
            {
                Path = '\\DSC1\C$\DSCShare\Updates\Some.MSU'
                Id = 'KBsomeid'
                Ensure = 'Present'
            }
        }
    }
    
    DesiredStateConfigurationDemo -ComputerName DSC1 , DSC2
    Start-DSCConfiguration .\DesiredStateConfigurationDemo -wait -verbose -force

    In middle of demo I couldn’t think about anything else. Since few of my clients were eager to get advantages of DSC – So, I just applied configuration using below code – Our Own PowerShell 🙂

    'DSC1' , 'DSC2' | %{Start-DSCConfiguration .\DesiredStateConfigurationDemo -ComputerName $_ -wait -verbose -force}

     

     

    Upload High Resolution Picture in Exchange Online

    Set-UserPhoto Error – Now Solved 🙂

    Error:

     

    Proxy method PSWS:
    Request return error with following error message:
    The remote server returned an error: (413) Request Entity Too Large…
    + CategoryInfo          : NotSpecified: (:) [Set-UserPhoto], CmdletProxyException
    + FullyQualifiedErrorId : Microsoft.Exchange.Configuration.CmdletProxyException,Microsoft.Exchange.Management.RecipientTasks.SetUserPhoto

    More Information:

    Logged an issue in Connect

    Refer to the Workaround posted which might have helped some. Unfortunately it didn’t work in WMF 4.0 at all. Recently, I thought of testing the case in Connect to explore some foot prints.

    Reason:

    The reason for the error is quite simple – Exchange Online will not allow images greater than 10 KB and 96 x 96

     Solution:

    I tried to upload an image using the below PowerShell Code

    Set-UserPhoto "Chendrayan" -PictureData ([System.IO.File]::ReadAllBytes("C:\Temp\TestImage.jpg")) -Confirm:$false
    Image Information:
    2015-04-14_13-38-27
    Conclusion:

    Updated TechNet Article