Archive

Archive for August, 2013

Listing all scopes in a Workflow Manager instance using PowerShell

August 12, 2013 4 comments

One of the pains I have encountered with setting up the Workflow Manager for use with SharePoint is that there is no clear visibility into the scopes that are currently on the Workflow Manager farm. It was hard to understand what scopes were already created, which of those were active, what the parent-child hierarchical relationships among those were and what applications were connected to those scopes.

So I tried to figure out if there was a way through OM code to get to this information or at least as much of it as I can get to without spending a whole lot of time and research. And below is what I could whip up.

param([parameter(mandatory=$true)][string]$Endpoint, [string]$Path = "/", [string]$Parent = "None")

[Reflection.Assembly]::Load("Microsoft.Workflow.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35") | Out-Null
Write-Output ""

function WriteScopeInfo([string]$scopeUri, [string]$path, [string]$parent) {
    $scopePath = $scopeUri + $path
    $client = New-Object -TypeName "Microsoft.Workflow.Client.WorkflowManagementClient" -ArgumentList $scopePath
    $manager = $client.CurrentScope
    $scope = $manager.Get()

    Write-Output "Name: `t`t`t$($scope.DisplayName)"
    Write-Output "Path: `t`t`t$($scope.Path)"
    Write-Output "Status: `t`t$($scope.Status.ToString())"
    Write-Output "User Comments: `t$($scope.UserComments)"
    Write-Output "Parent: `t`t$parent"
    Write-Output "Children: `t`t$($scope.ChildScopeCount)"
    Write-Output ""

    if ($scope.ChildScopeCount -gt 0) {
        $manager.GetChildScopes() | % { WriteScopeInfo $scopeUri $_.Path $scope.Path }
    }
}

WriteScopeInfo $Endpoint $Path $Parent

Note that the script requires 3 inputs, one of which is mandatory. The Endpoint parameter requires the URL to the workflow endpoint. This will likely be a web application on port 12290 or 12291 depending on whether you set up the Workflow Manager farm to just use HTTPS (which is the default) or also HTTP. Please refer to this post if you are not sure how to set up the Workflow Manager farm.

The other two parameters are just used to kick things off for the scope discovery function. The first called Path is used to determine the node in the scope tree that you want to begin with. By default, this assumes the value “/” representing the root scope on the farm. But if I know of another scope somewhere down the tree and the branches and spring thenceforth are the only ones I am concerned with, I would pass it so – “/myscope/subscope”. The second called Parent is only required to identify the parent of the scope node that I am starting at. It is not really required and assumes the value “None” by default.

When run the script produces an output as shown below. As you can see, I am providing the endpoint URL of the Workflow Manager instance installed on my machine.

clip_image001

For those that are unable to use PowerShell, here is the C# version of the same thing.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Workflow.Client;

namespace WorkflowClient
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string workflowEndpoint = string.Empty;
                if (args.Length < 1)
                {
                    Console.Write("Provide workflow endpoint URL: ");
                    workflowEndpoint = Console.ReadLine();
                }
                else
                {
                    workflowEndpoint = args[0];
                }
                Console.WriteLine("");
                WriteScopeInfo(workflowEndpoint, "/", "None");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + ex.StackTrace);
            }
            finally
            {
                Console.WriteLine("Press any key to end...");
                Console.ReadKey(true);
            }
        }

        private static void WriteScopeInfo(string scopeUri, string path, string parent)
        {
            try
            {
                WorkflowManagementClient client = new WorkflowManagementClient(scopeUri + path);
                ScopeManager manager = client.CurrentScope;
                ScopeDescription scope = manager.Get();

                Console.WriteLine("Name: " + scope.DisplayName);
                Console.WriteLine("Path: " + scope.Path);
                Console.WriteLine("Status: " + scope.Status.ToString());
                Console.WriteLine("User Comments: " + scope.UserComments);
                Console.WriteLine("Parent: " + parent);
                Console.WriteLine("Children: " + scope.ChildScopeCount.ToString());
                Console.WriteLine("");

                if (scope.ChildScopeCount > 0)
                {
                    Collection<ScopeDescription> children = manager.GetChildScopes();
                    foreach (ScopeDescription child in children)
                    {
                        WriteScopeInfo(scopeUri, child.Path, scope.Path);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + ex.StackTrace);
            }
        }
    }
}

I hope to be able to use this to keep things clean/sane when I have more applications using my workflow farm and needing more scopes and child scopes.

