Deploy a SharePoint 2016 Standalone VM in Azure using PowerShell Desired State Configuration (DSC)

In the PowerShell Desired State Configuration (DSC) world, you really have two options when it comes down to configuring a machine. You can either use Push mode to manually “push” a DSC script into a machine’s Local Configuration Manager (LCM) memory, or use a Pull Server and connect your machine to it and let them obtain their DSC script themselves. In the Azure world, we have something called “Azure Automation DSC” which is effectively a PowerShell Desired State Configuration Pull Server in the cloud, managed as “Software-as-a-Service” (SaaS). With Azure Automation DSC, you can manage both on-premises and Azure VMs by having them connect back to your Azure Automation Account as if it was just a regular DSC Pull Server.

In this article, we will go through the process of setting up a SharePoint 2016 Standalone Virtual Machine using nothing but Azure Automation DSC. The idea is for people to easily create SharePoint Development machines in Azure Infrastructure-as-a-Service (IaaS).

The Scenario

In Azure IaaS I already have setup two VMs:

  • SPTechCon-DC is a Windows Server 2016 VM acting as a Domain Controller for the contoso.com domain.
  • SPTechCon-Share is a Windows Server 2016 VM that acts as a File Share, where I have the installation media for both SQL Server 2016 Enterprise and SharePoint Server 2016. These two shares are exposed at:
    • \\SPTechCon-Share\Share\SQL2016Media\
    • \\SPTechCon-Share\Share\SP2016Media

The Process

By following the following steps in order you will be able to deploy a new Windows Server 2016 VM in Azure IaaS, have it automatically join the contoso.com domain, and install both SQL Server 2016 and SharePoint 2016 on it. By the end of this process, you will have a fully functioning SharePoint 2016 development VM that you can simply go and install Visual Studio 2017 on to use as you main development environment for developers within your enterprise. This process is completely reusable, and can help your enterprise ensure your development team all have VMs with a configuration that matches your production environment.

1 – Create a new VM

In this article, this is the only manual process. Off course this could be automated, by for this example here, I will leave it up to you to decide how you wish to create your VM. In my case, I will be creating my VM with the following specs:

  • Name: SPTechCon-Share
  • OS Version: Windows Server 2016 Datacenter
  • Memory: 7Gb of RAM
  • CPU: 2 cores

To create the VM, start by selecting Windows Server as you Template category.

Creating Azure Windows VM

Creating Azure Windows VM

From the following screen, I select Windows Server 2016 Data Center.

Make sure you select Resource Manager as your deployment model and click Create

Azure Resource Manager

Azure Resource Manager

Fill in all the mandatory information, give your machine a meaningful name and make sure you create it as part of the same resource group where your Domain Controller and File Share servers are. Click OK.

Create an Azure Virtual Machine

Create an Azure Virtual Machine

Choose an appropriate VM size for your environment. In my case, I use a DS11_V2 Standard size. Click Select.

DS11_V2 Azure Machine

DS11_V2 Azure Machine

Leave out the default values on the Settings screen. Click OK.

Review the details for your machine. Click OK.

Summary of Azure VM

Summary of Azure VM

Wait a few minutes until you receive notification that the VM was successfully provisioned.

Azure VM being provisioned

Azure VM being provisioned


2 – Create a New Azure Automation Account

Remember we mentioned that Azure Automation is somewhat a DSC Pull Server in the Cloud. What we need to do next is create an instance of an Azure Automation Account to manage our DSC configurations. Azure Automation Accounts are available in the marketplace. Simply do a search for Azure Automation to find and select it.

Create a new Azure Automation Account

Create a new Azure Automation Account

Click on it to select it from the marketplace and then click Create.

Azure Automation Account

Azure Automation Account

Give your Azure Automation Account a name, make sure you select the same Resource Group as all the VMs we have created so far in this demo. Click Create.

Setting up Azure Automation

Setting up Azure Automation

The Azure Automation Account creation process is almost instantaneous, it should only take a few seconds to get created.

Review the Desired State Configuration Script

To configure our Standalone SharePoint box, we will be using the following PowerShell Desired State Configuration (DSC) script. I strongly encourage you quickly read through it an try to understand what is really happening under the covers. This below, is the complete script.

Configuration SharePoint2016StandAlone
{
    param(
        [String]$ParamDomain,
        [String]$ParamInternalDomainControllerIP,
        [String]$ParamMachineName,
        [String]$ParamProductKey,
        [String]$ParamUsername,
        [String]$ParamPassword,
        [String]$ParamShareName
	)

    Import-DSCResource -ModuleName xDSCDomainJoin
    Import-DSCResource -ModuleName xNetworking    
    Import-DSCResource -ModuleName SharePointDSC
    Import-DSCResource -ModuleName xSQLServer    

    $secdomainpasswd = ConvertTo-SecureString $ParamPassword -AsPlainText -Force
    $ParamCredsJoindomain = New-Object System.Management.Automation.PSCredential($ParamUsername, $secdomainpasswd)

    Node $ParamMachineName
    {
        xFireWall SQLFirewallRule
        {
            Name = "AllowSQLConnection"
            DisplayName = 'Allow SQL Connection' 
            Group = 'DSC Configuration Rules' 
            Ensure = 'Present' 
            Enabled = 'True' 
            Profile = ('Domain') 
            Direction = 'InBound' 
            LocalPort = ('1433') 
            Protocol = 'TCP' 
            Description = 'Firewall Rule to allow SQL communication' 
        }

        xDNSServerAddress DNS
	{
	    Address = $ParamInternalDomainControllerIP
	    AddressFamily = "IPv4"
	    InterfaceAlias = "Ethernet 2"
	}

        xDSCDomainJoin Join
	{
	    Domain = $ParamDomain
	    Credential = $ParamCredsJoindomain
	    DependsOn = "[xDNSServerAddress]DNS"
	}		

        xSQLServerSetup SQLSetup
        {
            SetupCredential = $ParamCredsJoindomain
            InstanceName = "MSSQLServer"
            SourcePath = "\\$ParamShareName\Share\SQL2016Media\"
            Features = "SQLENGINE,FULLTEXT,RS,AS,IS"
            InstallSharedDir = "C:\Program Files\Microsoft SQL Server"
            SQLSysAdminAccounts = $ParamCredsJoindomain.UserName
            DependsOn = "[xDSCDomainJoin]Join"
        }

        SPInstallPrereqs SP2016Prereqs
        {
            InstallerPath = "\\$ParamShareName\Share\SP2016Media\prerequisiteinstaller.exe"
            OnlineMode = $true
            DependsOn = "[xSQLServerSetup]SQLSetup"
        }

        SPInstall InstallSharePoint 
        { 
             Ensure = "Present" 
             BinaryDir = "\\$ParamShareName\Share\SP2016Media\" 
             ProductKey = $ParamProductKey
             DependsOn = @("[SPInstallPrereqs]SP2016Prereqs", "[xFirewall]SQLFirewallRule")
        } 

        SPCreateFarm CreateSPFarm 
        { 
            DatabaseServer           = $ParamMachineName
            FarmConfigDatabaseName   = "SP_Config" 
            Passphrase               = $ParamCredsJoindomain 
            FarmAccount              = $ParamCredsJoindomain 
            AdminContentDatabaseName = "SP_AdminContent" 
            PsDSCRunAsCredential     = $ParamCredsJoindomain
            ServerRole               = "SingleServerFarm"
            CentralAdministrationPort = 7777
            DependsOn                = "[SPInstall]InstallSharePoint" 
        }

        SPManagedAccount FarmAccount
        {
            AccountName = $ParamCredsJoindomain.UserName
            Account = $ParamCredsJoindomain
            PsDSCRunAsCredential     = $ParamCredsJoindomain
            DependsOn = "[SPCreateFarm]CreateSPFarm"
        }

        SPServiceAppPool SharePoint80
        {
            Name = "SharePoint - 80"
            ServiceAccount = $ParamCredsJoinDomain.UserName
            PsDSCRunAsCredential     = $ParamCredsJoindomain
            DependsOn = "[SPManagedAccount]FarmAccount"
        }

        SPWebApplication RootWebApp
        {
            Name = "RootWebApp"
            ApplicationPool = "SharePoint - 80"
            ApplicationPoolAccount = $ParamCredsJoinDomain.UserName
            Url = "http://$ParamMachineName"
            DatabaseServer = $ParamMachineName
            DatabaseName = "WebApp-SharePoint-80"
            Port = 80
            PsDSCRunAsCredential = $ParamCredsJoinDomain
            DependsOn = "[SPServiceAppPool]SharePoint80"
        }

        SPSite RootSite
        {
            Url = "http://$ParamMachineName"
            OwnerAlias = $ParamCredsJoinDomain.UserName
            Template = "STS#0"         
            PsDSCRunAsCredential = $ParamCredsJoinDomain
            DependsOn = "[SPWebApplication]RootWebApp"
        }
	}
}

Let’s take a closer look at what the script actually defines. Note that the Configuration script actually expects 7 parameters to be passed at compilation time. These parameters are:

Parameter Name Value Description
ParamDomain contoso.com Specifies the domain name that our machine will be joining.
ParamInternalDomainControllerIP 10.0.10.5 Internal IP address of our Domain Controller VM. (Note that this will likely differ for you).
ParamMachineName SPTechCon-SA Name of the Azure VM we created at Step 1 above.
ParamProductKey XXXXX-XXXXX-XXXXX-XXXXX-XXXXX Your own SharePoint 2016 (Standard or Enterprise) Product Key.
ParamUsername contoso\sp_farm Username for your SharePoint Farm Account.
ParamPassword pass@word1 Password for the SharePoint Farm Account used.
ParamShareName SPTechCon-Share Name of the File Share VM.

We will now break down each resource block an give you a quick overview of what it actually does.

The following creates a Domain Firewall rule on port 1433, to allow connections to our SQL Server (in our case hosted on the local machine) in case we wished to add more servers to our farm.

xFireWall SQLFirewallRule
{
    Name = "AllowSQLConnection"
    DisplayName = 'Allow SQL Connection' 
    Group = 'DSC Configuration Rules' 
    Ensure = 'Present' 
    Enabled = 'True' 
    Profile = ('Domain') 
    Direction = 'InBound' 
    LocalPort = ('1433') 
    Protocol = 'TCP' 
    Description = 'Firewall Rule to allow SQL communication' 
}

This block changes the DNS Server IP address to point to our domain controller.

xDNSServerAddress DNS
{
    Address = $ParamInternalDomainControllerIP
    AddressFamily = "IPv4"
    InterfaceAlias = "Ethernet 2"
}

The following joins the machine to the contoso.com domain.

xDSCDomainJoin Join
{
    Domain = $ParamDomain
    Credential = $ParamCredsJoindomain
    DependsOn = "[xDNSServerAddress]DNS"
}

This block installs SQL Server 2016 from our Shared Media Installation location.

xSQLServerSetup SQLSetup
{
    SetupCredential = $ParamCredsJoindomain
    InstanceName = "MSSQLServer"
    SourcePath = "\\$ParamShareName\Share\SQL2016Media\"
    Features = "SQLENGINE,FULLTEXT,RS,AS,IS"
    InstallSharedDir = "C:\Program Files\Microsoft SQL Server"
    SQLSysAdminAccounts = $ParamCredsJoindomain.UserName
    DependsOn = "[xDSCDomainJoin]Join"
}

This block installs the SharePoint 2016 pre-requisites. The server will automatically reboot itself once it reaches that step and will automatically resume the DSC configuration process.

SPInstallPrereqs SP2016Prereqs
{
    InstallerPath = "\\$ParamShareName\Share\SP2016Media\prerequisiteinstaller.exe"
    OnlineMode = $true
    DependsOn = "[xSQLServerSetup]SQLSetup"
}

This block installs the actual SharePoint 2016 bits on the machine.

SPInstall InstallSharePoint 
{ 
    Ensure = "Present" 
    BinaryDir = "\\$ParamShareName\Share\SP2016Media\" 
    ProductKey = $ParamProductKey
    DependsOn = @("[SPInstallPrereqs]SP2016Prereqs", "[xFirewall]SQLFirewallRule")
} 

This block creates the SharePoint Farm. Think of it as being the equivalent of running PSConfig.

SPCreateFarm CreateSPFarm 
{ 
    DatabaseServer           = $ParamMachineName
    FarmConfigDatabaseName   = "SP_Config" 
    Passphrase               = $ParamCredsJoindomain 
    FarmAccount              = $ParamCredsJoindomain 
    AdminContentDatabaseName = "SP_AdminContent" 
    PsDSCRunAsCredential     = $ParamCredsJoindomain
    ServerRole               = "SingleServerFarm"
    CentralAdministrationPort = 7777
    DependsOn = "[SPInstall]InstallSharePoint" 
}

This block creates a SharePoint Managed Account for our farm admin.

SPManagedAccount FarmAccount
{
    AccountName = $ParamCredsJoindomain.UserName
    Account = $ParamCredsJoindomain
    PsDSCRunAsCredential     = $ParamCredsJoindomain
    DependsOn = "[SPCreateFarm]CreateSPFarm"
}

This block creates a SharePoint Application Pool for our Web Application to be.

SPServiceAppPool SharePoint80
{
    Name = "SharePoint - 80"
    ServiceAccount = $ParamCredsJoinDomain.UserName
    PsDSCRunAsCredential     = $ParamCredsJoindomain
    DependsOn = "[SPManagedAccount]FarmAccount"
}

this block should be self explanatory. It creates a SharePoint Web Application on port 80.

SPWebApplication RootWebApp
{
    Name = "RootWebApp"
    ApplicationPool = "SharePoint - 80"
    ApplicationPoolAccount = $ParamCredsJoinDomain.UserName
    Url = "http://$ParamMachineName"
    DatabaseServer = $ParamMachineName
    DatabaseName = "WebApp-SharePoint-80"
    Port = 80
    PsDSCRunAsCredential = $ParamCredsJoinDomain
    DependsOn = "[SPServiceAppPool]SharePoint80"
}

This last block simply creates a Site Collection at the root of our Web Application.

SPSite RootSite
{
    Url = "http://$ParamMachineName"
    OwnerAlias = $ParamCredsJoinDomain.UserName
    Template = "STS#0"         
    PsDSCRunAsCredential = $ParamCredsJoinDomain
    DependsOn = "[SPWebApplication]RootWebApp"
}

What we need to do now, is upload this DSC configuration into our Azure Automation Account. To do this, start by navigating to your Automation Account and click on DSC Configurations.

DSC Configuration Node

DSC Configuration Node

Click on Add a configuration.

Upload DSC Configuration

Upload DSC Configuration

Click on the folder icon and browse to the .ps1 script above. Note that you will need to copy the complete script and save it locally first. Click on OK.

Our DSC Configuration Script is now in the Cloud, contained within our Azure Automation Account.

4 – Import the Required DSC Module

If you paid close attention to our full script above, you’ve realized that it needs to import 4 different DSC modules:

  • xDSCDomainJoin
  • xNetworking
  • SharePointDSC
  • xSQLServer

However, by default Azure Automation knows nothing about these modules. We need to import them first for Azure Automation to be able to properly configure our servers. Think of this as being the equivalent of putting the required modules and resources on a Pull Server for your registered nodes to consume (in an on-premises type of scenario). To import a resource, you need to go back to the main Azure Automation screen and click on Assets.

On the next screen, select Modules.

Adding Azure Automation Module

From here we have two choices: upload the required resources as individual .zip files from our local machine, or Import them from the PowerShellGallery.com repository. In my case, I choose to import them from the gallery, therefore I need to click on Browse gallery.

PowerShell Gallery Import

In the search box, type in the name of the first module we are trying to import: xDSCDomainJoin. Select the proper module from the search results by clicking on it.

Import DSC Domain Join

To finalize the import process, click on the Import icon.

Module imported

On the next screen, simply click OK.

Imported Module DSC

Repeat the same import process with the remaining missing modules: xNetworking, SharePointDSC, and xSQLServer.

5 – Initiate Compilation Job

In an on-premises scenario, you need to call the Configuration keyword of your DSC script in order for it to get “compiled” as a .MOF file. In Azure Automation, this is normally done by clicking on your DSC Configuration (uploaded at Step 3 above), and by clicking on Compile. However, in our case, we have credentials that need to be passed to our configuration. Therefore, instead of manually initiating the compilation job from the Azure Portal, we will use a local PowerShell script to remotely initiate the compilation job, allowing us to pass in parameters.

We will be using the following PowerShell script to remotely initiate that compilation job. Note that these are all the parameters we mentioned previously that are simply passed up to my Azure Automation Account.

$ProductKey = Read-Host "Please enter your SharePoint 2016 Product Key"
$MachineName = "SPTechCon-SA"
$ConfigData = @{
    AllNodes = @(
        @{
            NodeName = $MachineName
            PSDscAllowPlainTextPassword = $True
        }
    )
}

$Parameters = @{
    ParamDomain = "contoso.com"
    ParamInternalDomainControllerIP = "10.0.10.5"; 
    ParamMachineName= $MachineName
    ParamProductKey = $ProductKey 
    ParamUsername = "contoso\sp_farm"
    ParamPassword = "pass@word1"
    ParamShareName = "SPTechCon-Share"
}

Login-AzureRMAccount
Start-AzureRmAutomationDscCompilationJob -ResourceGroupName "SPTechCon" -AutomationAccountName "SPTechCon-Automation" -ConfigurationName "SharePoint2016StandAlone" -ConfigurationData $ConfigData -Parameters $Parameters

Upon executing this script, you will get prompted to enter your Azure Credentials, which you’ll need to do in order for the compilation jo to get queued up.

Provide Azure credentials

The script should only take a second or two to execute and will automatically initiate a compilation job in Azure.

Azure Automation DSC Compilation

Give Azure about5 minutes to initiate and finalize the compilation. Once the job has completed, the compilation status will get updated to Completed.

Completed Compilation of Azure DSC

6 – Register a DSC Node

If we recap what we have done so far, we started off by creating a new Azure IaaS VM that we wish to configure as a SharePoint 2016 Standalone development machine. We have then wrote the Desired State Configuration script for it, and have uploaded and compiled it into Azure. Now what we need to do is actually associate the VM we created with the DSC script we’ve uploaded. To do this, you need to go back to your Azure Automation’s account main page, and this time click on DSC Nodes.

Register an Azure DSC Node

Azure Automation gives you the option of managing both Azure and on-premises Virtual Machines. On-premises Virtual Machines will be covered in another article, when time permits. In our case we want to register an existing Azure VM. Click on Add Azure VM.

Register Azure VM with DSC

Azure Automation will then ask you for two things: The VM you wish to register, and the DSC Configuration to associate with it. Start off by clicking on Virtual Machines, and select the Virtual Machine we created from the list (in my case SPTechCon-SA). Click OK. One thing that is interesting to note here, is that because we have generalized our DSC script (meaning not valus are hardcoded in it), we can easily select multiple VMs in this step and they will each get assigned the exact same configuration.

Associate Azure VM with Automation DSC in Azure

Now that you have selected your VM, it’s time to pick the DSC Configuration we wish to deploy onto it. Click on Registration. From the Node Configuration Name, pick the Node Configuration we compiled previously. The rest of the properties listed on the page should look familiar to you. They represent the LCM settings that can normally be set via PowerShell in on-premises scenarios. Leave everything as default, with the exception of the Reboot Node if Needed checkbox that absolutely need to be checked for the installation to complete properly. Click OK.

Associate Configuration

The last thing left for us to do now is initiate the registration process by clicking on Create.

Initiate DSC Registration

Now sit back and relax, your machine is going to go and configure itself. Depending on several factors (machine size, region, etc.) the process may take up to 45 minutes to complete

How does it Work?

If you were to connect to your VM before registering it to the Azure Automation Account and run the Get-DSCLocalConfigurationManager cmdlet on it, you would see that by default the machine’s LCM is set to PUSH mode.

Get-DSCLocalConfigurationManager

Upon registering your machine against the Azure Automation Account, a DSC Extension is assigned to your VM. That extension will automatically change the configuration of the machine’s LCM to set it in PULL mode and register it against your Azure Automation’s Pull Server endpoint.

DSC Azure Pull mode

My Microsoft Virtual Academy Session on Office 365 Development

I just realized that I never truly advertised on my blog the session I delivered back in March 2015 for the Microsoft Virtual Academy in Redmond, on Office 365 Development. This full day session was recorded live on March 24th, with my co-host Christopher Harrison. We had a blast! The audience asked several great questions throughout the day. Topics covered included OAuth for the new SharePoint Online Add-In model (still apps at the time), Office 365 apps development, as well as an overview of Azure authentication for your Office 365 apps. You can go ahead and listen to my awesome French Canadian accent right now, and learn all about apps and SharePoint add-ins in Office 365 by going to:

https://www.microsoftvirtualacademy.com/en-us/training-courses/sharepoint-online-development-for-office-365-10523






Bridging the Gap Between IT Pros & Devs using PowerShell

The title of this article also happens to be the title of my PowerShell book. For a long tiem I’ve been wanting to go and write a non-technical article explaining how I envision the future of traditional IT shops with the rapid growth of PowerShell in the industry. When I first started working with SharePoint 2003 back in 2006, my role was to develop several webparts and application pages for my company. The environment I was maintaining had over 1,500 site collections with about 150 sites each. Whenever I created a new webpart, I had to go back and add it retro-actively to all sites (150 * 1,500 = 225,000 sites). Off course there was not way in hell I was going to do this manually. Instead, I started creating new console applications from within Visual Studio (VS 2005 at the time), and had to my application loop through all site collections, through all sites, and add the webpart to the main page of each one using the SharePoint Object Model and some C# code. Then my application was packaged as an .exe (a.k.a. total black box), and this executable file was sent off to the servers’ administrators for them to execute directly on the SharePoint servers (starting with the dev environment off course). I can just imagine being in their shoes, where some crazy developer just gave me a piece of executable, that I need to trust does20150415-01.png what the developers says it does, and then simply have to execute it on the server and wait for hours for it to complete. I used to be known as the “Dev cow-boy” back then due to my total lack of risk adversion 🙂 Depending on the timeframes I had, I would even threat the administrators with some verbose logging of what was really happening in the background…..if I had the time. Otherwise, they would be starting at a good old black MS-DOS style dialog for quite a while.

That is how I use to do it back in the days. The dev guys were doing the work, and then the admins had to trust them entirely, and only be invovled at the very end of the process, whenever the code was all finalized. They had no say really in the code’s logic, and had no exposure to the SharePoint object model. This very often initiated very interesting discussions between the dev teams and the administrators. “You need to activate the feature at the SPSite level!”……”Say what???”. On one end you had developers spending their times in C# and the SharePoint object model, and on the other end, you had the administrators trying to do everything using batch files and STSAdm.exlifelong-learning-skills-gap.jpge. Looking back at it, I think the worst part was that developers used to improvised them as being adminsitrators. I remember my team creating such a .exe console app to automate the creation of Shared Service Providers (SSPs), the ancestors of the SharePoint 2010 Service Application architecture. This is some scary stuff, since the devs at total control over components that could directly have a huge negative impact on the overall stability of the environment (god knows that th SSPs were shaky at best). Back then, the gap between IT Pros and the developers was incredibly hard to bridge.

Then towards the end of life of SharePoint 2007 (beginning of year 2009), a new technology emerged at Microsoft: PowerShell. This technology opened up a lot of possibilities for both the developers and the IT Pros. First off, it was for most scenario, faster to accomplish a task than the STSAdm. Secondly, it now allowed developers, who were willing to learn the language, to provide their servers admins with a clear text .ps1 file that they could open and read instead of the scealed box that was the console applications my team used to write. Even if the IT Pros didn’t want to learn the object model and understand exactly what each line of code was doing, it was at least giving them the false feeling that it was not only a Dev thing anomore, and that they had some little more control as to what was being execued on their servers. One problem remained however, in order for one to write an efficient script, they had to learn and understand the SharePoint Object model. Back then, with SharePoint 2007, you didn’t have any PowerShell cmdlets to help you out. So for example, if you wimage002.pngished to acquired the number of items contained in a specific SharePoint list, say “Fruits”, your code would have looked like the following:

[Void][System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint”)
$site = New-Object Microsoft.SharePoint.SPSite(“http://portal”)
$web = $site.RootWeb
$list = $web.Lists[“Fruits”]
$count = $list.Items.Count

Therefore, even if there was some light at the end of the tunnel, there was still a huge gap in that in the eyes of IT Pros, the lines of code above are dev specific and most of them had no interest in learning how to become a programmer.

Then at the end of year 2009, Microsoft revealed what was to become SharePoint 2010, and everything changed. Microsoft introduced over 400 SharePoint specific PowerShell cmdlets. These “shortcut” methods made the code required to accomplish a set of very powerful actions against a SharePoint environment extremely simple, and most of all, human readable to both IT Pros and Devs. I think that this is where the true power of PowerShell resides. Both developers and IT Pros were now given the tools to write their own scripts. Off course IT Pros still had to learn a bit about the Object Model if they wished to interact with what I consider to be lower level objects (lists, items, views, pages, etc), and developers still had to understand some SharePoint architecture concepts if they wished to interact with higher level objects (Shared services applications, Web Applications, etc.), but all in all, the gap between devs and admins was being bridged! Based on my example above, with the venue of SharePoint specific PowerShell cmdlets, one could now achieve the same result with the following lines of code:

$web = Get-SPWeb http://localhost

$list = $web.Lists[“Fruits”]

$count = $list.Items.Count

3 lines instead of 6, and the code if much more readable.  PowerShell is just that great. It completely changed the story for SharePoint as far as I am concerned. You guys just stay tuned for what is coming up with SharePoint 2016 🙂 !