Wednesday, 7 November 2012

Configure people picker in Sharepoint to search the entire forest

Consider the situation where you have a multi-domain forest with MOSS/Sharepoint installed in a child domain. You need to be able to pick users from the entire forest and not only from child domain where Sharepoint is installed, but you can't. If you use people picker from Central Administration site then you are able to find users from entire forest.

Check if your people picker for that particular web application is configured correctly by running this command:

STSADM.exe -o getsiteuseraccountdirectorypath -url http://intranet

If you get output like this:
<SiteUserAccountDirectoryPath>DC=childdomain,DC=rootdomain,DC=local</SiteUserAccountDirectoryPath>

 Then you need to run the following command to change the people picker search scope to search the entire forest:

STSADM.exe -o setsiteuseraccountdirectorypath -path "" -url http://intranet

The setuseraccountdirectorypath property is actually intended to allow you to limit people picker searches to a sub OU, but in a multi-domain environment it is limited to domain level by default in MOSS 2007. So if you need to search the entire forest, you need to set directory path to "" like it is set for Central Administration web application.

Thursday, 18 October 2012

Setting up Meet URLs in Lync Server 2010 Multitenant

In this article I'm going to explain how I solved the problem of setting up the tenant meet URLs correctly in Lync Server 2010 Multitenant Pack for Parner Hosting. Microsoft released a special version of Lync Server 2010 intended just for hosting organizations that want to provide Lync services to multiple distinct organizations.

The deployment guide for this scenario can be very confusing in some sections and it takes multiple reading as well as good Lync 2010 background to get it right. But I still believe that some procedures in the document are not correct. The section that gave me the most trouble was about setting up simple URLs for hosted organizations.

In on-premise installation of Lync 2010 there is usually one SIP domain and one set of simple URLs. However, in multitenant installation there are multiple SIP domains, one SIP domain for each tenant.

The deployment guide in one section states that one should use Topology Builder to setup the meet URLs for a tenant. In a dynamic environment like multitenant hosting where you would use custom built provisioning service to set up tenants, you cannot rely on Topology Builder. Furthermore, if you try to run the scripts offered for setting up meet URLs (section 8.11.3), you would get the following error:

There must be an Active URL for Meet for each SIP Domain.

What this actually means is that you cannot set up one Meet URL for one SIP domain and assign it to one tenant organization and do the same for each tenant. That would make sense but it does not work. What you must do is create Meet URLs for all SIP domains and assign them all to a single tenant. And you must do that for each tenant.

This is the script I first wrote following the deployment guide:

#get your tenant GUID
$TenORgID = xxxx-xxxx-xxxx-xxxx-xxxx

#assing a SIP domain to a variable
$SIPDomain1 = "itsolutionbraindumps.com"

#generate a base URL for your Meet URL
$URL1 =  "https://meet.hoster.com/" + $SIPDomain1

#generate a Simple URL entry
$urlEntry1 = New-CsSimpleUrlEntry -Url $URL1

#generate a Simple URL for the meet component and assing all the variables
$simpleUrl1 = New-CsSimpleUrl -Component "meet" -Domain $SIPDomain1 -SimpleUrl $urlEntry1 -ActiveUrl $URL1

#assign the Simple URL to your tenant
Set-CsSimpleUrlConfiguration –Tenant $TenORgID -SimpleUrl @{Add=$simpleUrl1}  -ErrorAction Stop

If you wrote the same script it means you too followed the deployment guide. The script would work if you have only one SIP domain on the system, but if you have multiple SIP domains you would get the error above.

So, the solution is to modify the script to create Simple URLs for each SIP domain and assign all these Simple URLs to a single tenant. And you must do that for all your tenants. The good thing is that, as you add new tenants and create SIP domain for each tenant, you must create Simple URLs for all SIP domains that exist on your system at that time but you don't have to fix each and every tenant. So, for a single tenant system, you would only create one Simple URL. When you add your second tenant, you would create two Simple URLs and assign them to your second tenant, three Simple URLs for your third tenant and so forth. As you add your tenants, your Simple URLs list would grow for your new tenants but you won't have to go back and correct the previously added tenants.

So, for your second tenant, let's name him Fabrikam, you would have a script like this:

$TenORgID = xxxx-xxxx-xxxx-xxxx-xxxx

$SIPDomain1 = "itsolutionbraindumps.com"
$SIPDomain2 = "fabrikam.com"