Managing Quota Templates on SharePoint through PowerShell

August 10, 2013 3 comments

Quota templates are a seldom used but practically important feature in SharePoint. Important because it allows administrators to keep tabs on what sites can consume what level of storage without having to keep close watch on them. Defining a quota template with a high water mark (for warning when its reached) and a maximum level is the first step to this. Depending upon use, an administrator may have to define a large number of quota templates but doing this through the UI in SharePoint Central Administration is tedious.

clip_image001

One of these forms needs to be filled out for each template and it could get tiresome if there are tens of templates to create or edit. The easier way to do this? PowerShell.

The files in the archive posted here are PowerShell scripts that will allow an administrator to get a quick view of all available quota templates in XML and then allow the same XML file to accrue additions, modifications and changes pertaining to deletions that can be applied back to the set.

The following need to be ensured before running the scripts

  1. Run the scripts locally using the SharePoint Management Shell on a computer where SharePoint Server 2013 is installed and configured.
  2. Run the scripts as a farm administrator with permissions to alter quota templates. If the user is able to use the Central Administration UI to manage quota templates, then the user will be able to run these scripts.
  3. If there are permission issues with executing the script, ensure Add-SPShellAdmin is run for the user running the script.
  4. Use Set-ExecutionPolicy to temporarily tone the restrictions down if the shell complains about the script not being signed.

GetQuotaTemplates.ps1

##########################################
# Script:   GetQuotaTemplates.ps1
# Function: Retrieves all quota templates
#           defined on the farm in XML
# Author:   Lenny Ankireddi (2013)
##########################################

function Get-SPQuotaTemplates
{
    # Prepare XML structure
    $xmlText =
@"
<?xml version="1.0" encoding="UTF-8" ?>
<QuotaTemplates AsOn="">
    <QuotaTemplate Name="Sample" Action="None|Add|Edit|Delete" StorageMaxLevelInMB="0" StorageWarnLevelInMB="0" UserCodeMaxLevel="0" UserCodeWarnLevel="0" />
</QuotaTemplates>
"@

    # Read the XML string into an object
    $xmlDoc = [xml]$xmlText

    # Read the sample element
    $sampleQuotaTemplate = @($xmlDoc.QuotaTemplates.QuotaTemplate)[0]

    # Get a reference to the content web service
    $service = [Microsoft.SharePoint.Administration.SPWebService]::ContentService

    # Iterate through the list of quota templates
    $service.QuotaTemplates | ForEach-Object {
        # Clone the sample element
        $newQuotaTemplate = $sampleQuotaTemplate.Clone()

        # Set the attributes of the element with the quota template properties
        $newQuotaTemplate.Name = $_.Name
        $newQuotaTemplate.Action = "None"
        $newQuotaTemplate.StorageMaxLevelInMB = [string]($_.StorageMaximumLevel / 1024 / 1024)
        $newQuotaTemplate.StorageWarnLevelInMB = [string]($_.StorageWarningLevel / 1024 / 1024)
        $newQuotaTemplate.UserCodeMaxLevel = [string]$_.UserCodeMaximumLevel
        $newQuotaTemplate.UserCodeWarnLevel = [string]$_.UserCodeWarningLevel

        # Add the element to the QuotaTemplates node as a new child
        $xmlDoc.QuotaTemplates.AppendChild($newQuotaTemplate)
    }

    # Remove Sample node
    $sample = $xmlDoc.QuotaTemplates.QuotaTemplate | Where-Object { $_.Name -eq "Sample" }
    $xmlDoc.QuotaTemplates.RemoveChild($sample)

    # Set date and time when generated
    $now = Get-Date
    $xmlDoc.QuotaTemplates.AsOn = [string]$now

    # Save the XML as a file to the current location
    $currentLocation = Get-Location
    $xmlDoc.Save("$currentLocation\QuotaTemplates.xml")

    # Report completion
    Write-Host -f Green "The quota template information has been saved to QuotaTemplates.xml."
}

Get-SPQuotaTemplates

This script fetches the quota templates currently specified on the local SharePoint farm and their related properties and places these details in an XML file in the same location as the script file.

clip_image002

The XML file will be named QuotaTemplates.xml and will be of the following format.

