## Provisioning students with Office 365 ProPlus licenses

Interesting… I still seem to be working at UVM. There must be a story there, but you won’t read about it here.

Anyway, after getting back from my brief hiatus at Stanford University, I got back on the job of setting up Azure DirSync with Federated login to our in-house Web SSO platforms. I’ll need to post about the security changes required to make that work with UVM’s FERPA interpretation. To summarize, we got it working.

However, once students can log in to Office 365, we need to provision them with licenses. DirSync can’t do this, so I needed to script a task that will grant an office 365 ProPlus license to any un-provisioned active student. You will find the script, mostly unaltered, below. I just set it up as a scheduled task that runs sometime after the nightly in-house Active Directory update process.

To be useful outside of UVM, the code would need to be customized to handle the logic used in your organization to determine who is a student. We have extended the AD schema to include the eduPerson schema, and have populated the “eduPersonPrimaryAffiliation” attribute with “Student” for currently active students. If you do something different, have a look at the “Get-ADUser” line, and use a different LDAP query to fetch your student objects.

Enjoy.

# Provision-MSOLUsers.ps1 script, by J. Greg Mackinnon, 2014-07-30

#Provisions all active student accounts in Active Directory with an Office 365

#Requires:
# - PowerShell Module "MSOnline"
# - PowerShell Module "ActiveDirectory"
# - Azure AD account with rights to read account information and set license status
# - Credentials for this account, with password saved in a file, as detailed below.
# - Script runs as a user with rights to read the eduPersonAffiliation property of
#     all accounts in Active Directory.

#Create a credential file using the following procedure:
#    1. Log in as the user that will execute the script.
#    2. Execute the following line of code in PowerShell:
#    ConvertTo-SecureString -String 'password' -AsPlainText -Force
#      | ConvertFrom-SecureString | out-file "c:\path\to\cred\file"

Set-PSDebug -Strict

#Setup local variables:
[string] $to = 'sysAdmin@domain.com' [string]$from = 'ProvisioningScript@myhost.domain.com'
[string] $smtp = 'smtp.uvm.edu' [string]$msolUser = 'azureAdServiceAccount@myAzureDomain.domain.com'

#initialize log and counter:
[string[]] $log = @() [long]$pCount = 0

#initialize logging:
[string] $logFQPath = "yourLogLocation" New-Item -Path$logFQPath -ItemType file -Force

$sTime = get-date$log += "Provisioning report for Office 365/Azure AD for: " + ($sTime.ToString()) + "rn" function errLogMail ($err,$msg) { # Write error to log and e-mail function # Writes out the error object in$err to the global $log object. # Flushes the contents of the$log array to file,
# E-mails the log contents to the mail address specified in $to. [string]$except = $err.exception; [string]$invoke = $err.invocationInfo.Line; [string]$posMsg = $err.InvocationInfo.PositionMessage;$log +=  $msg + "rnrnException: rn$except rnrnError Position: rn$posMsg";$log | Out-File -FilePath $logFQPath -Append; [string]$subj = 'Office 365 Provisioning Script:  ERROR'
[string] $body =$log | % {$_ + "rn"} Send-MailMessage -To$to -From $from -Subject$subj -Body $body -SmtpServer$smtp
}

#Import PS Modules used by this script:
Import-Module MSOnline -ErrorAction SilentlyContinue
Import-Module ActiveDirectory -ErrorAction SilentlyContinue

#Get credentials for use with MS Online Services:
try {
$msolPwd = get-content 'c:\path\to\cred\file' | convertto-securestring -ErrorAction Stop ; } catch {$myError = $_ [string]$myMsg = "Error encountered getting creds from file."
errLogMail $myError$myMsg
exit 110
}
try {
$msolCreds = New-Object System.Management.Automation.PSCredential ($msolUser, $msolPwd) -ErrorAction Stop ; } catch {$myError = $_ [string]$myMsg = "Error encountered in generating credential object."
errLogMail $myError$myMsg
exit 120
}

