Create Redirect Rule with PowerShell

This is a follow-up to my recent post showing how quick the creation of a self-signed certificate can be. Now that you have an SSL certificate it is helpful to ensure that all calls to your site go through on SSL. The easiest way to do this ( and save yourself typing time) is to create a Url rewrite rule via IIS’s Url Rewrite Module, https://www.iis.net/downloads/microsoft/url-rewrite.

Ensure Url Rewrite Module is Installed

For redirect rules to be written you need to confirm the module is installed. The best way to do this is to open up IIS, select a web application, and check for the module’s icon.

Url Redirect Rule - Url Rewrite Module Icon

If you do not see the icon, then you will need to install the module before moving on. You can install the module through the Web Platform Installer or the link at https://www.iis.net/downloads/microsoft/url-rewrite.

Run the Create Redirect Rule Script

  1. Download and save as CreateRedirectRule.ps1 from Gist, https://gist.github.com/gillissm/0344c9551e7a8180354544ff5f60e821.
  2. Open a PowerShell command prompt, ensure you are running it as Admin
  3. Change directory to the location you saved to in step 1
  4. At the prompt enter, and complete the requested parameters
    .\CreateRedirectRule.ps1

    Url Redirect Rule -Script with Prompts

    OR you can submit the parameters at run

    .\CreateRedirectRule.ps1 -SiteBinding "coffeehouse.thecodeattic.com" -RuleName "Redirect to SSL"

    Url Redirect Rule -Script in singleline

Script for Reference

Advertisements

Self-Signed Certs in One Command

As security becomes more critical to web based applications the need to test and develop locally with SSL is good practice. Outside of spending mountains of money generating local test environment official SSL Certificates, we can create what is called a ‘Self-Signed’ Certificate.

There are plenty of articles out there that walk through steps to creating a self-signed certificate, some more detailed than others. This post aims to serve two purposes. First, to provide me a future (easy to find) reference point. Secondly, provide helpful information to the fewest number of steps required for creating a self-signed certificate.

Sitecore Fundamentals Install

As a Sitecore developer first (and most of my current teams are Sitecore related) the usage of the Sitecore Fundamentals Install framework will already be on their machine. For those not into Sitecore development, the following additional quick steps will need to be ran.

Setup PowerShell to interact with the Sitecore MyGet feed

  1. Open a PowerShell command prompt, ensure you are running it as Admin
  2. Register the connection to MyGet feed, at the prompt enter
    Register-PSRepository -Name SitecoreGallery -SourceLocation https://sitecore.myget.org/f/sc-powershell/api/v2

    Sitecore Fundamentals - Register Sitecore Gallery

  3. Install the Sitecore Fundamentals module, at the prompt enter
    Install-Module SitecoreFundamentals
  4. PowerShell will ask if untrusted scripts can be ran, enter ‘A‘ and hit Enter.Sitecore Fundamentals -  Accept Untrusted Scripts
  5. Before performing any other steps, and each time before you use the module, you will want to perform a check and update of the module via
    Update-Module SitecoreFundamentals
  6. Confirming everything installed correctly is as easy as running the following command, at time of writing the current version is 1.1.0
    Get-Module SitecoreFundamentals -ListAvailable

    Sitecore Fundamentals - Available Versions

Setup SSL

  1. Ensure your site exists in IIS
  2. Open a PowerShell command prompt, ensure you are running it as Admin
  3. Run the following at the prompt
    Import-Module SitecoreFundamentals
  4. Run the following at the prompt, this will create the self-signed certificate and assign it to your site
    Add-WebFeatureSSL -HostName "ENTER THE HOSTNAME FOR SITE" -RootDnsName "DO_NOT_TRUST_TheCodeAttic"
  5. Enjoy developing securely!!!

Solr Setup for Sitecore XP

Solr

As we continue through the steps for Sitecore Experience Platform installation, the next thing we must have is Solr. This tries to simplify that process.

Background

When installing Sitecore 9 for local development or on-premise for your organization Solr is now the only search provider fully supported. Lucene can still be used to support search within the Context Editor, but all analytics related, and site search scenarios now use Solr or when in the cloud Azure Search Provider.

The following guide will be focused on setting up Solr to support Sitecore 9 on local developer’s machine, where everything (SQL, Sitecore, Solr, etc…) are all installed on the same machine. The steps taken can be used to setup an all in one development or QA server, but should not be used for production setup.

Choose a method

