Showing posts with label Powershell. Show all posts
Showing posts with label Powershell. Show all posts

Wednesday, 20 July 2011

Update - Creating SharePoint 2010 user profile exclusion filters using Powershell

This post is actually a follow-up on my previous post on user profile exclusion filters.

A quick recap of that post:
  • I needed to script the creation of a user profile service application, along with some profile exclusion filters.
  • Unfortunately I couldn't find any existing reference on script the creation of user profile exclusion filters (only guides on how to do this in the central admin pages).
  • Took a peek at the code SharePoint uses to create exclusion filters in central admin, only to see they use internal/hidden classes & methods to create these filters.
  • In the end I couldn't find any public API/powershell cmdlet to create these filters, so ended up writing my own workaround: a small .NET 3.5 project that will invoke the internal/hidden SharePoint 2010 methods using reflection.

Still, for me there was something missing: an easy way to use this workaround in PowerShell. Cause we all know: Powershell rocks! :p

First attempt
Originally I wanted to translate the complete .NET 3.5 workaround into one (or more) ps1 scripts. This would eliminate all need for external assemblies and is just easier to read/fix should something happen.
I managed to translate 99% of the .NET code, which was actually easier than I thought it would be. It was a nice exercise in the usage of reflection and generics within PowerShell 2, so I might make a specific blogpost on this subject later on :)

However, 99% is not 100%. I just couldn't get the final step working: how to assign the FilterSet objects (the actual filters) to the user profile connection.
Internally SharePoint 2010 uses the SetExclusionFilters method - something I had to call using reflection. This worked like a charm from within a .NET project, but I couldn't get PowerShell to cast the objects to the correct internal classes (despite the fact that Get-Member did mention it was casted correctly).
As a result, I kept getting the following error ... no matter what I tried

Exception calling "Invoke" with "2" argument(s): "Parameter count mismatch."
At Functions.ps1:96 char:35
+ $setExclusionFiltersMI.Invoke <<<< ($connection, @($filterSetList)) + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : DotNetMethodException
When I find the time, I'll post the ps1 scripts too ... maybe one of you can solve this mystery.

First Second attempt
Soooo translating it all into ps1 scripts didn't work, but no worries! Time for a workaround .. for a workaround?!
This time I just wrote another .NET project, which defines some custom cmdlets. These cmdlets will then invoke the .NET code that I described in my previous post. On the bright side: the cmdlets can be easily used from within PowerShall and it actually works! :)
(But you're still stuck with a bunch of .NET assemblies that you cannot change on the fly ... can't have it all, I guess.)

Before I start a lengthy description of the cmdlets I developed, I'll post provide a quick overview. Use these links if you can't be a***d / bothered to read manuals :)

The project defines these cmdlets
  • Get-FilterSetCondition
  • Get-FilterSet
  • Set-ExclusionFilter
  • Get-FilterAttributes
  • Get-FilterSetConditionOperators
  • Get-FilterSetOperators

Get-FilterSetCondition
Use this cmdlet to create a new condition for your exclusion filter. These conditions are internally stored as FilterSetCondition objects, hence the naming. You can find an example of this in Example1.ps1 and Example2.ps1
Syntax:
Get-FilterSetCondition -Connection $connection `
                       -ConditionType "user" `
                       -Attribute "userAccountControl" `
                       -Operator "Bit_on" `
                       -Value "2"
Parameters:
  • Connection
    The connection on which you want to create an exclusion filter (Microsoft.Office.Server.UserProfiles.Connection object)
  • ConditionType
    Whether this condition applies to a user-related attribute or a group-related attribute (just like you have separate windows in the UI to specify user and group filters)
  • Attribute
    The attribute on which you want to filter, e.g. company or userAccountControl. Use the Get-FilterAttributes cmdlet to get an overview of all available attributes.
  • Operator
    The operator that you want to use in this condition, e.g. "Bit_on" or "Equals". Use the Get-FilterSetConditionOperators cmdlet to get an overview of all available operators.
  • Value
    The string-value you want to use in this condition.