$URL1 =  "https://meet.hoster.com/" + $SIPDomain1
$URL2 =  "
https://meet.hoster.com/" + $SIPDomain2

$urlEntry1 = New-CsSimpleUrlEntry -Url $URL1
$urlEntry2 = New-CsSimpleUrlEntry -Url $URL2


$simpleUrl1 = New-CsSimpleUrl -Component "meet" -Domain $SIPDomain1 -SimpleUrl $urlEntry1 -ActiveUrl $URL1
$simpleUrl2 = New-CsSimpleUrl -Component "meet" -Domain $SIPDomain2 -SimpleUrl $urlEntry2 -ActiveUrl $URL2


Set-CsSimpleUrlConfiguration –Tenant $TenORgID -SimpleUrl @{Add=$simpleUrl1,$simpleUrl2}  -ErrorAction Stop

But of course, in a dynamic environment you must have more automated way of creating Meet URLs for new tenants. So I wrote the following script:


$OUName = "IT Solution Braindumps"
$PathRoot = "OU=OCS Tenants,DC=cloud,DC=local"
$TargetOU = "OU="+$OUName+","+$pathRoot
$TenantOU = Get-ADOrganizationalUnit -Identity $TargetOU -Properties msRTCSIP-TenantId -Server "ad01.cloud.local"
$TenORgID = New-Object -TypeName System.guid -ArgumentList $TenantOU.ObjectGUID

$Count = 0

$SIPDomain = @()
$URL = @()
$URLEntry = @()
$SimpleUrl = @()

$siparray = Get-CsSipDomain

foreach($sip in $siparray)
{
 $SIPDomain += $sip.Identity
 $URL += "https://meet.hoster.com/" + $SIPDomain[$Count]
 $URLEntry += New-CSSimpleUrlEntry -Url $URL[$Count]
 $SimpleUrl += New-CsSimpleUrl -Component "meet" -Domain $SIPDomain[$Count] -SimpleUrl $URLEntry[$Count] -ActiveUrl $URL[$Count]
 $Count++
}

Set-CsSimpleUrlConfiguration -Tenant $TenORgID -SimpleUrl @{Add=$SimpleUrl} -ErrorAction Stop

After the script executes you will get the following warning:

WARNING: Activation (Enable-CsComputer) may need to be re-run on impacted Front End servers.

Execute Enable-CsComputer and you are done.

The previous script fully automates generation of Simple URLs based on all existing SIP domains on a system and assignes them all to a tenant. When adding new tenant to your Lync 2010 hosting environment you can just automatically run this script.

Once you setup your tenant Meet URLs correctly, the tenant users would be able to succesfully schedule Online Meetings from Outlook and the meeting URL for other users to connect would be correctly populated.

 


There is one more thing to do. Tenant users must be configured with the correct Base URL for their tenant organization. Base URL is configured on the msRTCSIP-BaseSimpleUrl property for each tenant user in Active Directory. Here is how to do that:

$SIPDomain = "itsolutionbraindumps.com"
$CompanyName = "IT Solution Braindumps"
$BaseURL = "https://meet.hoster.com/"+$SIPDomain
$PathRoot = "OU=OCS Tenants,DC=cloud,DC=local"
$TargetOU = "OU="+$CompanyName+","+$PathRoot
$OUObject = Get-ADOrganizationalunit -Identity $TargetOU

Get-ADUser -LDAPFilter "(objectClass=user)" -SearchBase $TargetOU -Properties msRTCSIP-BaseSimpleUrl -Server "AD01.cloud.local" |Set-ADUser -Replace @{'msRTCSIP-BaseSimpleUrl'=$BaseURL}

Without setting this property your users would get this error when they click on the meet URL:

Error: Meeting URL is not valid.

Lync Server 2010 Logging Tool will also return this error:

Failed to find a domain mapping for this BaseURL

So you need to make sure that msRTCSIP-BaseSimpleUrl is set at the moment you provision tenant users.

There it is, I hope I made it clear enough for you to be able to set Meet URLs for tenant organizations in Lync. If you have troubles getting this to work please let me know. Also, if you have a better way to solve this problem, please share it here. I'm not going to exclude a possibility that I got everyhing wrong, but this is a configuration that works in my environment.

Wednesday, 17 October 2012

Trouble installing Exchange 2010 SP2 Rollup 4 v2

If you have trouble with the installation of KB2756485, that is, Rollup 4-v2 for Exchange Server 2010 SP2, you must run the installer from the elevated command prompt like this:

 
 