#Connect to MS Online Services:
try {
#ErrorAction set to "Stop" for force any errors to be terminating errors.
# default behavior for connection errors is non-terminating, so the "catch" block will not be processed.
Connect-MsolService -Credential $msolCreds -ErrorAction Stop } catch {$myError = $_ [string]$myMsg = "Error encountered in connecting to MSOL Services."
errLogMail $myError$myMsg
exit 130
}
$log += "Connected to MS Online Services.rn" #Generate license report:$lics = @()
$ppsSub = Get-MsolAccountSku | ? {$_.accountSkuId -match 'OFFICESUBSCRIPTION_STUDENT'}
$log += 'Office 365 ProPlus for Student - license report:'$log += 'Total licenses: ' + $ppsSub.ActiveUnits$log += 'Consumed licenses: ' + $ppsSub.ConsumedUnits$log += 'Remaining licenses: ' + ($ppsSub.ActiveUnits -$ppsSub.ConsumedUnits) + "rn"

#Retrieve active student accounts into a hashtable:
[hashtable] $students = @{} try { get-aduser -LdapFilter '(&(ObjectClass=inetOrgPerson)(eduPersonAffiliation=Student))' -SearchBase 'ou=people,dc=campus,dc=ad,dc=uvm,dc=edu' -SearchScope Subtree -ErrorAction Stop | % {$students.Add($_.userPrincipalName,$_.Enabled)}
} catch {
$msg = "Error encountered in reading accounts from Active Directory." errLogMail$myError $myMsg exit 200 }$log += "Retrieved active students from Active Directory."
$log += "Active student count: " +$students.count

#Retrieve unprovisioned accounts from Azure AD:
[array] $ulUsers = @() try { #Note use of "Synchronized" to suppress processing of cloud-only accounts.$ulUsers += Get-MsolUser -UnlicensedUsersOnly -Synchronized -All -errorAction Stop
} catch {
$msg = "Error encountered in reading accounts from Azure AD. " errLogMail$myError $myMsg exit 300 }$log += "Retrieved unlicensed MSOL users."
$log += "Unlicensed user count: " +$ulUsers.Count + "rn"

#Provision any account in $ulUsers that also is in the$students array:
foreach ($u in$ulUsers) {
if ($students.get_item($u.UserPrincipalName) -eq $true) {$log += $u.UserPrincipalName + " is an active student." try {$u | Set-MsolUser -UsageLocation 'US' -ErrorAction Stop ;
$log += 'Successfully set the Office usage location for the user. ' } catch {$msg = = "Error encountered in setting Office 365 usage location to user. "
errLogMail $myError$myMsg
exit 410
}
try {
$u | Set-MsolUserLicense -AddLicenses uvmoffice:OFFICESUBSCRIPTION_STUDENT -ErrorAction Stop ;$log += 'Successfully set the Office license for the user. '
$pCount += 1 } catch {$msg = "Error encountered in assigning Office 365 license to user. "
errLogMail $myError$myMsg
exit 420
}
} else {
$log +=$u.UserPrincipalName + " is not an active student.  Skipped Provisioning."
}
}

#Add reporting details to the log:
$eTime = Get-Date$log += "rnProvisioning successfully completed at: " + ($eTime.ToString())$log += "Provisioned $pCount accounts."$tTime = new-timespan -Start $stime -End$etime
$log += 'Elapsed Time (hh:mm:ss): ' +$tTime.Hours + ':' + $tTime.Minutes + ':' +$tTime.Seconds