Get-FilterSet
Use this cmdlet to create a new exclusion filter, based on conditions you've created using the Get-FilterSetCondition cmdlet. Do not mix user-based conditions with group-based conditions though! You can find an example of this in Example1.ps1 and Example2.ps1
Syntax:
Get-FilterSet -FilterType "user" `
              -Operator "or" `
              -Conditions $userConditions
Parameters:
  • FilterType
    Whether this filter contains user-related conditions or a group-related condition
  • Operator
    The operator that you want to use to combine the conditions. Either supply "or" or "and" as value.
  • Conditions
    An array of FilterSetCondition objects (which you created using the Get-FilterSetCondition cmdlet)

Set-ExclusionFilter
Use this cmdlet to assign exclusion filters (created using the Get-FilterSet cmdlet) to an existing user profile connection. You can either assign a user-based exclusion filter, a group-based exclusion filter or both.
You cannot however assign multiple user-based exclusion filters or multiple group-based exclusion filters. You'll have to combine these into a single user-based exclusion filter or a single group-based exclusion filter. An example of this cmdlet can be found in Example1.ps1 and Example2.ps1
Syntax:
Set-ExclusionFilter -Connection $connection `
                    -UserFilter $userFilter `
                    -GroupFilter $groupFilter
Parameters:
  • Connection
    A reference to a user profile connection (Microsoft.Office.Server.UserProfiles.Connection object)
  • UserFilter
    The user-related exclusion filter you want to set on the connection. Don't supply this parameter if you don't want to set a user-related exclusion filter.
  • GroupFilter
    The group-related exclusion filter you want to set on the connection. Don't supply this parameter if you don't want to set a group-related exclusion filter.

Get-FilterAttributes
Use this cmdlet to get a list of all available user-related or group-related attributes on a given user profile connection. Each of the returned strings can be used in the Get-FilterSetCondition cmdlet to create a condition on. You can find an example of this in Example3.ps1
Syntax:
Get-FilterAttributes -Connection $connection -User
Get-FilterAttributes -Connection $connection -Group
Parameters:
  • Connection
    A reference to a user profile connection (Microsoft.Office.Server.UserProfiles.Connection object)
  • User
    A switch parameter. Supply this if you want to list all user-related attributes on the connection. Cannot be used in combination with the -Group switch
  • Group
    A switch parameter. Supply this if you want to list all group-related attributes on the connection. Cannot be used in combination with the -User switch

Get-FilterSetConditionOperators
Lists all available operators that you can use in Get-FilterSetCondition

Get-FilterSetOperators
Lists all available operators that you can use in Get-FilterSet


That's brilliant ... but how do I use this in SharePoint's management shell?!
  1. Download either the complete package or the standalone archive. Look in the download section for a link ...
    • The standalone package only contains the necessary assemblies and some example scripts.
    • The complete package also contains the source-code I used, so you could make your own modifications. The source-code is located in the src folder, but I've also copied the necessary assemblies and examples in the distribution folder for easy access
  2. Whatever you downloaded, copy the VNTG.UserProfileFilters.PowerShell.dll and VNTG.UserProfileFilters.dll assemblies to a folder on your SharePoint 2010 server.
  3. Open a new SharePoint 2010 Management Shell on that server
  4. Load the cmdlets using Import-Module ".\VNTG.UserProfileFilters.PowerShell.dll"
  5. That's it! You can now use the cmdlets to create exclusion filters. Don't forget to check out the Example*.ps1 scripts that are also present in the downloaded packages. These scripts provide you with some real-life scenarios to create exclusion filters using the new cmdlets.


Disclaimer and downloads
Like I mentioned in the first part of this post: the workaround is based on reflection, in order to call the internal classes/methods that SharePoint 2010 uses in its Central Admin pages.
I'm sure they're hidden for a reason, so this workaround is definately not officially supported!