<?xml version="1.0" encoding="UTF-8"?>
<QuotaTemplates AsOn="04/24/2013 14:58:32">
    <QuotaTemplate Name="Personal Site" Action="None" StorageMaxLevelInMB="100" StorageWarnLevelInMB="80" UserCodeMaxLevel="300" UserCodeWarnLevel="200" />
    <QuotaTemplate Name="Sample 01" Action="None" StorageMaxLevelInMB="4096" StorageWarnLevelInMB="2048" UserCodeMaxLevel="0" UserCodeWarnLevel="0" />
    <QuotaTemplate Name="Sample 02" Action="None" StorageMaxLevelInMB="5120" StorageWarnLevelInMB="4096" UserCodeMaxLevel="0" UserCodeWarnLevel="0" />
</QuotaTemplates>

This file will serve as in input descriptor for the other script that manages the creation, edition and deletion of quota templates. Running the GetQuotaTemplates.ps1 script will overwrite any existing QuotaTemplates.xml file in the script file location. A file with the same name is expected as input by the ManageQuotaTemplates.ps1 script. Therefore if an input file is required for posterity, a copy of the file will need to be saved away manually.

ManageQuotaTemplates.ps1

###############################################
# Script:   ManageQuotaTemplates.ps1
# Function: Applies changes to quota templates
#           based on definition in XML
# Author:   Lenny Ankireddi (2013)
###############################################

function ManageQuotaTemplates
{
    # Get a reference to the content service
    $service = [Microsoft.SharePoint.Administration.SPWebService]::ContentService

    # Read XML input file
    $currentLocation = Get-Location
    $xmlDoc = New-Object XML
    $xmlDoc.Load("$currentLocation\QuotaTemplates.xml");
    $xmlDoc.QuotaTemplates.QuotaTemplate | Foreach-Object {

        $Name = $_.Name
        $StorageMaxLevel = $_.StorageMaxLevelInMB
        $StorageWarnLevel = $_.StorageWarnLevelInMB
        $UserCodeMaxLevel = $_.UserCodeMaxLevelInMB
        $UserCodeWarnLevel = $_.UserCodeWarnLevelInMB

        switch ($_.Action)
        {
            "Add"
            {
                Write-Host "Attempting to add new quota template $name..."
                # Check if quota template already exists
                if ($service.QuotaTemplates[$Name] -ne $null)
                {
                    Write-Host -f Red "Quota Template $Name already exists. Cannot add a new one with the same name. Use the Edit action to alter the existing template."
                    Write-Host ""
                }
                else
                {
                    # Get a reference to a quota template object
                    Write-Host "    Getting a reference to a new quota template object..."
                    $quotaTemplate = New-Object Microsoft.SharePoint.Administration.SPQuotaTemplate

                    # Set mandatory properties on the quota template
                    Write-Host "    Setting quota template properties..."
                    $quotaTemplate.Name = $Name
                    $quotaTemplate.StorageMaximumLevel = [int]$StorageMaxLevel * 1024 * 1024
                    $quotaTemplate.StorageWarningLevel = [int]$StorageWarnLevel * 1024 * 1024
                    $quotaTemplate.UserCodeMaximumLevel = [double]$UserCodeMaxLevel
                    $quotaTemplate.UserCodeWarningLevel = [double]$UserCodeWarnLevel

                    # Add the new quota template to the list of quota templates
                    Write-Host "    Adding the quota template $Name..."
                    $service.QuotaTemplates.Add($quotaTemplate)

                    # Update the service
                    $service.Update()

                    Write-Host -f Green "Quota Template $Name has been added."
                    Write-Host ""
                }
            }

            "Edit"
            {
                Write-Host "Attempting to edit quota template $name..."
                # Check if quota template already exists
                if ($service.QuotaTemplates[$Name] -ne $null)
                {
                    # If found, edit the template with new property values
                    Write-Host "    Quota template was located; editing it..."
                    $service.QuotaTemplates[$Name].Name = $Name
                    $service.QuotaTemplates[$Name].StorageMaximumLevel = [int]$StorageMaxLevel * 1024 * 1024
                    $service.QuotaTemplates[$Name].StorageWarningLevel = [int]$StorageWarnLevel * 1024 * 1024
                    $service.QuotaTemplates[$Name].UserCodeMaximumLevel = [double]$UserCodeMaxLevel
                    $service.QuotaTemplates[$Name].UserCodeWarningLevel = [double]$UserCodeWarnLevel

                    # Update the service
                    $service.Update()

                    Write-Host -f Green "Quota Template $Name has been edited."
                    Write-Host ""
                }
                else
                {
                    Write-Host -f Red "Quota Template $Name was not found. Verify that it exists and name has been correctly typed in XML input. Use the Add action to add a new template by this name."
                    Write-Host ""
                }
            }

            "Delete"
            {
                Write-Host "Attempting to delete quota template $name..."
                # Check if quota template can be found
                if ($service.QuotaTemplates[$Name] -ne $null)
                {
                    # If found, delete it
                    Write-Host "    Quota template was located; deleting it..."
                    $service.QuotaTemplates.Delete($Name)

                    # Update the service
                    $service.Update()

                    #Report completion
                    Write-Host -f Green "Quota template $Name has been deleted."
                    Write-Host ""
                }
                else
                {
                    Write-Host -f Red "Quota template by name $Name was not found. Verify that it exists and name has been correctly typed in XML input."
                    Write-Host ""
                }
            }
        }
    }
}