When installing Solr there are many different options that become available, from a direct install, to a pre-packaged solution like Bitnami, a cloud solution, or even a Docker container.

I initially set out to write this guide leveraging Bitnami, as I’ve had a lot of success and quick setup leveraging it, but as it happens old versions of their packages are difficult to find, and Sitecore 9 is tied to Solr 6.6.2. I repeat Sitecore 9.0 update 1 is tied to Solr 6.6.2!!! (I’ve burned a week or so trying to make it work with Solr 7.2 during this series.)

Docker is an intriguing scenario to me, but I’ve got a lot more learning to do before I can successfully get up and running, and I’m in a hurry to start my Sitecore XP 9 life…so this lands me with doing a traditional local install.

As mentioned, Solr is a critical component to Sitecore XP 9, and historical a critical piece to large site implementations. To that benefit there are many write-ups out there regarding Solr installs for Sitecore. The one I found to be very helpful was done by fellow MVP Jeremy Davis. Who has written up Sitecore Installation Framework (SIF) PowerShell module to simplify and streamline the installation you can read his article at https://jermdavis.wordpress.com/2017/11/13/solr-installs-with-sif/.

I’ll be walking you through the steps I took leveraging his excellent PowerShell module.

Steps

  1. Ensure PowerShell will allow for running of unsigned modules by opening a PowerShell prompt as Admin
    Run Set-ExecutionPolicy Unrestricted
  2. Install Java if you don’t already have it.
  3. Install Sitecore Installation Framework (SIF), if you need this check out my how-to from last week .
  4. Copy Jeremy’s PowerShell module code into a new file name ‘SolrInstall-SIF-Extensions.psm1‘ from his Gist at https://gist.github.com/jermdavis/49018386ae7544ce0689568edb7ca2b8#file-solrinstall-sif-extension-psm1
  5. Copy the associated JSON config into a file named ‘SolrInstall-Config.json‘ from https://gist.github.com/jermdavis/49018386ae7544ce0689568edb7ca2b8#file-solrserver-json.I’ve created a fork of the config JSON file that includes updates for JRE runtime and most importantly updated NSSM version for Windows 10 Creator Update machines, find it at https://git.io/vAJXS.
    Solr Install - Files
  6. Open the config JSON, SolrInstall-Config.json, in your preferred editor (my recommendation is Visual Studio Code)
  7. Review the Parameters section and confirm that the default values are correct for your installation. Note for Sitecore 9.0 and 9.0 update 1 do NOT change the Solr version. I found I needed to change the following

    JREVersion => 9.0.4, this should be the JRE value used in the folder path to your Java installation, ie C:\Program Files\Java\jre-9.0.4

    NSSMVersion => 2.24-101-g897c7ad, there is a known issue with stable 2.24 version of Non-Sucking Service Manager on Window 10 machines where it will not start the service.

    SolrHost => sitecoresolrMake note of this name as it will be critical to future steps of the install process

    SolrPort – should be updated if you already have an instance of Solr running on the default port 8983.

  8. Confirm the Variables point to valid paths on your machine. I found the following required updating for me

    JREPath => C:\Program Files\Java\jre-

    NSSMSourcePackage => https://nssm.cc/ci/nssm-

  9. Open a PowerShell command prompt as Admin.
  10. Confirm SIF is update and installed by running
     Update-Module SitecoreInstallFramework
  11. Confirming everything installed correctly is as easy as running the following command, at time of writing the current version is 1.1.0
     Get-Module SitecoreInstallFramework -ListAvailable
  12. Change directory to the location of your config JSON and PowerShell Module file
  13. Before running the install the PowerShell Module needs to be placed in one of the known module library locations, found via running.
     $env:PSModulePath

    Solr Install - Module Locations
    NOTE: The psm1 file needs to be placed into a folder of the same name minus the extension with in the location you pick above.

  14. Run the install via the following command
     Install-SitecoreConfiguration .\SolrInstall-config.json

    Solr Install - Install Script
    NOTE: If you end up with the install running and then failing with a message of “Failed to start service ‘solr-6.6.2 (solr-6.6.2)'” then you are most likely facing the NSSM Windows 10 issue, and will need to either manually install the pre-release build or delete what you have and re-run the install job with an updated config such as this one https://git.io/vAJXS.
    Solr Install - Install Script - Service Start Error

  15. Solr should open in your browser or you should be able to reach it via: HTTPS://sitecoresolr:8983/solr
    Solr Install - Solr Admin Screen

A Master Key to Your Content