#Flush out the log and mail it:
$log | Out-File -FilePath$logFQPath -Append;
[string] $subj = 'Office 365 Provisioning Script: SUCCESS' [string]$body = $log | % {$_ + "rn"}
Send-MailMessage -To $to -From$from -Subject $subj -Body$body -SmtpServer $smtp  ## NTLMv2 – Troubleshooting Notes – NTLM Negotiation is a lie! We are now in the process, several years late, of trying to disallow use LM and NTLM authentication on our campus. First attempt? Train Wreck! For stage 1 of enforcement we chose to restrict our domain controllers to accept only NTLMv2 authentication, and reject LM/NTLM. Our thinking was that clients are supposed to negotiage for the the best auth security available, so by forcing NTLMv2 at the DC, we would be able to quickly flush out non-compliant clients. Futher, we assumed that there would not be a lot of non-compliant clients. Guess what? We were WRONG WRONG WRONG You know which clients are non-compliant? Windows 7, IE8, IE8, FireFox 4b10, Chrome 9. What? Really? It’s true… when accessing IIS web servers configured to use Windows authentication, with NTLMv2 enforced on the authenticating server, even Windows 7 will negotiate down to the weakest possible NTLM level. So… access to SharePoint 2007 from IE 9 beta on Win 7 falls back to welcome-to-the-80s LM authentication. This is very shocking, and annoying. When inspecting authentication negotiation traffic to the web server, we see that both client and server have indicated that NTLMv2 is supported, and that LM should not be used. However, when the client sends its authentication challenge response, it sends NTLM and LM responses only and not NTLMv2. Only by setting local security policy on the Windows client to “NTLMv2 only” are we able to prevent the sending of LM/NTLM. Negotiation is a lie! So, we are going to take this us with MS Support. Some resources that may be helpful going forward: http://technet.microsoft.com/en-us/magazine/2006.08.securitywatch.aspx A good explanation of how the “LMCompatibilityLevel” security setting works. Contains one major factual error… it claims that clients and servers will always negotiate the highest mutually-support LM auth level. This may be true for SMB/CIFS. It certainly is not true for web auth. http://kb.iu.edu/data/bamu.html Over at Indiana University, LM/NTLM has been disabled for some time. They have thorough documentation on configuring workstations to function properly in their more restrictive environment. This page concerns the use of SharePoint at IU, and the required configuration settings for various browsers and operating systems for their network. There is nothing specific to our problem here, but there is the implication that they experienced problems similar to our own. All of their docs state that you must configure your client to perform NTLMv2 only, or authentication will fail. Also included are notes on FireFox and Mac platforms, where client-side configuration apparently is required. http://kb.iu.edu/data/atpq.html An additional page that suggests that network clients will not be able to authenticate to IU resources unless they disabled NTLMv2 on the client. Concerning FireFox on Windows: http://forums.mozillazine.org/viewtopic.php?f=23&t=1648755 A developer notes that as of FF 3.6, FF uses Microsoft APIs for NTLM authentication. So, if FF is not performing NTLM auth as you expect, you need to look to your OS for the source of the problem. It’s true… we confirmed via packet capture that FF 4.0b10 and IE9RC both perform NTLM authentication in exactly the same way. Concerning Macintosh client settings: http://discussions.apple.com/thread.jspa?threadID=2369451 Recommended smb.conf file settings to enforce use of NTLMv2 on the Mac. References are made here to an “nsmb.conf” file, which is not a typographic error. More on this file here: http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man5/nsmb.conf.5.html ## Exploring Smart Cards and Windows Logon I have been having some “fun” this week in exploring two-factor authentication possibilities for Macintosh and Windows 7 clients when connecting to Server 2008 and Server 2003 resources, especially via RDP. Findings so far: All Smart Cards are not created Equal: • Microsoft released a new Cryptographic API (CNG) with Vista/Server 2008. This API allows Smart Cards to use the “Microsoft Smart Card Key Storage Provider” CSP (which, apparently, is part of the MS “Base CSP”), instead of a vendor-specific CSP, when generating certificates for Smart Card-based logon. You must still install a driver for each Smart Card, but very often these drivers are available though Windows Update. • Aladdin/SafeNet eTokens do not support CNG, so you cannot use CNG-based Certificate Templates (i.e. Server 2008-compatible templates) when issuing Smart Card Logon certificates to Aladdin eTokens. Thus, you must make sure that any Certificate Template that you use for Smart Cards are compatible with Server 2003 or earlier. You also will need a software stack including the eToken CSP and drivers before you will be able to perform Smart Card login using an eToken. • Gemalto .NET Smart Cards do support CNG, so you can use CNG certificate templates with these Smart Cards. • RSA SecurID Hybrid Authenticators do not appear to support CNG, according to their (dated) product data sheet, so I expect you would need a custom CSP to use these for Windows Logon, too. • PGP Desktop can be configured to use keys stored on a Smart Card to unlock PGP-WDE encrypted drives. However, only a few vendor’s Smart Cards are supported in this capacity, probably because PGP has hard-coded in support for only a few CSPs: • Aladdin eToken devices are supported both for WDE sign-on and for Administrator Key storage. • RSA Smart Cards are supported for WDS sign-on only • Gemalto Smart Cards are not supported at all. Web Service Enrollment was dead: • Most how-to sites you visit on certificate enrollment and smart card logon (including one in Tech Net) state that you should set up a Certificate Enrollment Agent Workstation to use the Web Services interface on your MS Certificate Server. Guess what? The procedures do not work anymore on Server 2008: http://technet.microsoft.com/en-us/library/cc753800(WS.10).aspx • Server 2008 R2 has a new Web Enrollment interface that supports Smart Card enrollment from Vista/Win7 workstations. Ban the use of Server 2008 R1! • Use the “Certificates” MMC snap-in in place of web enrollment if you must run Server 2008 (R1). RDGateway – Smart Card Authentication requires trust: • Smart Card auth to our Remote Desktop Gateway Load Balancing cluster (based on F5) was failing. Apparenly something about the SSL-offload configuration was creating a trustworthiness problem, and this was preventing Kerberos/Smart Card authentication from working. We switched to a TCP/Layer-4 forwarding config, and now Smart Card authorization works just fine. Note that the config that works is not the one that was recommended by F5. They are big into TCP offload over there. The thing is, for Kerberos and Smart Cards to work in an SSL-offload config, you need F5′s expensive Advanced Client Authenticaiton module. I do not need more cost and complexity, so we will keep things simple this time. ## ADFS 2 and SharePoint 2010 Integration Here is a quick entry on ADFS2 and SharePoint 2010 integration. It is not an implementation guide or end-to-end walkthough… that comes later, if we decide to implement this thing. At present, I am most interested in the model of SharePoint->ADFS2->Shibboleth, where the SP-STS trusts tokens from ADFS2. ADFS2 is part of a chained federation with our Shib service. ADFS will consume Shib tokens, then transform them for the benefit of SharePoint. However, I have no idea how to implement this solution at this time. There are a few-too-many blog entires out there detailing how to configure ADFS2 and SharePoint 2010 for integration. Trouble is, many of the step-by-step guides present contradictory configuration steps. I guess there is no substitute for a deep, working knowledge of ADFS 2, SAML, and other Federation topics. Here are some of the claims setup guides I have been working with: Here are additional configuration posts on the process of upgrading an existing SharePoint Web Application from “Windows” authentication to “Claims” authentication. The common denominators? 1. You must add a valid new user to your claims-aware web app before migrating existing users, or the web application will be inaccessible after migration (or indeed, even before migration!) 2. To trigger migration of users, you must invoke the “Migrate Users” method on your web app, E.g.: $wa = get-SpWebApplication "https://webappurl"
$wa.MigrateUsers($true)