USE IT AT YOUR OWN RISK!
Neither I nor my bosses at Ventigrate can be held reliable if something goes wrong!
(The download is hosted at their codeplex project, so they forced me to add this disclaimer ... :p)

If you're interested in the complete source: use this download. This zip-archive contains the source of both the .NET workaround mentioned in the first part, as well as the powershell cmdlets. It also comes with additional examples to clarify its usage.

If you're not interested in any source code, but just want to use this workaround in your SharePoint 2010 Management Shell: use this download. This archive only contains the necessary .NET assemblies and some example ps1 scripts on how to use the cmdlets in your Powershell 2 console.


Examples
I'll list the same example that was mentioned in my original post - only this time using the custom cmdlets. This example is also available as Example1.ps1 in the downloads.
This code will create a user-attribute based exclusion filter and a group-attribute based exclusion filter and assign it to a user profile connection.
  • The user-attrib exclusion filter will filter on "userAccountControl bit on equals 2" or "company equals ventigrate"
  • The group-attrib exclusion filter will filter on "name equals testgroup"
Check the Example2.ps1 script if you want an example that only adds a user-attribute based filter.

#region STEP 1: load the necessary references
#--------------------------------------------
# Load the sharepoint powershell cmdlets
Add-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue | Out-Null

# Load the custom userprofile cmdlet dll
Import-Module ".\VNTG.UserProfileFilters.PowerShell.dll"
#endregion

#region STEP 2: get the user profile connection on which you want to create exclusion filters
#--------------------------------------------------------------------------------------------
# Get a reference to the service application called 'User Profile Service Application' (very original)
# and a connection called Example 1.
# Change these parameters to whatever user profile service app & connection you want to change on 
# your environment...
$serviceAppName = "User Profile Service Application"
$connectionName = "Example 1"

$profileApp = Get-SPServiceApplication | ? {$_.DisplayName -eq $serviceAppName}
$profileContext = [Microsoft.SharePoint.SPServiceContext]::GetContext(
          $profileApp.ServiceApplicationProxyGroup, 
          [Microsoft.SharePoint.SPSiteSubscriptionIdentifier]::Default)
          
# Get a new UserProfileConfigManager
$configManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileConfigManager($profileContext)

# Get the connection called "Example 1" in the service application
$connection = $configManager.ConnectionManager | ? {$_.DisplayName -eq $connectionName}

#endregion

#region STEP 3: create the conditions and bind them into an user-filter and a group-filter
#-----------------------------------------------------------------------------------------
# Execute Create-FilterSetCondition to create 2 user-related conditions and 1 group-related condition
# - userCondition1 = userAccountControl bit on equals 2
# - userCondition2 = company equals ventigrate
# - groupCondition = name equals testgroup
$userCondition1 = Get-FilterSetCondition -Connection $connection -ConditionType "user" -Attribute "userAccountControl" -Operator "Bit_on" -Value "2"
$userCondition2 = Get-FilterSetCondition -Connection $connection -ConditionType "user" -Attribute "company" -Operator "Equals" -Value "ventigrate"
$groupCondition = Get-FilterSetCondition -Connection $connection -ConditionType "group" -Attribute "name" -Operator "Equals" -Value "testgroup"

# Combine the user-related conditions in a single user FilterSet, using an OR operand
$userConditions = @($userCondition1, $userCondition2)
$userFilter = Get-FilterSet -FilterType "user" -Operator "or" -Conditions $userConditions

# "Combine" the group condition in another group FilterSet, using an AND operand (though it doesn't matter as it's a single condition ;))
$groupConditions = @($groupCondition)
$groupFilter = Get-FilterSet -FilterType "group" -Operator "and" -Conditions $groupConditions

#endregion

#region STEP 4: assign the filters to the connection
#---------------------------------------------------
# Assign the newly created filters to the connection
Set-ExclusionFilter -Connection $connection -UserFilter $userFilter -GroupFilter $groupFilter

#endregion

I think this code is pretty self-explaining (lots of comments in there).
As always: leave a comment if something isn't 100% clear ... or if you just want to post some constructive feedback :)

Monday, 28 June 2010

Removing SSL from SharePoint Central Administration

Encountered yet another SharePoint 2007 inconsistency today.

Situation:
One of my customers wanted to test out an SSL-secured central admin page to allow for SSL-secured content deployments. His wish is my command (he's paying the bills after all), so I proceeded with executing the following stsadm command
stsadm -o setadminport -port 443 -ssl

As expected, after clicking on the Sharepoint Central Admin link, we were greeted by a nice https://server****/ url in our browser's address bar. Huzzah!
As can be read on many other blogs covering the setup of SSL in central admin, I then proceeded with installing the SSL certificates to finalize the setup. 'T was a true miracle, but the SSL central admin actually worked.

However, due to some problems with the SSL certificate itself (not the setup), the content deployment tests failed. So we ended up in removing the SSL from the central admin again.
Should be easy stuff, right?

WRONG! Remember, we're dealing with SharePoint after all ;)
My first guess was: "let's run the stsadm again but without the -ssl parameter"
stsadm -o setadminport -port 10000

This resulted in a nice "Operation completed succesfully" message, but when we clicked on the central admin link ... we were greeted by a less nice https://server***:10000. So, looks like the command succesfully changed the portnumber, but unfortunately it kept the https part.

Tried some other things, but they all failed. The portnumber changed, but the https part didn't move an inch.
So it was time to bring out the big guns:
  • .NET Reflector (awesome tool!)
  • Powershell (even more awesome for these situations!)
Ran a reflector on the stsadm.exe command, after some digging I found the code responsible for the setadminport operation:
Microsoft.SharePoint.Administration.SPGlobalAdmin.SetAdminPort()

(not going to post the code here, don't wanna risk lawsuits ... if you're after the code: disassemble it yourself ;))


In general, this method will open the SPAdministrationWebApplication.Local object, change its ServerBindings or SecureBindings properties (based on the -ssl flag), modify the AlternateUrls SPAlternateUrlCollection and finally create a webapp provisioning job.

Armed with that information, I started fiddling around in Powershell. Remember kids, only fiddle around in a VM with a recent snapshot ... don't do this on production servers! You'll find out why in a couple of paragraphs :)

First thing I noticed: after executing the "stsadm -o setadminport -port 10000", I ended up with 2 alternate url's in the AlternateUrls property for the webapp's default zone. Looks like we're getting a bit warmer:
  • one with https://server*:10000 as incoming url
  • one with http://server*:10000 as incoming url
Attempt 1:
Tried to manually delete the https alternate url by calling AlternateUrls.Delete(0). However, calling Update() on the AlternateUrls property will then result in dependency errors and a failing central admin.
There was no way to fix this, so I ended up in restoring a VM snapshot :)


Attempt 2:
Instead of removing the urls, I tried calling the SetResponseUrl method on the AlternateUrls property & passed the http://server*:10000 as parameter.
Guess what: this method replaced both alternate url definitions with a single alternate url (which I had supplied in the method parameters). Result!

For all you lazy arses out there, I used the following Powershell script to solve it. Maybe it's easier to read than my ranting :)
//Load the SharePoint assembly
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

//Get the central admin webapp
$cAdmApp = [Microsoft.SharePoint.Administration.SPAdministrationWebApplication]
::Local

//Create a new alternate url
$altUrl = New-Object "Microsoft.SharePoint.Administration.SPAlternateUrl"
-ArgumentList "http://server:10000", "Default"

//Set this alternate url as response url
$cAdmApp.AlternateUrls.SetResponseUrl( $altUrl )
$cAdmApp.AlternateUrls.Update()

//Get the alternate urls definition, this should only list our
//http://server:10000 url
$cAdmApp.AlternateUrls
... and there was much rejoicing.