Once the installation starts, be patient because it can take a while.

If you just double click on the .msp file from Windows Explorer, the installation will start but it will very soon rollback and you will see this error in the application log:


Log Name:      Application
Source:        MsiInstaller
Date:          16.10.2012. 10:30:01
Event ID:      1024
Task Category: None
Level:         Error
Keywords:      Classic
User:          DOMAIN\username
Computer:     
exchange.contoso.com
Description:
Product: Microsoft Exchange Server - Update 'Update Rollup 4-v2 for Exchange Server 2010 Service Pack 2 (KB2756485) 14.2.318.4' could not be installed. Error code 1603. Windows Installer can create logs to help troubleshoot issues with installing software packages. Use the following link for instructions on turning on logging support:
http://go.microsoft.com/fwlink/?LinkId=23127

You may also ran into some blog posts that say to try setting the execution policy in Powershell from RemoteSigned to Unrestricted, but that did not help in my case.

There, I hope it helps someone.

Wednesday, 8 August 2012

Expiring password notifications with Powershell scripting

Let's say that your users have non-domain joined workstation, but use Outlook or Outlook Web Access to access your e-mail infrastructure. These users would never be notified that their domain password is about to expire. Once it does, their only resort would be to contact administrator and ask him to reset his or her password.

Because of this, I wrote a simple Powershell script that is scheduled to run every day and creates a list of user accounts which passwords will expire in less than 7 days and sends them an e-mail notification that they should change their password. Once a user receives this notification, he can use Outlook Web Access to change his password.

Some notes about the script:

  • The script uses System.Globalization.CultureInfo object to format the date returned for Croatian region (hr-HR). If you come from US, you can change hr-HR in the script to en-US
  • The $msg.Body property in the script contains HTML formatted text that user receives as an e-mail message body. You can modify this HTML code, for example, to include your company branding
  • You can modify the $OU property so that script only scopes your external users, users from a specific department, etc.
  • The script does not send e-mail to users whose passwords are already expired or who have "Password never expires" property set
  • You can modify how many days are left until password expiry before you start notifying users. Currently, script notifies users whose passwords expire in less than 7 days.
    You can modify the following line: $DaysToExpire.Days -lt 7
  • The policy works with default domain password policy and with Password Settings Object, a feature of Windows Server 2008 Active Directory
  • You must run the script on a domain controller or on a server with Remote Server Administration Tools installed
  • You must change the variable $smtpServer to your internal mail server (usually Exchange Hub Transport server) that can send e-mails to your users

Here is the script:


PowerGUI Script Editor
Import-Module ActiveDirectory

#System globalization
$ci = New-Object System.Globalization.CultureInfo("hr-HR")

#SMTP server name
$smtpServer = "smtp.domain.local"

#Creating SMTP server object
$smtp = new-object Net.Mail.SmtpClient($smtpServer)

#E-mail structure
Function EmailStructure($to,$expiryDate,$upn)
{

 #Creating a Mail object
 $msg = new-object Net.Mail.MailMessage

 $msg.IsBodyHtml = $true
 $msg.From = "administrator@domain.com"
 $msg.To.Add($to)
 $msg.Subject = "Password expiration notice"
 $msg.Body = "<html><body><font face='Arial'>This is an automatically generated message from Service Provider Exchange service.<br><br><b>Please note that the password for your account $upn will expire on $expiryDate.</b><br><br>Please change your password immediately or at least before this date as you will be unable to access the service without contacting your administrator.</font></body></html>"
 
 return $msg
}


#Set the target OU that will be searched for user accounts
$OU = "OU=Users,DC=domain,DC=local"

$ADAccounts = Get-ADUser -LDAPFilter "(objectClass=user)" -searchbase $OU -properties PasswordExpired, PasswordNeverExpires, PasswordLastSet, Mail, Enabled | Where-object {$_.Enabled -eq $true -and $_.PasswordNeverExpires -eq $false}

Foreach ($ADAccount in $ADAccounts)
{
 $accountFGPP = Get-ADUserResultantPasswordPolicy $ADAccount

                if ($accountFGPP -ne $null) {
                   $maxPasswordAgeTimeSpan = $accountFGPP.MaxPasswordAge
                } else {
                   $maxPasswordAgeTimeSpan = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
                }

  #Fill in the user variables
  $samAccountName = $ADAccount.samAccountName
  $userEmailAddress = $ADAccount.Mail
  $userPrincipalName = $ADAccount.UserPrincipalName

  if ($ADAccount.PasswordExpired) {
   Write-host "The password for account $samAccountName has expired!"
  } else {
   $ExpiryDate = $ADAccount.PasswordLastSet + $maxPasswordAgeTimeSpan
   Write-host "The password for account $samAccountName expires on: $ExpiryDate"

   $TodaysDate = Get-Date
   $DaysToExpire = $ExpiryDate - $TodaysDate
   #Write-Host $DaysToExpire.Days
   if ($DaysToExpire.Days -lt 7) {
    $expiryDate = $expiryDate.ToString("d",$ci)
    
    #Generate e-mail structure and send message
    $msg = EmailStructure $userEmailAddress $expiryDate $userPrincipalName
    $smtp.Send($msg)

    Write-Host "The expiration notification e-mail was sent to $userEmailAddress"
   }

  }
}

Tuesday, 3 July 2012

VLAN tagging problems in Windows Server 2012 Hyper-V

Now when the Release Candidate of Windows Server 2012 is available I started to evaluate its features, most of all Hyper-V because the company I work for has a Hyper-V based lab environment I can use to play with the new stuff.

We have two HP DL380 G5 servers in a cluster and I decided to evict one node from the Windows Server 2008 R2 cluster and install Windows Server 2012. You can read about it here.

Very soon I have encountered a problem with VLAN tagging. I set up NIC Teaming feature using two network cards. Network cards I used are HP NC7170 Dual Gigabit Server Adapter, but I had to uninstall these drivers and install Windows included drivers. Then my network cards appeared as Intel PRO/1000 MT Dual Port in Device Manager.

Physical network switches where these cards are connected to have ports configured as trunk ports so I can pass the VLAN tags from the operating system above. This worked fine using Windows Server 2008 R2 and HP Network Configuration Utility, but not with NIC Teaming and VLAN tagging in Windows Server 2012.

I could easily set up NIC Teaming, but when I tried to tag the traffic, either on the host OS or from the virtual machine, nothing would pass through to the switches. After a couple of days of troubleshooting I discovered that I must tell the NIC driver to pass through the tagged VLAN traffic. However, this was not exposed anywhere in the GUI but only in the registry.

If you have a similar problem, you must edit or create the VlanFiltering DWORD value in registry on the following location and set it to 0:

HKEY_LOCAL_MACHINE\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}\00xx

xx could be any number so you must open each key and look for your network adapter. If you are using NIC Teaming you probably need to do this for both adapters or all adapters that set up your team, in case you have more than two.


After you have changed the values, you can just go to Device Manager and disable and enable the network cards. This should be enough for the new setting to become active. It worked for me.

I hope this is only RC "feature" and it will be fixed in the final release.

Wednesday, 20 June 2012

Upgrade Windows Server 2008 R2 Hyper-V cluster to Windows Server 2012

With the RC version of Windows Server 2012 now available it is time to start preparing for the final version. Since the company I work for has implemented numerous Windows Server 2008 R2 Hyper-V cluster installations, one of the first tasks upon release will be to start upgrading them to 2012 version. I decided to skip extensive preparations and just start an in-place upgrade of our lab environment and see where it goes.

I've tried to do an in-place upgrade of one of the Windows Server 2008 R2 Enterprise Hyper-V cluster nodes, but I got an error message saying that I need to evict the node first and then do the upgrade. So I did just that. I've evicted the node and in-place upgrade went pretty smooth and I even did the whole process through Remote Desktop session. It took 30-45 minutes.

When the Windows Server 2012 booted up, I could not find my Failover Cluster Management Console. I needed to go to the Server Manager > Local Server > Manage > Remove Roles and Features and I removed and readded the Failover Clustering feature. The Failover Cluster Manager console was now available in Server Manager > Local Server > Tools combo box.






I've started up the Failover Cluster Manager console and I tried to connect to existing Windows 2008 R2 cluster, but I got the following message:



So my hopes that I could maybe join the Windows Server 2012 cluster node to the existing 2008 R2 cluster and just Live Migrate the virtual machines to the new node were gone. That's something that's available in VMware for some time now.

So, I had to build the entirely new cluster and import virtual machines into it. Creating Failover Cluster process is pretty much the same as the previous version. I've created a single node cluster, provisioned the 800 GB iSCSI LUN that will be my CSV disk and imported it under the Storage > Disk node in Failover Cluster Manager console and created a CSV from it.




Once I got my Windows 2012 Hyper-V cluster operational it was time to move virtual machines from old Windows Server 2008 R2 cluster to the new 2012 node. I just simply copied the VM from old CSV disk to the new CSV disk through Windows Explorer without first exporting the machine or any special preparations. Once I did that I started up Hyper-V Manager console on Windows 2012 and used the Import Virtual Machine wizard. I followed through the wizard and that was it! My VM was now imported and running on Windows Server 2012 Hyper-V.








Note: You will have to reconnect the VM network adapter of the importing machine to a valid Virtual Switch on your new 2012 Hyper-V server.




After I did that for all my VMs, now I had to make them clustered. I opened Failover Cluster Manager console, right click Roles and click Configure Role...


Choose Virtual Machine role....



Select the virtual machines that you want to make Highly Available...


And the virtual machines are now clustered.




This is excellent progress from the previous versions and pains of exporting and importing machines and using scripts to migrate clustered VMs.

Considering this procedure my guess is that I could just dismount the existing CSV LUN from the old cluster node and mount it to the new cluster node. Using Hyper-V Manager console I could simply just import the virtual machines one by one and then make them cluster aware. There is no provisioning of new LUNs, copying large amounts of data and long periods of downtime to move to the Windows Server 2012 Hyper-V cluster. So far so good from Microsoft.

Now I will dig up into SCVMM 2012 SP1 which is requirement if you want to manage Windows Server 2012 Hyper-V. SP1 has just recently been progressed to CTP2 (link). Maybe it will make the cluster migration process even easier.

Thursday, 26 January 2012

Antispam agents in Exchange 2010 SP1 /Hosting mode

Hi,

If you have played with unfortunate Exchange 2010 SP1 /Hosting mode installation or you are a real hoster that runs it in the production, then you maybe had problems with getting Antispam agents to work on your Hub transport server.

This special mode of Exchange 2010 SP1 installation has some limitations when compared to the regular Exchange 2010 installation. One of them is that you cannot use Exchange 2010 Edge Server role with EdgeSync. This means that you will have to use install-AntispamAgents.ps1 Powershell script to enable Antispam agents on your Hub transport servers.

The script works well, transport agents get installed and enabled. But you will soon notice that e-mail messages don't get stamped with SCL even if you have correctly configured the Content Filtering agent. Furthermore, if you have enabled Recipient Filtering agent too, you will see that all recipient addresses, even the existing ones in your tenant organizations will get rejected with "user unknown" message.

This is becase the Antispam agents simply do not work with Exhange 2010 SP1 /Hosting mode installation. Don't let this article fool you as it did me: http://technet.microsoft.com/en-us/library/ff923278.aspx

Here is an example of a behaviour you will notice with Content Filtering agent. The Get-AgentLog Powershell output on your Hub transport will show this:


RunspaceId      : 018024ed-1c5a-498a-8c15-087b1c81ed2e
Timestamp       : 1/24/2012 7:07:28 AM
SessionId       : 08CEA824BF825A40
IPAddress       : 111.222.111.222
MessageId       : <E9F3014D-C9FF-427F-91EA-0B4E253784A7@span.hr>
P1FromAddress   : Dinko.Fabricni@span.hr
P2FromAddresses : {Dinko.Fabricni@span.hr}
Recipients      : {dfabricni@example.net}
Agent           : Content Filter Agent
Event           : OnEndOfData
Action          : AcceptMessage
SmtpResponse    :
Reason          : SCL
ReasonData      : 0
Diagnostics     :

You can see that agent does its thing, but when you check the message headers in Outlook you will see that actually no SCL is provided:


X-MS-Exchange-Organization-AuthSource: EXHUBEXT01.cloud.local
X-MS-Exchange-Organization-AuthAs: Anonymous
X-MS-Exchange-Organization-PRD: span.hr
X-MS-Exchange-Organization-SenderIdResult: None
Received-SPF: None (EXHUBEXT01.cloud.local: Dinko.Fabricni@span.hr does not
designate permitted sender hosts)
X-MS-Exchange-Organization-Antispam-Report: SCLNotProvided

To make it even worse, ALL messages, even those that are 100% spam messages will receive SCL rating of 0 and you will see the SCLNotProvided in message headers.

We did raise this incident to Microsoft and got a confirmation that Antispam agents really do not work and that the documentation on Technet website (the article I linked) is misleading.