The things here that seem very unclear to me are:  What exactly is being done when you invoke the “MigrateUsers” method on the Web Application object?  How does SharePoint map legacy “Windows” users to new “Claims” users?  Anyway, here are the links:

Pages containing information that I have found useful while contemplating how to pull off SharePoint 2010:

Many of these links, as it turns out, were already discovered by members at dirteam.com.  Doh…

## ADFS 2 Setup and Configuration

The following is a work in progress… Everything done so far works, but this is not yet a complete end-to-end deployment guide…

ADFS 2: new… improved?

There were lots of setup and configuration guides for ADFS 1.0, even though the product was nauseatingly difficult to understand.  Along comes ADFS 2… new, more powerful, better, more standards compliant… documentation reads like scrawl on a napkin.

Some setup quirks:

• Don’t forget that no current version of Windows Server includes ADFS 2.0… not even Server 2008 R2.  If you want to install ADFS 2.0 you must download it from MS and install.  DO NOT add the out-of-box ADFS role on Server 2008 R2.  It will confuse you, because it does not disclose which version of ADFS that it is.
• Since we are planning a farm, generate a farm SSL certificate on one of the ADFS servers, then export the certificate in PFX format (that is, with the private key), and restore the cert to the second ADFS server.
• It is considered desirable to put the ADFS database on a reliable external database server, but documentation on this options is limited to command line help.  Here is what I did:
1. On one of the ADFS servers, run:
Fsconfig.exe GenerateSQLScripts /ServiceAccount [account] /ScriptDestinationFolder [destination folder]
2. Install the appropriate version of the SQL Native Client on the ADFS servers.
3. On the SQL server, run the two scripts that were generated by the above command.  This will create two databases… AdfsConfiguration and AdfsArtifactStore, and set permissions for the service account specified.
4. Make sure that any firewalls on the SQL server will allow the ADFS servers to connect to it.
5. Since we use SQL mirroring, set up mirrors for these databases.  Add the service account to the list of accepted logins on the mirror server, since this will not be done automatically.
6. Generate certificates that will be used for the ADFS web site, token signing, and token decryption.  Put the certificates in the Windows certificate store of the local computer.
7. Preconfigure binding of the SSL certificate to the default IIS web site.
8. Configure the first server in the farm using the following syntax:
Fsconfig.exe CreateSQLFarm /ServiceAccount [Service Account]
/ServiceAccountPassword [password] /SQLConnectionString "Initial Catalog=AdfsConfiguration; Data Source=spellbound; failover partner=rearwindow; integrated security=SSPI; Network Library=DBMSSOCN" /FederationServiceName login2.uvm.edu /CleanConfig /CertThumbprint "[thumbprint]" /SigningCertThumbprint "[thumbprint]" /DecryptCertThumbprint "[thumbprint]"
Note that the thumbprint can be obtained by viewing the properties of the certificate in Windows explorer.`
9. Note that the ADFS “Artifact Store” database should get configured automatically, but you can ceck on this by doing the following:
1. Launch PowerShell with admin privs
3. Run “get-adfsproperties | select artifactdbconnection”
4. Use “set-adfsproperties -artifactdbconnection” to change the string if necessary.
5. See this resource for more details:

Of course, an ADFS server with no Federation partners does not do us much good.  Unfortunately, I don’t have any other ADFS servers that I want to integrate with, either.  What interests me more are Shibboleth services (such as our own “login.uvm.edu”), and federation with other “InCommon” partners.  I also am interested in “user centric” identity providers such as OpenID and “Windows Live ID”.  Here are some links to get us started down this path.  Unfortunately, I cannot find anything that details exactly what we want:

## ECTS Login Errors – Troubleshooting

Users of our ECTS implementation “PartnerPoint” are not an overly happy set.  Most of the problems that we have experienced are centered around login errors.  This application is particularly prone to login errors for the following reasons:

• Randomly generated initial password is too complex – data entry errors cause login denial
• Password expiration errors are not transparent – We need to capture the error that is seen in a password expiration instance.
• Passwords generated by ECTS (either during account creation or a ECTS admin reset) are temporary and must be changed on next login.  The account attribute “eatmuPwdGenerated” holds this information.
• Password strength requirements are not displayed in the forms, and password strength errors are not detailed or helpful (i.e. they do not tell you why your password is unacceptable).
• Login errors are not detailed or helpful – they do not tell you if the account is locked or disabled, it the password is expired, or if you simply entered an invalid username/password combination.
• Even when login is successful, users often will get “access denied” messages because of permissions problems:
• The ECTS “Add External User” dialog generally refuses to add permissions to the ACL for a site… it only works consistently when you add the user to an existing site group
• Sign-in for ECTS users requires at least “Read” permissions to the to-level site in a site collection.  You cannot grant external users rights to a child site with no permissions in the parent.

Clearing account lockouts – accomplished by setting the “lockoutTime” attribute of the AD LDS account to “0”.  This causes the “badPwdCount” attribute to be reset to zero.  Note that you cannot set “badPwdCount”, nor “badPasswordTime” as these attributes are owned by “SYSTEM”, and thus cannot be edited manually.  Solution located in the “EggHead Cafe”:

Other AD LDS account attributes to watch – see MSDN Active Directory Schema documentation for details:
http://msdn.microsoft.com/en-us/library/ms675090(VS.85).aspx

• eatmuPwdGenerated – attribute added by ECTS installer.  Indicated whether current password was generated by ECTS, or by the user.  Reset when the user successfully logs in and sets his own password.
• msDS-UserPasswordExpired – populated, but apparently not accurate or used.
• msDS-User-Account-Control-Computed – Most commonly used for reporting on account state.  This value is computed from other fields, and should not be modified directly.
• pwdLastSet – in active use for ECTS accounts, uses “Large Integer” value, formatted as “NT Time”, or number of 100 nanosecond intervals from 1 Jan, 1601.  This can be converted to a readable date using “w32tm.exe /ntte [string]”.  This value cannot be reset to a specific value, although you can use the special values “0” (meaning “must change at next login”) or “-1” (meaning “now”); however, ADSI Edit does not allow you to enter the value of “-1”, so we would have to use a different tool to set a “-1” value.  Setting the value to “0” will break login as there is no LDAP method for requesting a password change.