ManageQuotaTemplates

This script is used to make changes to the quota templates currently defined within the system. This can be done by altering the XML file output generated by GetQuotaTemplates.ps1 shown above.

clip_image003

The Action attribute of the QuotaTemplate element can be altered to one of the following values.

 

Action Result
Add The script will attempt to create a new quota template. However if one already exists by the name used in the XML, no action is taken.
Edit The script will attempt to find an existing quota template by the given name and edit its properties. If one is not found, no action is taken.
Delete The script will attempt to find an existing quota template by the given name and delete it. If one is not found, script reports failure to find it.
None The quota template is left untouched.

Altering the XML

The following precautions need to be taken while altering the input file for the quota template management script

  1. Only edit the Action attribute on QuotaTemplate elements that need to be changed. It is None by default and these quota templates are not altered by the script.
  2. To add a new quota template, copy one of the existing elements over and change its attributes. Do not forget to set the Action attribute to Add.
  3. To edit an existing quota template, select the QuotaTemplate element with the corresponding Name attribute and change its attributes. Do not forget to set the Action attribute to Edit.
  4. To delete an existing quota template, select the QuotaTemplate element with the corresponding Name attribute and set its Action attribute to Delete.
  5. Add, Edit and Delete values on the Action attribute are case sensitive. Type exactly as shown here.
  6. Change the values of the attributes of the QuotaTemplate elements as necessary. Do NOT change any of the other XML constructs.
  7. Values for StorageMaxLevelInMB and StorageWarnLevelInMB are to be provided in integer megabytes. If you do not want to specify these, set them to zero.
  8. Values for UserCodeMaxLevel and UserCodeWarnLevel are to be provided in numerical points. If you do not want to specify these, set them to zero. If you want to set them to SharePoint standard values, specify 300 and 100 respectively.

Recommended approach

Run the GetQuotaTemplates.ps1 script first always to get a fresh updated XML from the server. Alter the XML as required and run ManageQuotaTemplates.ps1. The scripts have been written to avoid providing input parameters on the command line. Hope this helps.

Uninstalling the Office 15 Click-to-Run Extensibility component

August 10, 2013 144 comments

So I was trying to install some 64-bit Office products on my computer and saw the following error message. There seemed to be a 32-bit version of the Office 15 Click-to-Run Extensibility Component installed on my computer.

clip_image001

The Click-to-Run Extensibility Component does seamless transparent upgrades of the Office product in the back end. So there were good reasons for not removing it. But if it prevented me for installing additional features because it was 32-bit then it was time for it to go.

IMPORTANT: If you have 32-bit version of Office installed, DO NOT remove the Click-to-Run components. Office applications may stop working altogether. I was able to do this because my version of Office installation was 64-bit and this item was unrelated.

Problem was, I couldn’t find this program in the programs list in Control Panel. So I was wondering how to remove it. After digging through the registry for the installer (or uninstaller), I found the key related to it and it showed where the MSI was that installs it.

clip_image002

So went to that location, right-clicked on c2rint.msi and selected Uninstall. Confirmed on the Installer dialog.

clip_image003

That did the trick. The product was removed and so was the corresponding key in the registry. There were other products as you can see in the registry that were Click-to-Run related but since the 64-bit installer didn’t complain about these, I left them alone.

clip_image004

I was able to proceed with the installation of the 64-bit Office products I wanted. It may have been a remnant from an old 32-bit install. It is believed (but I can’t vouch for this) that the 32-bit version gets installed when you have preview versions of Office installed. In any case, there is a way to get it out if required. Now to see if I regret removing it eventually.

%d bloggers like this: