Email Concurrent Connections to Web Front End – SharePoint 2013


If you need to find out how many concurrent user connections on to your WFE, you need to use the below script.  THe script uses Win32_PerfFormattedData_W3SVC_WebService performance Counter to give you exactly how many people are hitting a WFE and will send an email.  I have created a scheduled task that runs every hour from 9 ET to 4 ET.  
Add-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue
#The mail address of who will receive the backup exception message
$from = “spadmin@contoso.com”
#Send email function
function SendMail($subject, $body)
{
    try
    {
        #Getting SMTP server name and Outbound mail sender address
        $caWebApp = (Get-SPWebApplication -IncludeCentralAdministration) | ? { $_.IsAdministrationWebApplication -eq $true }
        $smtpServer = $caWebApp.OutboundMailServiceInstance.Server.Address
        $smtp = new-object Net.Mail.SmtpClient($smtpServer)

        #Creating a Mail object
        $message = New-Object System.Net.Mail.MailMessage
        $message.Subject = $subject
        $message.Body = $body
        $To = someone@contoso.com
        $message.To.Add($To1)
        $message.From = $from
             
        #Creating SMTP server object
        
    
        #Sending email
        $smtp.Send($message)
        Write-Host "Email has been Sent!"
    }
    catch [System.Exception]
    {
        Write-Host "Mail Sending Error:" $_.Exception.Message -ForegroundColor Red
    }
}

$String = ""
$String +="==============================================================================`n"
$String +="                 !!!!!    Connections Alert     !!!!!  `n"
$String +="==============================================================================`n"

#Change the value as per need
[system.int32]$Threshold = 100
[System.Boolean]$EmailNeed = $false
Write-Host "Server      : Connections"
Write-Host "============================"
$String +="Server            :     Connections Count`n"
$String +="==============================================================================`n"
$ServerCSV = ""

Get-WmiObject -Class Win32_PerfFormattedData_W3SVC_WebService -ComputerName Server1,Server2,Server3,Server4 | Where {$_.Name -eq "_Total"} | % `
{
    $ServerName = $_.__SERVER;
    $Connections = $_.CurrentConnections
    $String +="$ServerName : $Connections`n"
    Write-Host "$ServerName : $Connections" 
}

$String +="`n`n==============================================================================`n"
$String +="This is just an alert.  Please contact someone@contoso.com for more information.`n"

$Date = Get-Date
SendMail -subject "SharePoint : ! Connections !: Time: $Date" -body $String

Write-Host "Done.."
#Examples of how to find the required Counters
#Get-WmiObject -List | ? Name -like "*NetLogon*"
#Get-WmiObject -List | ? Name -like "*NetLogon*" | Select Name
#Get-WmiObject -List | ? Name -like "*NetLogon*" | Select -ExpandProperty Properties
#Get-WmiObject -Class Win32_PerfFormattedData_Counters_Netlogon -ComputerName Server1,Server2,Server3,Server4 | Where {$_.Name -eq "_Total"} | % {Write-Host $_.__SERVER;Write-Host $_.SemaphoreTimeouts}
Save the above script as GetConnect-FromServes.ps1

Scheduled Task XML

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2015-08-20T15:54:47.5695947</Date>
    <Author>Domain\UserName</Author>
  </RegistrationInfo>
  <Triggers>
    <CalendarTrigger>
      <Repetition>
        <Interval>PT5M</Interval>
        <Duration>P1D</Duration>
        <StopAtDurationEnd>false</StopAtDurationEnd>
      </Repetition>
      <StartBoundary>2015-08-20T15:52:04.6019445</StartBoundary>
      <Enabled>true</Enabled>
      <ScheduleByDay>
        <DaysInterval>1</DaysInterval>
      </ScheduleByDay>
    </CalendarTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <UserId>Domain\UserName</UserId>
      <LogonType>Password</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>false</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
      <Arguments>-ExecutionPolicy Bypass E:\Scripts\GetConnect-FromServes.ps1</Arguments>
    </Exec>
  </Actions>
</Task>
Save the above XML as File and Open the Task in Task Schedule.  Change Credentials and Script Locations.

Export all SharePoint Profiles to CSV – PowerShell


Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue

$CSV = "C:\UserProfiles.csv"

$contextSiteUrl = "https://mysites.contoso.com/"
$serviceContext = Get-SPServiceContext -Site $contextSiteUrl
$UPMGR = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext);
$AllUserProfile= $UPMGR.GetEnumerator()

$Properties = @( 
            "SID", "ADGuid", "AccountName","FirstName","LastName","PreferredName","CountryCode","PersonalUrl","WorkPhone","Office","Department","Title","Manager","AboutMe","UserName","SPS-Skills","SPS-School","SPS-Dotted-line",
            "SPS-Peers","SPS-Responsibility","SPS-PastProjects","SPS-Interests","SPS-SipAddress","SPS-HireDate","SPS-Location","SPS-TimeZone","SPS-StatusNotes","Assistant","WorkEmail","SPS-ClaimID","SPS-ClaimProviderID","SPS-ClaimProviderType",
            "CellPhone","Fax","HomePhone","PictureURL"
           )

$Profilecollection = @()

foreach ($aProfile in $AllUserProfile) {
   $UserProfile = "" | select $Properties
   foreach ($field in $Properties) {
     if($aProfile[$field].Property.IsMultivalued) {
        try{
            $UserProfile.$field = $aProfile[$field] -join "|" } catch {}
     } else {
        try{
            $UserProfile.$field = $aProfile[$field].Value } catch {}
     }
   }
   if($aProfile.PersonalSite -ne $null)
     {
        try{
        $UserProfile.PersonalUrl = $aProfile.PersonalSite.Url  
        } catch {}
     }
   $Profilecollection += $UserProfile
}

$Profilecollection | Export-Csv $CSV -NoTypeInformation

Bulk Create or Fixing Personal Sites after Migration/Upgrade


I was involved in many projects where we were upgrading different versions of SharePoint environments to SharePoint 2013.  Most of the content migration was done using Database attach process.  We have migration servers where content databases are being upgrade from 2007 to 2010 and 2010 to 2013.  During the migration many users try to access their personal sites which are replaced by upgraded sites from old versions.  Additionally during testing many users see their personal sites are showing “We are working for  you” or unable to open the site at all becuase their user id is migrated from old domain to new domain. 

Some users reported that although their personal site exist but they were unable to see and post any newsfeeds.  Once I review I found the feed url of some of the profiles were either empty or wrong.  Some users were not owners of their personal sites as their user names were changed.

Step 1 was to export and review all users profiles so that junk profiles and mysites can be removed.  I write a script that can iterate through the profiles and try to make a decision if profile has any of the issues.  Once the profiles were identitfied I used to fix them in small chunks.  But later I was told to fix this issue and for over 50,000 users profiles and my sites.  Customer also requested that the my sites of users must be created in advanced before the launch.  I personal feel that it is not a great idea but as company is launching a large social platform around newsfeed and mysites, I had to do it.  The script to achieve all that is below.  Use it as as needed.  It cover most scanario but please test it in labs before running it on production. 

The average time to create personal site during busy working day was 6 minutes but during weekend changed to 1 minute.   The script can be stoped and started by removing all the entries before the last one manually.  I have not implemented any such logic to start automatically at last step.  The script uses transcript so you can review how many profiles fails to fix or personal sites failed.

CSV Format : Save the Final.CSV which has the following format.
AccountName,PropertyName,UserName
Domain\User1,PropertyValue,User1

Add-PSSnapIn "Microsoft.SharePoint.Powershell" -ErrorAction SilentlyContinue
$FileName = "C:\CreatePersonalSitesBulk\CreatePersonalSiteTranscript" + (Get-Date).tostring("dd-MM-yyyy-hh-mm-ss") + ".txt"
Start-Transcript -Path $FileName

$NewMySiteCount = 0
$MySiteProcessed = 0

$ContextSiteUrl = "https://mysites.contoso.com"
$site = new-object Microsoft.SharePoint.SPSite($ContextSiteUrl);  
$ServiceContext = [Microsoft.SharePoint.SPServiceContext]::GetContext($site);  
$ProfileManager = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($ServiceContext);  

Write-Output "`nUser Profile Context Loaded Successfully"
$Data = Import-Csv -Path 'C:\CreatePersonalSitesBulk\Final.csv'
Write-Output "`nCSV Imported Sucessfully..."

if($Data)
{
    foreach($aUser in $Data)
    {
         $GlobalStartDate=GET-DATE
         [string]$AccountName = $aUser.AccountName
         $UserName = $aUser.UserName
         $CountryCode = $aUser.CountryCode
         if($AccountName)
         {
            Write-Output "`n************************************************"
            Write-Output "`nCurrent User is $AccountName"
            Write-Output "Sleeping for 5 Seconds..."
            try
            {
                $CurrentProfile = $ProfileManager.GetUserProfile($AccountName);
            }
            catch
            {
                try
                {
                    Write-Output "`nTry to Grab the Profile from Profile Store Again!. Cover the Timeout"
                    $CurrentProfile = $ProfileManager.GetUserProfile($AccountName);
                }
                catch
                {
                    $CurrentProfile = $null
                }
            }
            if($CurrentProfile -ne $null)
            {
                Write-Output "`nUser Profile Exit in Profile Store..."
                $AccountName = $CurrentProfile["AccountName"].Value
                Write-Output "`nReading Account Name from Profile... $AccountName"
                Write-Output "`nChecking If Personal Site Exist in Profile..."
                $PersonalSite = $CurrentProfile.PersonalSite
                $aStartDate=GET-DATE
                if($PersonalSite -ne $null)
                {
                    Write-Output "`nPersonal Site Does Exists in Profile with URL: $PersonalSite"
                    Write-Output "`nChecking for Feed Identifier..."
                    $FeedUrl = $CurrentProfile.FeedIdentifier
                    if($FeedUrl -ne $null)
                    {
                        Write-Output "`nFeed Url is not empty so Skipping to Next Profile."
                        Write-Output "`nPersonal Space Url" 
                        Write-Output $CurrentProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PersonalSpace].Value
                        Write-Output "Feed Url Exist : $FeedUrl "
                        Write-Output "All Looks Good. so Skipping this user" 
                    }
                    else
                    {
                        Write-Output "`nFeed Url is empty so creating the Url..."
                        $microFeedId = ($PersonalSite.Url + ";1." + $PersonalSite.ID.ToString().Replace("-", "") + "." + $PersonalSite.RootWeb.ID.ToString().Replace("-", "") + "." + $PersonalSite.RootWeb.Lists["Microfeed"].ID.ToString().Replace("-", "") + ".0c37852b34d0418e91c62ac25af4be5b")
                        $CurrentProfile["SPS-FeedIdentifier"].Value = $microFeedId
                        $CurrentProfile.Commit()
                        Write-Output "`nFeed Url is is Updated to Profile..."
                    }
                    Write-Output "`nPersonal Site Object is Disposed..."
                    $PersonalSite.Dispose()
                    $MySiteProcessed +=1
                    $anEndDate=GET-DATE
                    $anGlobalTimeSpan = NEW-TIMESPAN –Start $aStartDate –End $anEndDate
                    Write-Output "My Sites Processed Complete : $NewMySiteCount : Profiles Processed Count : $MySiteProcessed : TIme Take for $UserName is : $anGlobalTimeSpan"
                }
                else
                {
                    Write-Output "`nPersonal Site is not linked to User Profile... Now Checking if MySite Exists..."
                    $MySiteUrl = $ContextSiteUrl + "/personal/$UserName"
                    Write-Output "`nMy Site Url will be  $MySiteUrl"
                    $MSite = Get-SPSite $MySiteUrl -ErrorAction SilentlyContinue
                    if($MSite -ne $null)
                    {
                        Write-Output "`nMy Site is not null so updating User Profile PersonalSpace property..."
                        $CurrentProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PersonalSpace].Value = ""
                        $CurrentProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PersonalSpace].Value = "/personal/$UserName"
                        Write-Output "`nCheck FeedIdentifier property for the Profile..."
                        $FeedUrl = $CurrentProfile.FeedIdentifier
                        if($FeedUrl -eq $null)
                        {
                            Write-Output "`nCheck FeedIdentifier is empty..."
                            $microFeedId = ($MSite.Url + ";1." + $MSite.ID.ToString().Replace("-", "") + "." + $MSite.RootWeb.ID.ToString().Replace("-", "") + "." + $MSite.RootWeb.Lists["Microfeed"].ID.ToString().Replace("-", "") + ".0c37852b34d0418e91c62ac25af4be5b")
                            Write-Output "`nCreating and Setting the Feed Identifier..."
                            $CurrentProfile["SPS-FeedIdentifier"].Value = $microFeedId
                        }
                        $CurrentProfile.Commit()
                        Write-Output "`nProfile updates has been commited..."
                        
                        try
                        {
                            Set-SPSite -Identity $MSite -OwnerAlias $AccountName -ErrorAction SilentlyContinue
                            Write-Output "`nProfil User Added As Ownere..."
                        }
                        catch [Exception]
                        {}
                        Write-Output "`nDisposing MySite Object..."
                        $MSite.Dispose()
                        Write-Output "`nProfile properties Personal Space and feed has been updated."
                        $MySiteProcessed +=1
                        Write-Output "My Sites Processed Complete : $NewMySiteCount : Profiles Processed Count : $MySiteProcessed"
                    }
                    else
                    {
                          Write-Output "`nPersonal Site is Null...Creating Personal Site..."
                          try
                          {
                               $StartDate=GET-DATE
                               $CurrentProfile.CreatePersonalSite()
                               $EndDate=GET-DATE
                               $TimeSpan = NEW-TIMESPAN –Start $StartDate –End $EndDate
                               Write-Output "`nFirst Time Personal Site has been Created Successfully in ... $TimeSpan"
                               $NewMySiteCount += 1
                               $MySiteProcessed +=1
                          }
                          catch 
                          {
                            $ErrorMessage = $_.Exception.Message
                            $FailedItem = $_.Exception.ItemName
                            Write-Output $ErrorMessage
                            Write-Output $_.Exception.ToString()
                            Write-Output "`nPersonal Site Creation has failed for User..." $AccountName
                            Write-Output "`nException Occured. Tyring One more Time after refresh."
                            try
                            {
                                $CurrentProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PersonalSpace].Value = ""
                                $CurrentProfile.Commit()
                                $StartDate=GET-DATE
                                $CurrentProfile.CreatePersonalSite()
                                $TimeSpan = NEW-TIMESPAN –Start $StartDate –End $EndDate
                                 Write-Output "`n2nd Try Personal Site has been Created Successfully in ... $TimeSpan"
                                $NewMySiteCount += 1
                                $MySiteProcessed +=1
                            }
                            catch 
                            {
                                $ErrorMessage = $_.Exception.Message
                                $FailedItem = $_.Exception.ItemName                           
                                Write-Output "`nException Occured in 2nd Attempt for $AccountName...Skipping it."
                                $CurrentProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PersonalSpace].Value = ""
                                $CurrentProfile.Commit()
                            }

                           
                          }
                          $GlobalEndDate=GET-DATE
                          $GlobalTimeSpan = NEW-TIMESPAN –Start $GlobalStartDate –End $GlobalEndDate
                          Write-Output "My Sites Created Count: $NewMySiteCount : MySite Processed Count : $MySiteProcessed : TIme Take for $UserName is : $GlobalTimeSpan"
                          Write-Output "`n************************************************"
                    }
                    Write-Output "`n************************************************"
                    
                }
            }
            else
            {
                Write-Output "User Profile Does not Exist for $AccountName"
            }
         }
    }
}
Stop-Transcript

Create Site Columns, Content Type and Add to Library using PowerShell


A good script to have for testing.

Add-PSSnapIn "Microsoft.SharePoint.Powershell"
#Get the site collection and web object
$siteColl = Get-SPSite -Identity "https://portal.contoso.com"
function Create-SiteColumnText ($siteColl, $CustomColumn)
{
    $rootWeb = $siteColl.RootWeb
    #Assign fieldXMLString variable with field XML for site column
    $fieldXMLString = "<Field Type='Text' Name='$CustomColumn'
    Description='Indicate date of last and next meeting and names of current members.'
    DisplayName='$CustomColumn'
    StaticName='$CustomColumn'
    Group='Custom Columns'
    Hidden='FALSE'
    Required='FALSE'
    Sealed='FALSE'
    ShowInDisplayForm='TRUE'
    ShowInEditForm='TRUE'
    ShowInListSettings='TRUE'
    ShowInNewForm='TRUE'></Field>"
    #See field XML on console
    #write-host $fieldXMLString
    #Create site column from XML string
    $rootWeb.Fields.AddFieldAsXml($fieldXMLString) 
    Write-Host "Column Added"
}

function Create-SiteColumnChoice ($siteColl, $CustomColumn)
{
    $rootWeb = $siteColl.RootWeb
    #Assign fieldXMLString variable with field XML for site column
    $fieldXMLString = "<Field Type='Choice' 
    Name='$CustomColumn'
    Description='Choice.'
    Format='Dropdown'
    DisplayName='$CustomColumn'
    StaticName='$CustomColumn'
    Group='Custom Columns'
    Hidden='FALSE'
    Required='FALSE'
    Sealed='FALSE'
    ShowInDisplayForm='TRUE'
    ShowInEditForm='TRUE'
    ShowInListSettings='TRUE'
    ShowInNewForm='TRUE'>
    <Default>Specification</Default>
                <CHOICES>
                  <CHOICE>Specification</CHOICE>
                  <CHOICE>Development</CHOICE>
                  <CHOICE>Test</CHOICE>
                  <CHOICE>Documentation</CHOICE>
                </CHOICES>
    </Field>"
    #See field XML on console
    #write-host $fieldXMLString
    #Create site column from XML string
    $rootWeb.Fields.AddFieldAsXml($fieldXMLString) 
    Write-Host "Column Added"
}


Create-SiteColumnText -siteColl $siteColl -CustomColumn "MyCustomColumn1"
Create-SiteColumnText -siteColl $siteColl -CustomColumn "MyCustomColumn2"
Create-SiteColumnText -siteColl $siteColl -CustomColumn "MyCustomColumn3"
Create-SiteColumnChoice -siteColl $siteColl -CustomColumn "MyCustomColumn4"

$web = $siteColl.RootWeb
$ctypeName = "MyCustomContentType"
$ctypeParent = $web.availablecontenttypes["Document"]
$ctype = new-object Microsoft.SharePoint.SPContentType($ctypeParent, $web.contenttypes, $ctypeName)
$web.contenttypes.add($ctype)
$field = $web.fields.getfield("MyCustomColumn1")
$fieldLink = new-object Microsoft.SharePoint.SPFieldLink($field)
$ctype.fieldlinks.add($fieldLink)

$field = $web.fields.getfield("MyCustomColumn2")
$fieldLink = new-object Microsoft.SharePoint.SPFieldLink($field)
$ctype.fieldlinks.add($fieldLink)

$field = $web.fields.getfield("MyCustomColumn3")
$fieldLink = new-object Microsoft.SharePoint.SPFieldLink($field)
$ctype.fieldlinks.add($fieldLink)

$field = $web.fields.getfield("MyCustomColumn4")
$fieldLink = new-object Microsoft.SharePoint.SPFieldLink($field)
$ctype.fieldlinks.add($fieldLink)
$ctype.Update()

$Library =$Web.Lists["Documents"]
$Library.ContentTypesEnabled = $true

$Library.ContentTypes.Add($ctype);
$Library.Update()

Get Site Collection Size using PowerShell


Add-PSSnapin Microsoft.SharePoint.Powershell -ErrorAction SilentlyContinue
$Sites = (Get-SPContentDatabase Content_DB).Sites
$AllSites = @();
foreach($site in $Sites)
{
    $Site = Get-SPSite $site.Url
    $Size = $site.usage.storage
    $Size = $Size/1mb
    $ASite = New-Object PSObject
    Add-Member -input $ASite noteproperty 'Url' $Site.url
    Add-Member -input $ASite noteproperty 'Size' $Size
    $AllSites+=$ASite
    $site.Dispose()
}
$AllSites | Sort-Object descending  | Out-GridView