Once in a while I find myself in a situation where workflow hasn’t been completed but users have begun content entry or cleanup. Normally these users also have not been setup as site admins, but with some level of custom accesses. This causes the editors to spend time dealing with locking and unlocking items for editing. At times they’ll not even realize and they locked the item, and you start to get a trail of items edited then forgotten.

What’s an admin to do?

Without too much work you can turn-on the gutter icon for ‘Locked Items’, then drill through the tree visual identifying and jotting down what’s locked and who.

Locked Items Gutter Setting

But that’s tedious (and a bit error prone). As with all cool administrative tasks in Sitecore we are best to turn our attention to Sitecore PowerShell Extensions (SPE) in the marketplace

The SPE ships with function that gives us the ability to see and react to locked items on our site.

Get-LockedChildItem

Step 1 – Import Function

By default the function Get-LockedChildItem is not callable. You must first ‘import’ it into your console or ISE session.

Import-Function Get-LockedChildItem

 Import-Function Get-LockedChildItem

If you don’t perform an import of the function, then you’ll receive a red error message like the following

Get-LockedChildItem : The term ‘Get-LockedChildItem’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

Step 2 – Get a Report

Before performing any sort of automation on our content tree, we should always produce a report that provides some context of what may get changed. In this case we want to get a listing of all items that are locked

Get-LockedChildItem -Recurse | Show-ListView

Here we are using the ‘Recurse’ flag to walk the entire tree. The collection of items locked are then piped to the very nice Show-ListView, allowing for easy review and export.

Get-LockedChildItem as a Report

We can also run the report for specific users via the -LockedBy parameter

Get-LockedChildItem -Recurse -LockedBy 'sitecore\editor' | Show-ListView

Step 3 – Time to Free the Content

Once we understand who has what locked we can start performing some mass freeing of content with the -Unlock parameter. To unlock everything, we run

Get-LockedChildItem -Recurse -Unlock

If we want to perform some hand picking of section of the site, we can either open the console to a certain node of the tree and run the above or define the start point with -Path parameter. This will unlock all the children of the item defined by path, even providing a nice output of what was unlocked.

Get-LockedChildItem -Recurse -Unlock -Path {85E0AF8C-ED9F-4CDA-BFB2-084015E17634}
#or
Get-LockedChildItem -Recurse -Unlock -Path /sitecore/content/Coffeehouse/Home/About-Us

Unlock for a path

Finally, if we want to unlock for a specific individual we can re-use the -LockedBy parameter,

Get-LockedChildItem -Recurse -Unlock -LockedBy 'sitecore\editor'

Not a Replacement for workflow (i.e. the Disclaimer)

Being able to execute unlocking of content on a mass scale is helpful, but this shouldn’t be the replacement for properly planned and built workflow on your site.

SharePoint 2010 SP1 Not Installed

The past few weeks I have been working n scripting out the installation and configuration of SharePoint 2010 and all the services for a multi-server environment. I’ll be posting the details of the script in the coming weeks (okay most likely months….)

For the installation I have been using a slip-streamed set of media which includes SP1 and the June 2012 Cumulative Update. I farm will be using a SQL 2012 database which also provides the advantage of using the new BI features such as PowerView and SSRS as an actual service to SharePoint.

Everything seemed fine until I tried to configure PowerPivot and received a message saying “SharePoint 2010 SP1 is not installed on this machine.” This seemed odd as I’ve been able to configure everything up to this point….

After a few days of googling I finally got the correct search terms, which was “powerpivot error SharePoint 2010 SP1 is not installed”. This led me to a post by Jeff Jones blogger of www.spjeff.com. He has a create article which solved my problem called “Fixed -SharePoint 2010 SP1 is not installed on this computer.

The basic issue is that a registry key does not get updated with the new build version, causing PowerPivot to think everything is out dated. The registry key in question is HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\14.0\BuildVersion.

Since I like scripts here is a little PowerShell goodness to help you out. Be sure that the Microsoft.SharePoint.PowerShell snap-in has been loaded into your PowerShell session before running.

$currentKey = Get-ItemProperty "hklm:\software\microsoft\office server\14.0\" -Name BuildVersion
$farmBuild = (Get-SPFarm).BuildVersion
Write-Host "Registry Build is $($currentKey.BuildVersion)"
Write-Host "Farm Build is $farmBuild"
if($currentKey.BuildVersion -ne $farmBuild)
{
Write-Host " - Updating registry build to $farmBuild"
Set-ItemProperty "hklm:\software\microsoft\office server\14.0\" -Name BuildVersion -value $farmBuild
}

Requires Qualified Assembly Name, ugh!

You are plugging away at writing this super sweet class, to be used in your latest application, and then you hit the wall! Nothing builds because the configuration file requires a reference to the fully qualified assembly name…ugh!!!

The first thing you do is scratch your inner mind trying to recall what makes up this name. (At least that is what I end up doing every time.) It seems depending on what you are working on you will either need a four-part name or a ‘five-part name’.

The four-part fully qualified name consists of the following in the following order:

Assembly Name, Version= 0.0.0.0, Culture=culture code, PublicKeyToken=public key

(It is common for culture to be set to ‘neutral’ if there is nothing contained that is specific to a cultural style.)

What I consider the to be the five-part name is the four-part name with the namespace and class name that is being referenced prefixed.

Namespace.Class Name, Assembly Name, Version=0.0.0.0, Culture=culture code, PublicKeyToken= public key

With the background details explained, how as a lazy programmer can we retrieve the fully qualified name without a lot of clicks? The old stand by use to be to fire up reflector, throw your assembly in and poof there was the four part assembly name, but any more finding a really good reflector is much more difficult (and there is a lot of extra clicking involved by using a second program.)

Visual Studio 2010 has a great feature in allowing you to set up single clicks from the menu bar and via hotkeys to external tools…which means a perfect fit for a simple PowerShell command.

  1. Go to Tools -> External Tools.
  2. The External Tools dialog box should open for you. Click Add.
    image
  3. This will then generate a new value labeled [New Tool 1] and have it selected for you.
    image
  4. In the lower half, provide a Tittle for the new tool such as "4-Part Name".
  5. In the Command, textbox you will enter the following
    powerhell.exe
  6. In the Arguments, textbox enter the following
    -command "[System.Reflection.AssemblyName]::GetAssemblyName(\"$(TargetPath)\").FullName
  7. Select Use Output window to force the assembly name into the output window of Visual Studio.
  8. The completed dialog should resemble the following
    image
  9. Click OK.
  10. Your command should now display under Tools.
    image
  11. To get the four part name,
    1. Select the project or any file in the project from Solution Explorer or Solution Navigator
    2. Go to Tools –> 4-Part Name
    3. See the four part name displayed in the Output window
      image

References:
MSDN AssemblyName.GetAssemblyName()
MSDN Type.FullName
MSDN Details on Assemnly Names

All Together Now

This is the final part in my series on how to load users and groups into Active Directory (AD) using the magical powers found in PowerShell. ‘All Together Now’ is combining the different commands that have been reviewed in the earlier parts and how to load the users and group details from an XML file. There are a number of post out in the vast inter-webs which explain how to insert groups and users via a comma delimited file, but I have a slight bias towards the nice hierarchal form of XML, hence this final piece to the puzzle.

I am going to presume you understand the ins and outs of the other commands which were explained and demoed in the first series of post. If you haven’t read the first three parts here are the links to them for reference:

    The first step is to generate the XML. Here is the XML I will be referencing.

    1 <NewUserManagement> 2 <User> 3 <givenname></givenname> 4 <surname></surname> 5 <fullname></fullname> 6 <login></login> 7 <description></description> 8 <password></password> 9 <group></group> 10 <group></group> 11 </User> 12 <User> 13 <givenname></givenname> 14 <surname></surname> 15 <fullname></fullname> 16 <login></login> 17 <description></description> 18 <password></password> 19 <group></group> 20 <group></group> 21 </User> 22 </NewUserManagement>

You will need one ‘User’ node for each user that you will want to generate. Within this node, the ‘group’ node can be repeated for any number of groups that this user will be a member of. One thing that caused me a slight headache was specifically listing the Users domain group. What I learned is that users are automatically added to this group upon add, so no need to list it out.

Here is the definition f the other fields for reference:

     givenname – First Name

     surname – Last Name

     login – User login name for the domain

     description – Description of the user

     password – password in plain text for the user

     group – Name of an existing or new group the user should be a member

Hint 1: When writing multiple functions for a script, these functions must be defined before they are called.

Now that we have our XML written, let’s begin the script.

  1. For this script to be as flexible as possible, we will need to generate a single required parameter for the path to the XML file.

    1 param( 2 [Parameter(Mandatory=$true)] 3 [string] $pathToFile 4 )

  2. The first function that will be written will be the adding of new users to Active Directory.

    1 function addNewUser{ 2 param($uXML) 3 $loginName = $uXML.login; 4 $user = Get-ADUser -Filter {samAccountName -eq $loginName}; 5 if(!$user) 6 { 7 $securePassword = ConvertTo-SecureString $uXML.password -AsPlainText -Force; 8 $user = New-ADUser -SamAccountName $uXML.login -Name $uXML.login -AccountPassword $securePassword -CannotChangePassword $true -PasswordNeverExpires $true -Description $uXML.description -DisplayName $uXML.login -Enabled $true -EmailAddress ($uXML.login + "@rainfly.com") -Server "rainfly.com" -GivenName $uXml.givename -Surname $uXML.surname -PassThru; 9 Write-Host "User $user.SamAccountName was created."; 10 } 11 Sleep -Seconds 5; 12 return $user; 13 }

    The function has a single parameter, uXML, which is the XML object of the entire user node from the file. Line 4, performs a get on the user login name. If the user exists, then an AD user object is returned else it will be empty.

    Hint 2: Objects in PowerShell can be tested for null or nothing, by a simple if statement.

    i.e.

    1 if($obj) 2 { # $obj has a value; } 3 else 4 { # $obj is null or nothing; }

    The not version of this test is performed in Line 5 to determine if the user already exists. If the user does not exist they are added via the New-ADUser command. (When adding new users with a password the ConvertTo-SecureString is required.)

    Finally, the method returns the AD User object to the main script.

  3. The second function is to add the new users to groups.

    1 function addUserToGroup 2 { 3 param($uObj, $groupSamName) 4 $gObj = Get-ADGroup -Filter {samAccountName -eq $groupSamName}; 5 if(!$gObj)#check for null, true is null 6 { 7 $gObj = New-ADGroup -Name $groupSamName -GroupScope DomainLocal -DisplayName $groupSamName -SamAccountName $groupSamName -PassThru; 8 Write-Host "Group $groupSamName was created"; 9 } 10 if($gObj) 11 { 12 try{ 13 Add-ADGroupMember -Identity $gObj -Member $uObj; 14 Write-Host "Added user, $uObj.SamAccountName, to group $gObj.SamAccountName"; 15 }catch 16 { 17 Write-Host "Failed to add user, $uObj.SamAccountName , to $gObj.SamAccountName "; 18 } 19 } 20 else 21 { 22 Write-Host "User was not added to Group $groupSamName." 23 } 24 }

    This method accepts two parameters. The first, $uObj, is the AD user object that is either retrieved or returned after creation from addNewUser method. The second parameter, $groupSamName, is the name of the group as taken from the XML user node’s group element.

    Line 4 performs a retrieval from AD of the group, just as with the addNewUser method, a check is done for null/nothing to determine if the group already exists.

    Hint 3: Version 2.0 of PowerShell introduced try…catch blocks, that you can see beginning on line 12. Here it is used if to catch any issues that occur when adding the user to the group.

  4. Now that the basic methods we will be needing are in place, let’s begin to write the main script. The first line will be

    1 Import-Module active*;

    This line will automatically load the active directory modules for the PowerShell prompt being ran. This saves you the hassle of trying to remember to load the active directory modules, or run the script from the Active Directory PowerShell prompt.

  5. Finally, here is the heavy lifting portion of the script.

    1 if(Test-Path $pathToFile) 2 { 3 [xml]$xmlObj = Get-Content $pathToFile; 4 $allUsers = $xmlObj.NewUserManagement.ChildNodes; 5 if($allUsers) 6 { 7 foreach($userXML in $allUsers) 8 { 9 if($userXML.Name -ne "#comment") 10 { 11 $theUser = addNewUser -uXML $userXML; 12 foreach($userGroup in $userXML.group) 13 { 14 Write-Host "Procsessing Group: $userGroup"; 15 addUserToGroup -groupSamName $userGroup -uObj $theUser; 16 } 17 } 18 } 19 } 20 } 21 Else 22 { 23 Write-Host "The path to the XML file of users and groups is not valid."; 24 Write-Host $pathToFile; 25 }

    Line 1 kicks things off with a simple check to make sure the user XML file does exist. From there the file is parsed via a loop, to load each user and group. One of the great parts about using PowerShell to parse XML is that once the XML file has been loaded, you can use simple dot notation to get at each node, attribute, and element.

There you have it. The final piece to the puzzle, a script that will generate users and groups based on an XML file. I will note that there may be even more efficient ways to perform this task that I haven’t thought of, so please let me and everyone else know in the comments.