About Jon Bryan

Posts by Jon Bryan:

Amending/ Changing IE Proxy Settings, Continued

After sorting through the data that I obtained from the collection script, removing duplicates, etc. The result set was slightly messier than I expected, quite a few manually added entries and incorrect ones.

Anyway, to get the bulk of the changes implemented, I’m just targeting the large expected set – those with VPN settings labelled SITE1 PPTP and SITE1 VPN (anonymised…). Those with SITE2 PPTP have the settings, that were previously enforced by another script, have them enforced here too. This script will take the place of the existing one.

New machines, who do not have the settings already have them defined. Anyway here it is:

' LOOKUP_SET_IE_VPN_PROXIES.VBS
' Jon Bryan Jan 2016
' Run as a user logon script, via GPO.
' Fixes those defined VPN Proxy settings "SITE1 PPTP" and "SITE1 VPN" - no tick boxes or text boxes filled.
' Retains and enforces the normal value for SITE2 based VPN Proxy settings.
' Will replace the existing SET_IE_VPN_PROXIES.VBS script, if the expected VPN Proxy settings are not found, they are added.
'
OPTION EXPLICIT
ForceScriptEngine("cscript")
Const HKEY_CURRENT_USER = &H80000001
Dim WSHNetwork, strComputer, strKeyPath, arrValueNames, arrValueTypes, i, strValueName, objReg, arrValues, errReturn, arraystrValueName
Set WSHNetwork = CreateObject("WScript.Network")
strComputer = WSHNetwork.ComputerName
arraystrValueName=""
Set objReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"
objReg.EnumValues HKEY_CURRENT_USER, strKeyPath, arrValueNames, arrValueTypes
For i=0 To UBound(arrValueNames)
If arrValueNames(i)="DefaultConnectionSettings" or arrValueNames(i)="SavedLegacySettings" or arrValueNames(i)="" Then
'Do Nothing
Else
strValueName = arrValueNames(i)
'Setup collection of "VPN Names" to check through at the end - used to see if the normal ones are there and add if not.
If arraystrValueName="" Then
arraystrValueName=strValueName
Else
arraystrValueName=arraystrValueName & "," & strValueName
End If
' Enforce SITE2 PPTP settings - standard - proxy.pac
If strValueName = "SITE2 PPTP" Then
arrValues = Array(70,0,0,0,3,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,39,0,0,0,104,116,116,112,58,47,47,109,121,112,114,111,120,121,115,101,114,118,101,114,46,98,108,97,104,46,99,111,109,47,112,114,111,120,121,46,112,97,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
errReturn = objReg.SetBinaryValue (HKEY_CURRENT_USER, strKeyPath, strValueName, arrValues)
End If
' Enforce new SITE1 settings - blanking all ticks and dialogs - looking for two names.
If strValueName = "SITE1 PPTP" Then
arrValues = Array(70,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
errReturn = objReg.SetBinaryValue (HKEY_CURRENT_USER, strKeyPath, strValueName, arrValues)
End If
If strValueName = "SITE1 VPN" Then
arrValues = Array(70,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
errReturn = objReg.SetBinaryValue (HKEY_CURRENT_USER, strKeyPath, strValueName, arrValues)
End If
End If
Next
'Check for existence of VPN settings that should be pushed out be default:
If Instr(arraystrValueName ,"SITE1 PPTP") = 0 then
' SITE1 PPTP not found, so add it
strValueName = "SITE1 PPTP"
arrValues = Array(70,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
errReturn = objReg.SetBinaryValue (HKEY_CURRENT_USER, strKeyPath, strValueName, arrValues)
End If
If Instr(arraystrValueName ,"SITE2 PPTP") = 0 then
' SITE2 PPTP not found, so add it
strValueName = "SITE2 PPTP"
arrValues = Array(70,0,0,0,3,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,39,0,0,0,104,116,116,112,58,47,47,109,121,112,114,111,120,121,115,101,114,118,101,114,46,98,108,97,104,46,99,111,109,47,112,114,111,120,121,46,112,97,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
errReturn = objReg.SetBinaryValue (HKEY_CURRENT_USER, strKeyPath, strValueName, arrValues)
End If
'TIDY UP
Set WSHNetwork = Nothing
Set strComputer = Nothing
Set objReg = Nothing
Set strKeyPath = Nothing
Set arrValueNames = Nothing
Set arrValueTypes = Nothing
Set i = Nothing
Set strValueName = Nothing
Set arrValues = Nothing
Set errReturn = Nothing
Set arraystrValueName = Nothing
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Sub ForceScriptEngine(strScriptEng)
' Forces this script to be run under the desired scripting host.
' Valid arguments are "wscript" or "cscript".
' The command line arguments are passed on to the new call.
Dim arrArgs
Dim strArgs
For Each arrArgs In WScript.Arguments
strArgs = strArgs & " " & Chr(34) & arrArgs & Chr(34)
Next
If Lcase(Right(Wscript.FullName, 12)) = "\wscript.exe" Then
If Instr(1, Wscript.FullName, strScriptEng, 1) = 0 Then
CreateObject("Wscript.Shell").Run "cscript.exe //Nologo " & _
Chr(34) & Wscript.ScriptFullName & Chr(34) & strArgs
Wscript.Quit
End If
Else
If Instr(1, Wscript.FullName, strScriptEng, 1) = 0 Then
CreateObject("Wscript.Shell").Run "wscript.exe " & Chr(34) & _
Wscript.ScriptFullName & Chr(34) & strArgs
Wscript.Quit
End If
End If
End Sub

Yubikey Neo

So this is not directly relevant to FIM per se, but it falls under the kind of IdM/ Authentication umbrella, so I thought it belonged here….

In December 2014, I bought a Yubikey Neo. I wanted to see how it could be used to harden access to some sensitive “stuff”.

These are really cool devices; they are relatively inexpensive (~£36), yet provide a bunch of functionality all on one device, some of which I have not used.

The components that I did use were:

  • Yubico OTP – the One Time Passcode functionality that is present OOB – used to sign into the Yubico Forums
  • U2F – I use this for 2FA for my Google accounts and this blog – it is very simple to set up this 2FA method across multiple services. Look here for more information: https://www.yubico.com/applications/fido/
  • SmartCard (PIV) – this was the part that I was really interested in for securing stuff within the enterprise. I had recently installed a Windows PKI Infrastructure, so used that to generate trusted SmartCard Logon Certificates to install onto the devices. Look here for configuration docs: https://developers.yubico.com/yubico-piv-tool/

As with most of these things the documentation was initially difficult to read, there were various command line tools to manage different aspects of the Yubikeys, some of them had bugs at the time.

Anyway, long story short, back then I got it configured just how I wanted and used it daily ever since. However, just before Christmas, the SmartCard certificate that I had generated the previous year expired. Thus, the SmartCard functionality of the Yubikey became invalid.

I generated myself a new certificate from my CA, then came to try to remove the old certificate from one of my Yubikeys. I could not because I needed to authenticate against the device to carry out this action. The authentication string (aka password) is called the Management Key, that is (should be) changed from the default value when configuring the device. I went on the scrounge trying to find the key for this particular device, I found my old notes (command line dumps) from the previous year, there were a few management Keys within but not one for this particular device.

So, I might as well reset it, back to the docs: https://developers.yubico.com/yubico-piv-tool/YubiKey_PIV_introduction.html, in order to be able to reset the device, I first need to lock the device, by providing bad PIN and PUK values:

yubico-piv-tool -a verify-pin -P 4711
yubico-piv-tool -a verify-pin -P 4711
yubico-piv-tool -a verify-pin -P 4711
yubico-piv-tool -a verify-pin -P 4711
yubico-piv-tool -a change-puk -P 4711 -N 67567
yubico-piv-tool -a change-puk -P 4711 -N 67567
yubico-piv-tool -a change-puk -P 4711 -N 67567
yubico-piv-tool -a change-puk -P 4711 -N 67567
yubico-piv-tool -a reset

OK, so now I need a new management key….. The docs use dd to generate the key:

key=`dd if=/dev/random bs=1 count=24 2>/dev/null | hexdump -v -e '/1 "%02X"'`
echo $key
yubico-piv-tool -a set-mgm-key -n $key

At the time, I didn’t have easy access to a Unix system to do this, but more importantly I wanted to find a way to achieve the same result in Windows, using PowerShell. This would allow me to script the whole process. Here is the script to create the management key (For info about what “{0:X2}” means, look here: http://www.powershellmagazine.com/2012/10/12/pstip-converting-numbers-to-hex/)

$i=0
Do
{
$rnd=get-random -Minimum 0 -Maximum 255
$result=$result+"{0:X2}" -f $rnd
$i=$i+1
}
Until($i -eq 24)
Write-Host "Management Key:" $result

I have now re-written a script that I put together last year to add initialise a new or reset Yubikey (with PIV support) and add a user SmartCard certificate from a Windows CA:

#===================================================================================================
# Generate the Management Key:
$i=0
Do
{
$rnd=get-random -Minimum 0 -Maximum 255
$MgmtKey=$MgmtKey+"{0:X2}" -f $rnd
$i=$i+1
}
Until($i -eq 24)
Write-Host "Management Key:" $MgmtKey
#===================================================================================================
# Setup some Variables:
$PIN=98765432
$PUK=23894832
$Template="CertificateTemplate:My_Smartcard_Logon" # SmartCard Logon Template name from your CA
$DN="/CN=MyAccount/OU=myOU/DC=blah/DC=ac/DC=uk/" # ADDN of the user requesting the certificate
$Path="C:\<SomePath>\yubico-piv-tool-1.2.2-win64\bin"
#===================================================================================================
# Initialise the Yubikey:
Invoke-Expression -Command "$path\yubico-piv-tool.exe -a set-mgm-key -n $MgmtKey"
Invoke-Expression -Command "$path\yubico-piv-tool.exe --key=$MgmtKey -a change-pin -P 123456 -N $PIN"
Invoke-Expression -Command "$path\yubico-piv-tool.exe --key=$MgmtKey -a change-puk -P 12345678 -N $PUK"
Invoke-Expression -Command "$path\yubico-piv-tool.exe -a verify-pin -P $PIN"
#===================================================================================================
# Generate, request and install the SmartCard Certificate:
Invoke-Expression -Command "$path\yubico-piv-tool.exe --key=$MgmtKey -s 9a -a generate -o $Path\public.pem"
Invoke-Expression -Command "$path\yubico-piv-tool.exe -a verify-pin -P $PIN -s 9a -a request-certificate -S $DN -i $Path\public.pem -o $Path\request.csr"
Invoke-Expression -Command "certreq -config '<FQDN of CA Server>\<Name Of Certification Authority>' -submit -attrib $Template $Path\request.csr $Path\cert.crt"
Invoke-Expression -Command "$path\yubico-piv-tool.exe --key=$MgmtKey -s 9a -a import-certificate -i $Path\cert.crt"
Invoke-Expression -Command "$path\yubico-piv-tool.exe --key=$MgmtKey -a set-chuid"
#===================================================================================================

U2F Login for WordPress – OPENSSL_VERSION_NUMBER Undefined

While looking to get this working: https://github.com/shield-9/u2f-login/, I was seeing errors in the http logs referring to OPENSSL_VERSION_TEXT. These were being generated by the following lines in the u2f.php file:

public function __construct($appId, $attestDir = null) {
if(OPENSSL_VERSION_NUMBER < 0x10000000) {
throw new Error('OpenSSL has to be atleast version 1.0.0, this is ' . OPENSSL_VERSION_TEXT, ERR_OLD_OPENSSL);
}
$this->appId = $appId;
$this->attestDir = $attestDir;
}

After running a few more tests, it seemed that this PHP Constant should have been defined, but was not. Goggling didn’t provide any quick answers, but I soon worked it out for myself.

In the php.ini file (under arch in /etc/php/), uncomment the line: “extension=openssl.so” and restart httpd. Once the extension is enabled, the Constant is then available.

In the end I did not use this plugin, I was unable to register my Yubikeys. The plugin seems to have been superseded by this one: http://github.com/georgestephanis/two-factor/. In order to have the ability to use U2F login using this plugin, the openssl.so extension has to be enabled; otherwise, the U2F login option is not available.

Hiding Portal Buttons from Normal Users

Recently, one of my co-workers was assigned to work with me to learn some FIM and to help, where possible, with some configuration.

One of the things on my list of “things to do” was to find a way of hiding certain portal buttons from a normal users view – after all they are not (currently) going to be allowed to use the portal for any self service task yet – that might come later….

So, I sent my co-worker off to find and implement the solution in the test environment, thinking “It can’t be that hard…”

I had already implemented the following solution to hide New and Delete User buttons:http://blogs.msdn.com/b/connector_space/archive/2014/10/02/5-minute-fim-hacks-hiding-the-quot-new-quot-and-quot-delete-quot-user-buttons.aspx

A week later we met to see how much progress had been made, he had found the following information (http://social.technet.microsoft.com/wiki/contents/articles/2139.how-to-remove-new-delete-and-other-buttons-from-fim-portal-pages.aspx), but didn’t really understand what it meant. So, we went through it together. I must admit, when I first read it through, it didn’t make a lot of sense to me either, but after a few re-reads it clicked into place.

This was done around 1 month ago and already I’m forgetting the actions that were carried out, so I’m going to document the steps in a more verbose fashion here for future reference.

Create new search scopes, using the original Search Scopes’ values as the basis. The resource type shold be “Resource”.

SearchScopes

I had already setup a new Usage Keyword “PrivilegedUsers”, that I was using to control the things that the helpdesk users would see to do their admin tasks. So, in the original search scopes this keyword needs to be added and the BasicUI and GlobalSearchResult keywords should be removed. Then in the (new) copies of the search scopes, BasicUI and GlobalSearchResultshould be present.

Original and New Search Scope Usage Keywords:

Original: New:
AllSecurityGroups AllSecurityGroups
customized customized
Global Global
Group Group
MailEnabledSecurity MailEnabledSecurity
PrivilegedUsers PrivilegedUsers
Security Security
GlobalSearchResult
BasicUI

Do an IIS reset….

Now on the home page, choose one of those new search scopes and do a “blank” search. Copy the URL that this search went to – note that the UI of this search presents only the details button.

Do the same thing for each of the new search scopes, copying the URL’s for later….

Now we need to make those URL’s available via the portal UI. We need to do this for both the Navigation Bar and Home Page, so create new resources, using the information within the originals as the basis. The only change that I made was to remove the reference to (DGs) and (SGs) for the top level items and to add a description “User Read Only view” for those new items with the same name (so that I could tell the difference):

NavBarResources

Use the URL’s that were obtained from the search scopes to define the behaviour of the Resource:Behaviour

Do an IIS reset….

If you copied the Parent Order and Order values of the original Resources, the result is a little messy from the Administrators POV, as all of those items are crammed together. Unfortunately, I know of no way to hide an item from the admin, so I just rearranged the order of the layout. It displays as follows after re-arranging both the Nav Bar and Home Page Resource orders:

HomePage_Admin

A normal user looking at the portal sees this, the links go to the URL’s in those Search Scopes defined earlier:

HomePage_User

And when looking at for example “Distribution Groups”, sees that Details button only:

HomePage_User_Result

The PrivilegedUsers view, looks like this:HomePage_PU

And if a member of PrivilegedUsers uses the Distribution Groups (DGs) link, they see this – note all of the buttons are available:

DIst_PU_View

If I remember missing something or something not being quite right, I’ll be back to correct…

emailAddressPresent Flag Setting and Checking Email Suffix Validity

In my Organisation, not all users have a mailbox, while others are just mail enabled.

In order to define XPATH filters for those people who should be allowed into a distribution list, managed by the portal, I needed to set a flag.

This boolean flag defines if they could be mailed and therefore should belong to a distribution list.

A nice add on was the fact that it allowed me to check the mail suffix of the user as part of the import process. Only those suffixes defined in the array are allowed, only suffixes that the Exchange Organisation is authoritative for. This check exists, because sometimes people just add a new suffix, or make a typo – this bit of code highlights those events.

Case "emailAddressPresent-ADMA-Import"
'AD attributes required: mail and msExchHomeServerName
' Default setting = False
mventry("emailAddressPresent").Value = "False"
If csentry("mail").IsPresent And csentry("msExchHomeServerName").IsPresent Then
Dim suffix() As String = Split((csentry("mail").Value), "@")
'Valid/allowed email suffixes are defined in the following array (amend as appropriate):
Dim validMailAddresses() As String = {"blah.ac.uk", "foo.ac.uk", "bar.ac.uk", "otherorg.ac.uk"}
If (Array.IndexOf(validMailAddresses, suffix(1).ToLower) <> -1) Then
mventry("emailAddressPresent").Value = "True"
Else
'If a suffix from the array is not found - raise an error, so that the suffix can be added to the array or simply sorted out - where a mistake was made. Rare!
Throw New Exception("Invalid email suffix found: " & suffix(1))
End If
ElseIf csentry("mail").IsPresent And csentry("mailNickName").IsPresent Then
'This person is a mail enabled user, maybe with an island site email address or just something else, so we want them to be able to be added to distribution lists....
mventry("emailAddressPresent").Value = "True"
End If

Looking for (and finding) odd ID’s

As I have said previously, the HR data that feeds FIM is out of my direct control and has had some data quality issues.

As a result, I have ended up putting some consistency checking into my code. I’ll present a few from my MVExtension here, what I’m tending to be looking for is where the user has already been provisioned, but then the reference has been deleted in HR, but no-one has told me so that I can tidy up the account in AD and FIM.

The ID’s that showed up after putting in the bit looking for FIM only references, was caused by disconnecting a table that validates historical end dates. I was assured that I would not need it anymore, because the end dates would not be randomly set to a period in the past that mean that I would never receive that update… However, this did not pan out, so I re-attached the table, but did not reset the MV object deletion rule afterwards, so I ended up with ID fragments in the portal – referenced only by ObjectID.

Again I used the Lithnet PowerShell module to clear these up. There were around 40 to do, so I just got the ObjectID’s from the job xml, put them in a text file and ran this:

Import-Module LithnetRMA
$DNs=Get-Content "C:\FIMScripts\FIMMA_DNs.txt"
ForEach ($DN in $DNs)
{
Write-Host $DN
$a=Get-Resource -ObjectType Person -AttributeName ObjectID -AttributeValue "$DN" | Remove-Resource
}
If MyADADMAConnectors = 0 And HRMAConnectors > 0 Then
'This bit applies to ID's that are coming from HR - New users - a user should be provisioned here....
'what would follow is the code to created the various attributes needed for that new user...
End If
If MyADADMAConnectors = 0 And HRMAConnectors = 0 And FIMMAConnectors > 0 Then
'This point looks for ID's that exist only in the portal - there shouldn't be any right now,
'but in the future, I want to be able to create new service accounts via the portal.
'So, as there shouldn't be any of these now, lets just raise an error:
Throw New Exception("Something odd going on here - remnant in FIM only?")
End If
If MyADADMAConnectors = 1 Then
adDN = mventry("adDN").Value 'this is constructed as part of the HR input sync
DN = ADMA.CreateDN(adDN)
'There is already an AD connector, so...
csentry = ADMA.Connectors.ByIndex(0)
'If the expected DN has changed change it in AD too
If Not csentry.DN.ToString.ToLower.Equals(DN.ToString.ToLower) Then
csentry.DN = DN
End If
'Check for real people who have been previously provided by HR (they have an EndDate),
'who have become disconnected from the HR PersonAssignment table.
'i.e. they have been deleted but the message has not been passed on! So, raise an
'error to ensure that the user is manually deleted from AD and FIM.
'We only care about those who have an EndDate, as all service accounts and some particular
'Users do not have an EndDate - for example those who have historically been added, but are no longer referenced in HR.
If HRMAPAConnectors = 0 Then
If mventry("employeeEndDate").IsPresent Then
Throw New Exception("User in AD, but not in HR - possible duplicate deleted but not informed!")
End If
End If
If MyADADMAConnectors > 1 Then
'There should never be anything except 0 or 1 MyADADMAConnectors, so raise an error.
Throw New UnexpectedDataException("Multiple MyADADMAConnectors:" + MyADADMAConnectors.ToString)
End If
End If

 

O365 License Management, Using AD Groups

Previously, I wrote the following post about license management: https://365.oholics.net/office-365-licence-management/. This post relied on text files to hold the UPN of users who should have specific licenses.

I now have a new script the does that same task but uses AD groups to hold the licence entitlements. I have placed a copy of the script below.

One thing of note (a bug), that will be present in the previous script, is that of assigning a licence that is in conflict with an already applied license. This issue arose while testing this new script, notably for users who were being entitled to a Project licence.

During processing, I was seeing errors like “Conflicting Service Plans: SHAREPOINTWAC_EDU, SHAREPOINTWAC_EDU” and “Conflicting Service Plans: SHAREPOINTSTANDARD_EDU, SHAREPOINTENTERPRISE_EDU”. Where this part was present in both of the license collections – the one already applied and the project license that was to be applied.

The solution is messy, but does work.

First the “base” user licence “STANDARDWOFFPACK_FACULTY” must be removed, and then replaced by the same license, but with more disabled components – in this case EXCHANGE_S_STANDARD, SHAREPOINTSTANDARD_EDU and SHAREPOINTWAC_EDU. Once that is complete and verified, then try to apply the complete Project license.

If ($MEMBERS_PROJECTONLINE_PLAN_1_FACULTY.SamAccountName -Contains $AdUser.sAMAccountName -and $PROJECTONLINE_PLAN_1_FACULTY_Applied -ne "True")
{
#Setup the bits that we don't want, because they are already present in the Project license and will cause an error otherwise...
$DisabledPlans=@()
$DisabledPlans+="EXCHANGE_S_STANDARD"
$DisabledPlans+="SHAREPOINTSTANDARD_EDU"
$DisabledPlans+="SHAREPOINTWAC_EDU"
# Define the Licence options
$FacultyLicenseOptions = New-MsolLicenseOptions -AccountSkuId $STANDARDWOFFPACK_FACULTY -DisabledPlans $DisabledPlans
#
#First we need to remove the standard licence..... in order to remove SHAREPOINTSTANDARD_EDU and SHAREPOINTWAC_EDU, we'll add the bits that we want back in a mo'
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $STANDARDWOFFPACK_FACULTY
Start-Sleep -s 30
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
#Now set the new licence in place
$CheckUser=Get-MSOLUser -UserPrincipalName $User.UserPrincipalName
$CheckLicenses=$CheckUser.Licenses
$CheckSKUIDs=$CheckLicenses.AccountSkuId
If ($CheckSKUIDs -NotContains $STANDARDWOFFPACK_FACULTY)
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_FACULTY -LicenseOptions $FacultyLicenseOptions
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
# Then try putting the full Project licence in place
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $PROJECTONLINE_PLAN_1_FACULTY
$LicensesAdded += "PROJECTONLINE_PLAN_1_FACULTY"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}

The complete script is here:

$ErrorActionPreference = "Stop"
$Error.Clear()
#####################################################
# Function to generate mail content for licencing errors
Function MailBody
{
$Exception=$error[0].Exception.Message
$Command=$error[0].InvocationInfo.Line.Trim()
$StrBody="Error: $Exception `r`rUser: $UserPrincipalName `r`rCommand: $Command `r`r##########################################################################################`r`r"
$Error.Clear()
Return $strBody
}
#####################################################
$sig = @"
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct NativeCredential
{
public UInt32 Flags;
public CRED_TYPE Type;
public IntPtr TargetName;
public IntPtr Comment;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
public UInt32 CredentialBlobSize;
public IntPtr CredentialBlob;
public UInt32 Persist;
public UInt32 AttributeCount;
public IntPtr Attributes;
public IntPtr TargetAlias;
public IntPtr UserName;
internal static NativeCredential GetNativeCredential(Credential cred)
{
NativeCredential ncred = new NativeCredential();
ncred.AttributeCount = 0;
ncred.Attributes = IntPtr.Zero;
ncred.Comment = IntPtr.Zero;
ncred.TargetAlias = IntPtr.Zero;
ncred.Type = CRED_TYPE.GENERIC;
ncred.Persist = (UInt32)1;
ncred.CredentialBlobSize = (UInt32)cred.CredentialBlobSize;
ncred.TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName);
ncred.CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob);
ncred.UserName = Marshal.StringToCoTaskMemUni(System.Environment.UserName);
return ncred;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Credential
{
public UInt32 Flags;
public CRED_TYPE Type;
public string TargetName;
public string Comment;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
public UInt32 CredentialBlobSize;
public string CredentialBlob;
public UInt32 Persist;
public UInt32 AttributeCount;
public IntPtr Attributes;
public string TargetAlias;
public string UserName;
}
public enum CRED_TYPE : uint
{
GENERIC = 1,
DOMAIN_PASSWORD = 2,
DOMAIN_CERTIFICATE = 3,
DOMAIN_VISIBLE_PASSWORD = 4,
GENERIC_CERTIFICATE = 5,
DOMAIN_EXTENDED = 6,
MAXIMUM = 7, // Maximum supported cred type
MAXIMUM_EX = (MAXIMUM + 1000), // Allow new applications to run on old OSes
}
public class CriticalCredentialHandle : Microsoft.Win32.SafeHandles.CriticalHandleZeroOrMinusOneIsInvalid
{
public CriticalCredentialHandle(IntPtr preexistingHandle)
{
SetHandle(preexistingHandle);
}
public Credential GetCredential()
{
if (!IsInvalid)
{
NativeCredential ncred = (NativeCredential)Marshal.PtrToStructure(handle,
typeof(NativeCredential));
Credential cred = new Credential();
cred.CredentialBlobSize = ncred.CredentialBlobSize;
cred.CredentialBlob = Marshal.PtrToStringUni(ncred.CredentialBlob,
(int)ncred.CredentialBlobSize / 2);
cred.UserName = Marshal.PtrToStringUni(ncred.UserName);
cred.TargetName = Marshal.PtrToStringUni(ncred.TargetName`);
cred.TargetAlias = Marshal.PtrToStringUni(ncred.TargetAlias);
cred.Type = ncred.Type;
cred.Flags = ncred.Flags;
cred.Persist = ncred.Persist;
return cred;
}
else
{
throw new InvalidOperationException("Invalid CriticalHandle!");
}
}
override protected bool ReleaseHandle()
{
if (!IsInvalid)
{
CredFree(handle);
SetHandleAsInvalid();
return true;
}
return false;
}
}
[DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr CredentialPtr);
[DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
public static extern bool CredFree([In] IntPtr cred);
"@
Add-Type -MemberDefinition $sig -Namespace "ADVAPI32" -Name 'Util'
$targetName = "LicenceManagment"
$nCredPtr= New-Object IntPtr
$success = [ADVAPI32.Util]::CredRead($targetName,1,0,[ref] $nCredPtr)
if($success){
$critCred = New-Object ADVAPI32.Util+CriticalCredentialHandle $nCredPtr
$cred = $critCred.GetCredential()
$UserName = $cred.UserName;
$Password = $cred.CredentialBlob;
$Password = ConvertTo-SecureString -String $Password -AsPlainText -Force
$objCreds = New-Object Management.Automation.PSCredential $UserName, $Password
}
############ Import modules and login to MSOL ############
If(@(Get-Module | ? { $_.Name -eq "MSOnline"}).Count -eq 0)
{
Import-Module MSOnline;
}
If(@(Get-Module | ? { $_.Name -eq "ActiveDirectory"}).Count -eq 0)
{
Import-Module ActiveDirectory;
}
Try
{
Connect-MsolService -Credential $objCreds
}
Catch [System.Exception]
{
Send-MailMessage -From admin@blah.com -To someadmin@blah.com -cc someotheradmin@blah.com -Subject "ERROR - License management Login Failed!" -Body $_.Exception.Message -SmtpServer smtp.blah.com
Write-Host $_.Exception.Message
Exit
}
############ Setup Variables ############
$dateFormat = "HH:mm:ss dd/MM/yyyy"
############ Setup logging file ############
$Logfile="C:\Office365-Scripts\Licencing\LicenceManagement.txt"
############ AD Groups containing whose users who should have individual Licenses ############
$MEMBERS_CRMSTANDARD=Get-ADGroupMember -Identity CRMSTANDARD_Users
$MEMBERS_INTUNE_A=Get-ADGroupMember -Identity INTUNE_A_Users
$MEMBERS_OFFICESUBSCRIPTION_FACULTY=Get-ADGroupMember -Identity OFFICESUBSCRIPTION_FACULTY_Users
$MEMBERS_OFFICESUBSCRIPTION_STUDENT=Get-ADGroupMember -Identity OFFICESUBSCRIPTION_STUDENT_Users
$MEMBERS_POWER_BI_STANDARD=Get-ADGroupMember -Identity POWER_BI_STANDARD_Users
$MEMBERS_PROJECTONLINE_PLAN_1_FACULTY=Get-ADGroupMember -Identity PROJECTONLINE_PLAN_1_FACULTY_Users
$MEMBERS_PROJECTONLINE_PLAN_1_STUDENT=Get-ADGroupMember -Identity PROJECTONLINE_PLAN_1_STUDENT_Users
######################## Standard Licencing ######################
$POWER_BI_STANDARD = "<YourTenancyName>:POWER_BI_STANDARD"
$CRMSTANDARD = "<YourTenancyName>:CRMSTANDARD"
$INTUNE_A = "<YourTenancyName>:INTUNE_A"
######################## Faculty Licensing #######################
$STANDARDWOFFPACK_FACULTY = "<YourTenancyName>:STANDARDWOFFPACK_FACULTY"
$OFFICESUBSCRIPTION_FACULTY = "<YourTenancyName>:OFFICESUBSCRIPTION_FACULTY"
$STANDARDWOFFPACK_IW_FACULTY = "<YourTenancyName>:STANDARDWOFFPACK_IW_FACULTY"
$PROJECTONLINE_PLAN_1_FACULTY ="<YourTenancyName>:PROJECTONLINE_PLAN_1_FACULTY"
######################## Student Licensing #######################
$STANDARDWOFFPACK_STUDENT = "<YourTenancyName>:STANDARDWOFFPACK_STUDENT"
$STANDARDWOFFPACK_IW_STUDENT = "<YourTenancyName>:STANDARDWOFFPACK_IW_STUDENT"
$PROJECTONLINE_PLAN_1_STUDENT = "<YourTenancyName>:PROJECTONLINE_PLAN_1_STUDENT"
$OFFICESUBSCRIPTION_STUDENT = "<YourTenancyName>:OFFICESUBSCRIPTION_STUDENT"
######################## Generic settings ########################
$DisabledPlans = "EXCHANGE_S_STANDARD"
$UsageLocation = "GB"
############ Define the two types of Licence option - Faculty and Student ############
$FacultyLicenseOptions = New-MsolLicenseOptions -AccountSkuId $STANDARDWOFFPACK_FACULTY -DisabledPlans $DisabledPlans
$StudentLicenseOptions = New-MsolLicenseOptions -AccountSkuId $STANDARDWOFFPACK_STUDENT -DisabledPlans $DisabledPlans
#
############ Get users and apply Licenses ############
#
############ Do the licenced users first ############
ForEach($User in (Get-MsolUser -all | where {$_.isLicensed -eq "True"}))
#ForEach($User in (Get-MsolUser -all | where {$_.UserPrincipalName -eq "someotheradmin@blah.com"}))
{
If($User.UserPrincipalName -notlike "*<YourTenancyName>.onmicrosoft.com" -and $User.UserPrincipalName -ne "DirSync@blah.com")
{
$AdUser=Get-ADUser -Filter {UserPrincipalName -eq $User.UserPrincipalName} -Properties extensionAttribute5, sAMAccountName
Try
{
$date = Get-Date -Format $dateFormat
$LoggingContent=$null
$Licenses=$null
$SKUIDs=$null
$STANDARDWOFFPACK_STUDENT_Applied=$null
$STANDARDWOFFPACK_FACULTY_Applied=$null
$PROJECTONLINE_PLAN_1_FACULTY_Applied=$null
$POWER_BI_STANDARD_Applied=$null
$CRMSTANDARD_Applied=$null
$INTUNE_A_Applied=$null
$OFFICESUBSCRIPTION_FACULTY_Applied=$null
$LicensesAdded=@()
$LicensesRemoved=@()
$Licenses=$User.Licenses
$SKUIDs=$Licenses.AccountSkuId
ForEach ($SKU in $SKUIDs)
{
If ($SKU -eq $STANDARDWOFFPACK_IW_FACULTY)
{
Try
{
##### These should not be used, so remove if found #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $STANDARDWOFFPACK_IW_FACULTY
$LicensesRemoved += "STANDARDWOFFPACK_IW_FACULTY"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
##### But then we need to replace that with a "normal" O365 licence, plus Pro Plus #####
If ($AdUser.extensionAttribute5 -eq "Staff")
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_FACULTY -LicenseOptions $FacultyLicenseOptions
$LicensesAdded += "STANDARDWOFFPACK_FACULTY"
##### Add user to the AD group - just for reference.... #####
Add-ADGroupMember -Identity STANDARDWOFFPACK_FACULTY_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $OFFICESUBSCRIPTION_FACULTY
$LicensesAdded += "OFFICESUBSCRIPTION_FACULTY"
##### Add user to the AD group to ensure that they licence is not subsequently removed.... #####
Add-ADGroupMember -Identity OFFICESUBSCRIPTION_FACULTY_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
Else
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_STUDENT -LicenseOptions $StudentLicenseOptions
$LicensesAdded += "STANDARDWOFFPACK_STUDENT"
##### Add user to the AD group - just for reference.... #####
Add-ADGroupMember -Identity STANDARDWOFFPACK_STUDENT_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $OFFICESUBSCRIPTION_STUDENT
$LicensesAdded += "OFFICESUBSCRIPTION_STUDENT"
##### Add user to the AD group to ensure that they licence is not subsequently removed.... #####
Add-ADGroupMember -Identity OFFICESUBSCRIPTION_STUDENT_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
If ($SKU -eq $STANDARDWOFFPACK_IW_STUDENT)
{
Try
{
##### These should not be used, so remove if found #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $STANDARDWOFFPACK_IW_STUDENT
$LicensesRemoved += "STANDARDWOFFPACK_IW_STUDENT"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
##### But then we need to replace that with a "normal" O365 licence, plus Pro Plus #####
If ($AdUser.extensionAttribute5 -ne "Staff")
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_STUDENT -LicenseOptions $StudentLicenseOptions
$LicensesAdded += "STANDARDWOFFPACK_STUDENT"
##### Add user to the AD group - just for reference.... #####
Add-ADGroupMember -Identity STANDARDWOFFPACK_STUDENT_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $OFFICESUBSCRIPTION_STUDENT
$LicensesAdded += "OFFICESUBSCRIPTION_STUDENT"
##### Add user to the AD group to ensure that they licence is not subsequently removed.... #####
Add-ADGroupMember -Identity OFFICESUBSCRIPTION_STUDENT_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
Else
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_FACULTY -LicenseOptions $FacultyLicenseOptions
$LicensesAdded += "STANDARDWOFFPACK_FACULTY"
##### Add user to the AD group - just for reference.... #####
Add-ADGroupMember -Identity STANDARDWOFFPACK_FACULTY_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $OFFICESUBSCRIPTION_FACULTY
$LicensesAdded += "OFFICESUBSCRIPTION_FACULTY"
##### Add user to the AD group to ensure that they licence is not subsequently removed.... #####
Add-ADGroupMember -Identity OFFICESUBSCRIPTION_FACULTY_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
##### Standard O365 Licenses #####
If ($SKU -eq $STANDARDWOFFPACK_STUDENT)
{
$STANDARDWOFFPACK_STUDENT_Applied="True"
If ($ADUser.extensionAttribute5 -eq "Staff")
{
Try
{
##### Remove Student licence from Staff member #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $STANDARDWOFFPACK_STUDENT
$LicensesRemoved += "STANDARDWOFFPACK_STUDENT"
##### Remove user from the AD group #####
Remove-ADGroupMember -Identity STANDARDWOFFPACK_STUDENT_Users -Members $AdUser.sAMAccountName -Confirm:$False
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
Try
{
##### Apply Staff licence #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_FACULTY -LicenseOptions $FacultyLicenseOptions
$LicensesAdded += "STANDARDWOFFPACK_FACULTY"
##### Add user to the AD group - just for reference.... #####
Add-ADGroupMember -Identity STANDARDWOFFPACK_FACULTY_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
If ($SKU -eq $STANDARDWOFFPACK_FACULTY)
{
$STANDARDWOFFPACK_FACULTY_Applied="True"
If ($ADUser.extensionAttribute5 -ne "Staff")
{
Try
{
##### Remove Staff licence from Student member #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $STANDARDWOFFPACK_FACULTY
$LicensesRemoved += "STANDARDWOFFPACK_FACULTY"
##### Remove user from the AD group #####
Remove-ADGroupMember -Identity STANDARDWOFFPACK_FACULTY_Users -Members $AdUser.sAMAccountName -Confirm:$False
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
Try
{
##### Apply Student licence #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_STUDENT -LicenseOptions $StudentLicenseOptions
$LicensesAdded += "STANDARDWOFFPACK_STUDENT"
##### Add user to the AD group - just for reference.... #####
Add-ADGroupMember -Identity STANDARDWOFFPACK_STUDENT_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
##### Individual Subscriptions #####
If ($SKU -eq $PROJECTONLINE_PLAN_1_FACULTY)
{
$PROJECTONLINE_PLAN_1_FACULTY_Applied="True"
If ($MEMBERS_PROJECTONLINE_PLAN_1_FACULTY.SamAccountName -NotContains $AdUser.sAMAccountName)
{
Try
{
#If the user is not in the group, then simply revoke the licence
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $PROJECTONLINE_PLAN_1_FACULTY
$LicensesRemoved += "PROJECTONLINE_PLAN_1_FACULTY"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
ElseIf ($ADUser.extensionAttribute5 -ne "Staff")
{
Try
{
##### Remove Staff licence from Student member #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $PROJECTONLINE_PLAN_1_FACULTY
$LicensesRemoved += "PROJECTONLINE_PLAN_1_FACULTY"
##### Remove user from the AD group to ensure that the licence is not subsequently/ mistakenly added again.... #####
Remove-ADGroupMember -Identity PROJECTONLINE_PLAN_1_FACULTY_Users -Members $AdUser.sAMAccountName -Confirm:$False
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
Try
{
##### Apply Student licence #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $PROJECTONLINE_PLAN_1_STUDENT
$LicensesAdded += "PROJECTONLINE_PLAN_1_STUDENT"
##### Add user to the AD group to ensure that the licence is not subsequently removed.... #####
Add-ADGroupMember -Identity PROJECTONLINE_PLAN_1_STUDENT_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
If ($SKU -eq $PROJECTONLINE_PLAN_1_STUDENT)
{
$PROJECTONLINE_PLAN_1_STUDENT_Applied="True"
If ($MEMBERS_PROJECTONLINE_PLAN_1_STUDENT.SamAccountName -NotContains $AdUser.sAMAccountName)
{
Try
{
#If the user is not in the group, then simply revoke the licence
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $PROJECTONLINE_PLAN_1_STUDENT
$LicensesRemoved += "PROJECTONLINE_PLAN_1_STUDENT"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
ElseIf ($ADUser.extensionAttribute5 -eq "Staff")
{
Try
{
##### Remove Student licence from Staff member #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $PROJECTONLINE_PLAN_1_STUDENT
$LicensesRemoved += "PROJECTONLINE_PLAN_1_STUDENT"
##### Remove user from the AD group to ensure that the licence is not subsequently/ mistakenly added again.... #####
Remove-ADGroupMember -Identity PROJECTONLINE_PLAN_1_STUDENT_Users -Members $AdUser.sAMAccountName -Confirm:$False
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
Try
{
##### Apply Staff licence #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $PROJECTONLINE_PLAN_1_FACULTY
$LicensesAdded += "PROJECTONLINE_PLAN_1_FACULTY"
##### Add user to the AD group to ensure that the licence is not subsequently removed.... #####
Add-ADGroupMember -Identity PROJECTONLINE_PLAN_1_FACULTY_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
If ($SKU -eq $OFFICESUBSCRIPTION_FACULTY)
{
$OFFICESUBSCRIPTION_FACULTY_Applied="True"
If ($MEMBERS_OFFICESUBSCRIPTION_FACULTY.SamAccountName -NotContains $AdUser.sAMAccountName)
{
Try
{
#If the user is not in the group, then simply revoke the licence
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $OFFICESUBSCRIPTION_FACULTY
$LicensesRemoved += "OFFICESUBSCRIPTION_FACULTY"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
ElseIf ($ADUser.extensionAttribute5 -ne "Staff")
{
Try
{
##### Remove Staff licence from Student member #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $OFFICESUBSCRIPTION_FACULTY
$LicensesRemoved += "OFFICESUBSCRIPTION_FACULTY"
##### Remove user from the AD group to ensure that the licence is not subsequently/ mistakenly added again.... #####
Remove-ADGroupMember -Identity OFFICESUBSCRIPTION_FACULTY_Users -Members $AdUser.sAMAccountName -Confirm:$False
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
Try
{
##### Apply Student licence #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $OFFICESUBSCRIPTION_STUDENT
$LicensesAdded += "OFFICESUBSCRIPTION_STUDENT"
##### Add user to the AD group to ensure that the licence is not subsequently removed.... #####
Add-ADGroupMember -Identity OFFICESUBSCRIPTION_STUDENT_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
If ($SKU -eq $OFFICESUBSCRIPTION_STUDENT)
{
$OFFICESUBSCRIPTION_STUDENT_Applied="True"
If ($MEMBERS_OFFICESUBSCRIPTION_STUDENT.SamAccountName -NotContains $AdUser.sAMAccountName)
{
Try
{
#If the user is not in the group, then simply revoke the licence
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $OFFICESUBSCRIPTION_STUDENT
$LicensesRemoved += "OFFICESUBSCRIPTION_STUDENT"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
ElseIf ($ADUser.extensionAttribute5 -eq "Staff")
{
Try
{
##### Remove Student licence from Staff member #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $OFFICESUBSCRIPTION_STUDENT
$LicensesRemoved += "OFFICESUBSCRIPTION_STUDENT"
##### Remove user from the AD group to ensure that the licence is not subsequently/ mistakenly added again.... #####
Remove-ADGroupMember -Identity OFFICESUBSCRIPTION_STUDENT_Users -Members $AdUser.sAMAccountName -Confirm:$False
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
Try
{
##### Apply Staff licence #####
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $OFFICESUBSCRIPTION_FACULTY
$LicensesAdded += "OFFICESUBSCRIPTION_FACULTY"
##### Add user to the AD group to ensure that the licence is not subsequently removed.... #####
Add-ADGroupMember -Identity OFFICESUBSCRIPTION_FACULTY_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
If ($SKU -eq $POWER_BI_STANDARD)
{
$POWER_BI_STANDARD_Applied="True"
If ($MEMBERS_POWER_BI_STANDARD.SamAccountName -NotContains $AdUser.sAMAccountName)
{
Try
{
#If the user is not in the group, then simply revoke the licence
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $POWER_BI_STANDARD
$LicensesRemoved += "POWER_BI_STANDARD"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
If ($SKU -eq $CRMSTANDARD)
{
$CRMSTANDARD_Applied="True"
If ($MEMBERS_CRMSTANDARD.SamAccountName -NotContains $AdUser.sAMAccountName)
{
Try
{
#If the user is not in the group, then simply revoke the licence
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $CRMSTANDARD
$LicensesRemoved += "CRMSTANDARD"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
If ($SKU -eq $INTUNE_A)
{
$INTUNE_A_Applied="True"
If ($MEMBERS_INTUNE_A.SamAccountName -NotContains $AdUser.sAMAccountName)
{
Try
{
#If the user is not in the group, then simply revoke the licence
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $INTUNE_A
$LicensesRemoved += "INTUNE_A"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
}
}
Catch [System.Exception]
{
#Mop up any other errors
Send-MailMessage -From admin@blah.com -To someadmin@blah.com -cc someotheradmin@blah.com -Subject "ERROR - O365 Licence Management" -Body $_.Exception.Message -SmtpServer smtp.blah.com
Write-Host $_.Exception.Message
}
Try
{
##### Apply individual licences if they are not applied already #####
If ($MEMBERS_PROJECTONLINE_PLAN_1_FACULTY.SamAccountName -Contains $AdUser.sAMAccountName -and $PROJECTONLINE_PLAN_1_FACULTY_Applied -ne "True")
{
#Setup the bits that we don't want, because they are already present in the Project license and will cause an error otherwise...
$DisabledPlans=@()
$DisabledPlans+="EXCHANGE_S_STANDARD"
$DisabledPlans+="SHAREPOINTSTANDARD_EDU"
$DisabledPlans+="SHAREPOINTWAC_EDU"
# Define the Licence options
$FacultyLicenseOptions = New-MsolLicenseOptions -AccountSkuId $STANDARDWOFFPACK_FACULTY -DisabledPlans $DisabledPlans
#
#First we need to remove the standard licence..... in order to remove SHAREPOINTSTANDARD_EDU and SHAREPOINTWAC_EDU, we'll add the bits that we want back in a mo'
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $STANDARDWOFFPACK_FACULTY
Start-Sleep -s 30
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
#Now set the new licence in place
$CheckUser=Get-MSOLUser -UserPrincipalName $User.UserPrincipalName
$CheckLicenses=$CheckUser.Licenses
$CheckSKUIDs=$CheckLicenses.AccountSkuId
If ($CheckSKUIDs -NotContains $STANDARDWOFFPACK_FACULTY)
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_FACULTY -LicenseOptions $FacultyLicenseOptions
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
# Then try putting the full Project licence in place
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $PROJECTONLINE_PLAN_1_FACULTY
$LicensesAdded += "PROJECTONLINE_PLAN_1_FACULTY"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
If ($MEMBERS_PROJECTONLINE_PLAN_1_STUDENT.SamAccountName -Contains $AdUser.sAMAccountName -and $PROJECTONLINE_PLAN_1_STUDENT_Applied -ne "True")
{
#Setup the bits that we don't want, because they are already present in the Project license and will cause an error otherwise...
$DisabledPlans=@()
$DisabledPlans+="EXCHANGE_S_STANDARD"
$DisabledPlans+="SHAREPOINTSTANDARD_EDU"
$DisabledPlans+="SHAREPOINTWAC_EDU"
# Define the Licence options
$StudentLicenseOptions = New-MsolLicenseOptions -AccountSkuId $STANDARDWOFFPACK_STUDENT -DisabledPlans $DisabledPlans
#
#First we need to remove the standard licence..... in order to remove SHAREPOINTSTANDARD_EDU and SHAREPOINTWAC_EDU, we'll add the bits that we want back in a mo'
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $STANDARDWOFFPACK_STUDENT
Start-Sleep -s 30
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
#Now set the new licence in place
$CheckUser=Get-MSOLUser -UserPrincipalName $User.UserPrincipalName
$CheckLicenses=$CheckUser.Licenses
$CheckSKUIDs=$CheckLicenses.AccountSkuId
If ($CheckSKUIDs -NotContains $STANDARDWOFFPACK_STUDENT)
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_STUDENT -LicenseOptions $StudentLicenseOptions
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
# Then try putting the full Project licence in place
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $PROJECTONLINE_PLAN_1_STUDENT
$LicensesAdded += "PROJECTONLINE_PLAN_1_STUDENT"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
If ($MEMBERS_POWER_BI_STANDARD.SamAccountName -Contains $AdUser.sAMAccountName -and $POWER_BI_STANDARD_Applied -ne "True")
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $POWER_BI_STANDARD
$LicensesAdded += "POWER_BI_STANDARD"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($MEMBERS_CRMSTANDARD.SamAccountName -Contains $AdUser.sAMAccountName -and $CRMSTANDARD_Applied -ne "True")
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $CRMSTANDARD
$LicensesAdded += "CRMSTANDARD"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($MEMBERS_INTUNE_A.SamAccountName -Contains $AdUser.sAMAccountName -and $INTUNE_A_Applied -ne "True")
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $INTUNE_A
$LicensesAdded += "INTUNE_A"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($MEMBERS_OFFICESUBSCRIPTION_FACULTY.SamAccountName -Contains $AdUser.sAMAccountName -and $OFFICESUBSCRIPTION_FACULTY_Applied -ne "True")
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $OFFICESUBSCRIPTION_FACULTY
$LicensesAdded += "OFFICESUBSCRIPTION_FACULTY"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($MEMBERS_OFFICESUBSCRIPTION_STUDENT.SamAccountName -Contains $AdUser.sAMAccountName -and $OFFICESUBSCRIPTION_STUDENT_Applied -ne "True")
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $OFFICESUBSCRIPTION_STUDENT
$LicensesAdded += "OFFICESUBSCRIPTION_STUDENT"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
Catch [System.Exception]
{
#Mop up any other errors
Send-MailMessage -From admin@blah.com -To someadmin@blah.com -cc someotheradmin@blah.com -Subject "ERROR - O365 Licence Management" -Body $_.Exception.Message -SmtpServer smtp.blah.com
Write-Host $_.Exception.Message
}
#Reporting bits:
If ($LicensesAdded.Count -eq 0 -and $LicensesRemoved.Count -gt 0)
{
$LoggingContent=$Date + ",User," + $User.UserPrincipalName + ",LicensesRemoved," + ($LicensesRemoved -join ',')
}
If ($LicensesAdded.Count -gt 0 -and $LicensesRemoved.Count -eq 0)
{
$LoggingContent=$Date + ",User," + $User.UserPrincipalName + ",Licenses Added," + ($LicensesAdded -join ',')
}
If ($LicensesAdded.Count -gt 0 -and $LicensesRemoved.Count -gt 0)
{
$LoggingContent=$Date + ",User," + $User.UserPrincipalName + ",Licenses Added," + ($LicensesAdded -join ',') + ",LicensesRemoved," + ($LicensesRemoved -join ',')
}
# Write out the log
Add-Content $Logfile $LoggingContent
# If any errors occurred adding or removing icences - the variable $strMailBody will have some content, so send it to admins...
If ($strMailBody -ne $null)
{
Send-MailMessage -From admin@blah.com -To someadmin@blah.com -cc someotheradmin@blah.com -Subject "ERROR - O365 Licence Management" -Body $strMailBody -SmtpServer smtp.blah.com
Write-Host $strMailBody
}
}
}
############ Now mop up the unlicenced users ############
ForEach($User in (Get-MsolUser -all | where{$_.isLicensed -ne "True"}))
{
If($User.UserPrincipalName -notlike "*<YourTenancyName>.onmicrosoft.com" -and $User.UserPrincipalName -ne "DirSync@blah.com")
{
$date = Get-Date -Format $dateFormat
$LoggingContent=$null
$LicensesAdded=@()
$AdUser=Get-ADUser -Filter {UserPrincipalName -eq $User.UserPrincipalName} -Properties extensionAttribute5, sAMAccountName
Try
{
If ($ADUser.extensionAttribute5 -eq "Staff")
{
Try
{
# Set usage location
Set-MsolUser -UserPrincipalName $User.UserPrincipalName -UsageLocation $UsageLocation
}
Catch [System.Exception]
{
$strBody="Error setting UsageLocation for $User.UserPrincipalName"
$strMailBody=$strMailBody+$strBody
}
Try
{
# Set standard O365 licence without Exchange
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_FACULTY -LicenseOptions $FacultyLicenseOptions
$LicensesAdded += "STANDARDWOFFPACK_FACULTY"
##### Add user to the AD group - just for reference.... #####
Add-ADGroupMember -Identity STANDARDWOFFPACK_FACULTY_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($ADUser.extensionAttribute5 -ne "Staff")
{
Try
{
# Set usage location
Set-MsolUser -UserPrincipalName $User.UserPrincipalName -UsageLocation $UsageLocation
}
Catch [System.Exception]
{
$strBody="Error setting UsageLocation for $User.UserPrincipalName"
$strMailBody=$strMailBody+$strBody
}
Try
{
# Set standard O365 licence without Exchange
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $STANDARDWOFFPACK_STUDENT -LicenseOptions $StudentLicenseOptions
$LicensesAdded += "STANDARDWOFFPACK_STUDENT"
##### Add user to the AD group - just for reference.... #####
Add-ADGroupMember -Identity STANDARDWOFFPACK_STUDENT_Users -Members $AdUser.sAMAccountName
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
##### Individual Subscriptions for unlicenced users - unlikely to ever hit here... #####
If ($MEMBERS_PROJECTONLINE_PLAN_1_FACULTY.SamAccountName -Contains $AdUser.sAMAccountName)
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $PROJECTONLINE_PLAN_1_FACULTY
$LicensesAdded += "PROJECTONLINE_PLAN_1_FACULTY"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($MEMBERS_PROJECTONLINE_PLAN_1_STUDENT.SamAccountName -Contains $AdUser.sAMAccountName)
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $PROJECTONLINE_PLAN_1_STUDENT
$LicensesAdded += "PROJECTONLINE_PLAN_1_STUDENT"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($MEMBERS_POWER_BI_STANDARD.SamAccountName -Contains $AdUser.sAMAccountName)
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $POWER_BI_STANDARD
$LicensesAdded += "POWER_BI_STANDARD"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($MEMBERS_CRMSTANDARD.SamAccountName -Contains $AdUser.sAMAccountName)
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $CRMSTANDARD
$LicensesAdded += "CRMSTANDARD"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($MEMBERS_INTUNE_A.SamAccountName -Contains $AdUser.sAMAccountName)
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $INTUNE_A
$LicensesAdded += "INTUNE_A"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($MEMBERS_OFFICESUBSCRIPTION_FACULTY.SamAccountName -Contains $AdUser.sAMAccountName)
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $OFFICESUBSCRIPTION_FACULTY
$LicensesAdded += "OFFICESUBSCRIPTION_FACULTY"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
If ($MEMBERS_OFFICESUBSCRIPTION_STUDENT.SamAccountName -Contains $AdUser.sAMAccountName)
{
Try
{
Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $OFFICESUBSCRIPTION_STUDENT
$LicensesAdded += "OFFICESUBSCRIPTION_STUDENT"
}
Catch [System.Exception]
{
$strBody=MailBody
$strMailBody=$strMailBody+$strBody
}
}
}
Catch [System.Exception]
{
Send-MailMessage -From admin@blah.com -To someadmin@blah.com -cc someotheradmin@blah.com -Subject "ERROR - O365 Licence Management" -Body $_.Exception.Message -SmtpServer smtp.blah.com
Write-Host $_.Exception.Message
}
$LoggingContent=$Date + ",User," + $User.UserPrincipalName + ",Licenses Added," + ($LicensesAdded -join ',')
# Write out the log
Add-Content $Logfile $LoggingContent
# If any errors occurred adding or removing icences - the variable $strMailBody will have some content, so send it to admins...
If ($strMailBody -ne $null)
{
Send-MailMessage -From admin@blah.com -To someadmin@blah.com -cc someotheradmin@blah.com -Subject "ERROR - O365 Licence Management" -Body $strMailBody -SmtpServer smtp.blah.com
Write-Host $strMailBody
}
}
}

Removing IE Proxy Settings

So, now we have gone full circle! There is now a need to remove the proxy settings from one of those VPN connections described in the last post.

For the machines that are supported within my group, the settings should be consistent, but there is the possibility that extra settings have been added by the user. So, I did a little discovery first:

OPTION EXPLICIT
ForceScriptEngine("cscript")
Const ForAppending = 8
Const HKEY_CURRENT_USER = &H80000001
Dim fso, WSHNetwork, strComputer, CScolItems, objItem, strCompDom, strComputerDomain, strLogPath, strFileDate, objTextFile, objReg, objWMIService, strKeyPath, arrValueNames, arrValueTypes, i, z, ValueName, iValues, BinaryValues(), strValues
Set fso = CreateObject("Scripting.FileSystemObject")
Set WSHNetwork = CreateObject("WScript.Network")
strComputer = WSHNetwork.ComputerName
'
Set objWMIService = GetObject( "winmgmts://" & strComputer & "/root/cimv2" )
Set CScolItems = objWMIService.ExecQuery("SELECT * FROM Win32_ComputerSystem")
For Each objItem in CScolItems
strComputerDomain = lcase(objItem.Domain)
Next
'SETUP SOME FILE/LOGGING PROPERTIES
strLogPath = "\\SOME_SERVER\SOME_VERY_OPEN_HIDDEN_SHARE$\"
strFileDate = Replace(FormatDateTime(Date(),2),"/","-")
'CREATE TEXT FILE SYSTEM OBJECT
Set fso = CreateObject("Scripting.FileSystemObject")
Set objTextFile = fso.OpenTextFile(strLogPath & "VPN_PROXIES_VALUES" & ".txt",8,True)
Set objReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
strComputer & "\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"
objReg.EnumValues HKEY_CURRENT_USER, strKeyPath, arrValueNames, arrValueTypes
For i=0 To UBound(arrValueNames)
If arrValueNames(i)="DefaultConnectionSettings" or arrValueNames(i)="SavedLegacySettings" or arrValueNames(i)="" Then
'Do Nothing
Else
ValueName = arrValueNames(i)
objReg.GetBinaryValue HKEY_CURRENT_USER, strKeyPath, ValueName, iValues
strValues=""
For z = lBound(iValues) to uBound(iValues)
strValues=strValues & iValues(z) & ","
Next
objTextFile.WriteLine strFileDate & "," & strComputer & "," & strComputerDomain & "," & ValueName & "," & strValues
End If
Next
objTextFile.Close
'TIDY UP
Set fso = Nothing
Set WSHNetwork = Nothing
Set strComputer = Nothing
Set strLogPath = Nothing
Set strFileDate = Nothing
Set objTextFile = Nothing
Set objWMIService = Nothing
'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Sub ForceScriptEngine(strScriptEng)
' Forces this script to be run under the desired scripting host.
' Valid arguments are "wscript" or "cscript".
' The command line arguments are passed on to the new call.
Dim arrArgs
Dim strArgs
For Each arrArgs In WScript.Arguments
strArgs = strArgs & " " & Chr(34) & arrArgs & Chr(34)
Next
If Lcase(Right(Wscript.FullName, 12)) = "\wscript.exe" Then
If Instr(1, Wscript.FullName, strScriptEng, 1) = 0 Then
CreateObject("Wscript.Shell").Run "cscript.exe //Nologo " & _
Chr(34) & Wscript.ScriptFullName & Chr(34) & strArgs
Wscript.Quit
End If
Else
If Instr(1, Wscript.FullName, strScriptEng, 1) = 0 Then
CreateObject("Wscript.Shell").Run "wscript.exe " & Chr(34) & _
Wscript.ScriptFullName & Chr(34) & strArgs
Wscript.Quit
End If
End If
End Sub

The resultant file shows mostly what I’m expecting to see, with a few other things like Vodafone dongles, user defined connections etc.

Removal can be easily carried out by trimming the setting script (from the last post) to no longer publish the one to be removed, then use a GPP item to remove the key relating to the VPN proxy settings, to be removed.

For those other groups, within the organisation, who do not have such consistency; the results of the script can be analysed in Excel to see what is “out there”. Then another (not yet written script – because I don’t think I’ll need it) can be used to find those binary values which match those that are to be removed and then subsequently remove them.

I like scripting…. 😉

Setting IE Proxy Settings

A number of years ago, I rationalised the way that IE proxy settings were delivered to supported systems. Previously, this had been done via installation scripts, which were not enforced after application, so were prone to subsequent error.

To achieve consistency and ease of deployment Group Policy was used. Back then there were Internet Explorer Maintenance GPO settings, which allowed the LAN Proxy setting to be deployed easily. However, I also wanted to make consistent the delivery of VPN settings. This was also achieved by using Group Policy to deliver a VPN address book (rasphone.pbk) to the clients.

In order to get the correct proxy settings assigned to those VPN connections, I used a little scripting.

First, find out on my test machine what the text in the IE proxy GUI translated to, where I had already setup the VPN connections and set the correct proxy settings:

HKEY_CURRENT_USER = &H80000001
strComputer = "."
Const ForAppending = 8
strComputer = "."
'Setup text file to write results to
Set fso = CreateObject("Scripting.FileSystemObject")
Set objTextFile = fso.OpenTextFile("C:\temp\proxy.txt",ForAppending, True)
dim iValues(1)
Set objReg = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"
'
ValueName = "DefaultConnectionSettings"
objReg.GetBinaryValue HKEY_CURRENT_USER, strKeyPath, ValueName, iValues
objTextFile.WriteLine ValueName & ": " & VbCrLf
For i = lBound(iValues) to uBound(iValues)
objTextFile.Write iValues(i) & ","
Next
objTextFile.WriteLine VbCrLf & "********************************************************************"
'
ValueName = "RandomVPN2"
objReg.GetBinaryValue HKEY_CURRENT_USER, strKeyPath, ValueName, iValues
objTextFile.WriteLine ValueName & ": " & VbCrLf
For i = lBound(iValues) to uBound(iValues)
objTextFile.Write iValues(i) & ","
Next
objTextFile.WriteLine VbCrLf & "********************************************************************"
'
ValueName = "RandomVPN2"
objReg.GetBinaryValue HKEY_CURRENT_USER, strKeyPath, ValueName, iValues
objTextFile.WriteLine ValueName & ": " & VbCrLf
For i = lBound(iValues) to uBound(iValues)
objTextFile.Write iValues(i) & ","
Next
objTextFile.WriteLine VbCrLf & "********************************************************************"

Then use the results of that to create a new vbs script to be pushed out as a User login script:

OPTION EXPLICIT
Const HKEY_CURRENT_USER = &H80000001
Dim strComputer, objRegistry, strKeyPath, strValueName, arrValues, errReturn
strComputer = "."
Set objRegistry = GetObject ("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections"
strValueName = "RandomVPN1"
arrValues = Array(70,0,0,0,3,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,39,0,0,0,104,116,116,112,58,47,47,109,121,112,114,111,120,121,115,101,114,118,101,114,46,98,108,97,104,46,99,111,109,47,112,114,111,120,121,46,112,97,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
'Translates to: Use automatic configuration script (ticked), plus http://myproxyserver.blah.com/proxy.pac as the proxy
errReturn = objRegistry.SetBinaryValue (HKEY_CURRENT_USER, strKeyPath, strValueName, arrValues)
strValueName = "RandomVPN2"
arrValues = Array(70,0,0,0,3,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,44,0,0,0,104,116,116,112,58,47,47,109,121,111,116,104,101,114,112,114,111,120,121,115,101,114,118,101,114,46,98,108,97,104,46,99,111,109,47,112,114,111,120,121,46,112,97,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
'Translates to: Use automatic configuration script (ticked), plus http://myotherproxyserver.blah.com/proxy.pac as the proxy
errReturn = objRegistry.SetBinaryValue (HKEY_CURRENT_USER, strKeyPath, strValueName, arrValues)

Getting license details for your licensed O365 users

Part of the process of getting the script in the previous post working was having a record of who is licenced *now*.

This was done using the following script. Note that the commented lines must be un-commented to log the data, I am currently using this script to keep the AD groups up to date , hence the Add-ADGroupMember lines are uncommented. AD groups will soon be replacing the text files.

$ErrorActionPreference = "Continue"
$sig = @"
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct NativeCredential
{
public UInt32 Flags;
public CRED_TYPE Type;
public IntPtr TargetName;
public IntPtr Comment;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
public UInt32 CredentialBlobSize;
public IntPtr CredentialBlob;
public UInt32 Persist;
public UInt32 AttributeCount;
public IntPtr Attributes;
public IntPtr TargetAlias;
public IntPtr UserName;
internal static NativeCredential GetNativeCredential(Credential cred)
{
NativeCredential ncred = new NativeCredential();
ncred.AttributeCount = 0;
ncred.Attributes = IntPtr.Zero;
ncred.Comment = IntPtr.Zero;
ncred.TargetAlias = IntPtr.Zero;
ncred.Type = CRED_TYPE.GENERIC;
ncred.Persist = (UInt32)1;
ncred.CredentialBlobSize = (UInt32)cred.CredentialBlobSize;
ncred.TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName);
ncred.CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob);
ncred.UserName = Marshal.StringToCoTaskMemUni(System.Environment.UserName);
return ncred;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Credential
{
public UInt32 Flags;
public CRED_TYPE Type;
public string TargetName;
public string Comment;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
public UInt32 CredentialBlobSize;
public string CredentialBlob;
public UInt32 Persist;
public UInt32 AttributeCount;
public IntPtr Attributes;
public string TargetAlias;
public string UserName;
}
public enum CRED_TYPE : uint
{
GENERIC = 1,
DOMAIN_PASSWORD = 2,
DOMAIN_CERTIFICATE = 3,
DOMAIN_VISIBLE_PASSWORD = 4,
GENERIC_CERTIFICATE = 5,
DOMAIN_EXTENDED = 6,
MAXIMUM = 7, // Maximum supported cred type
MAXIMUM_EX = (MAXIMUM + 1000), // Allow new applications to run on old OSes
}
public class CriticalCredentialHandle : Microsoft.Win32.SafeHandles.CriticalHandleZeroOrMinusOneIsInvalid
{
public CriticalCredentialHandle(IntPtr preexistingHandle)
{
SetHandle(preexistingHandle);
}
public Credential GetCredential()
{
if (!IsInvalid)
{
NativeCredential ncred = (NativeCredential)Marshal.PtrToStructure(handle,
typeof(NativeCredential));
Credential cred = new Credential();
cred.CredentialBlobSize = ncred.CredentialBlobSize;
cred.CredentialBlob = Marshal.PtrToStringUni(ncred.CredentialBlob,
(int)ncred.CredentialBlobSize / 2);
cred.UserName = Marshal.PtrToStringUni(ncred.UserName);
cred.TargetName = Marshal.PtrToStringUni(ncred.TargetName`);
cred.TargetAlias = Marshal.PtrToStringUni(ncred.TargetAlias);
cred.Type = ncred.Type;
cred.Flags = ncred.Flags;
cred.Persist = ncred.Persist;
return cred;
}
else
{
throw new InvalidOperationException("Invalid CriticalHandle!");
}
}
override protected bool ReleaseHandle()
{
if (!IsInvalid)
{
CredFree(handle);
SetHandleAsInvalid();
return true;
}
return false;
}
}
[DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr CredentialPtr);
[DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
public static extern bool CredFree([In] IntPtr cred);
"@
Add-Type -MemberDefinition $sig -Namespace "ADVAPI32" -Name 'Util'
$targetName = "LicenceManagment"
$nCredPtr= New-Object IntPtr
$success = [ADVAPI32.Util]::CredRead($targetName,1,0,[ref] $nCredPtr)
if($success){
$critCred = New-Object ADVAPI32.Util+CriticalCredentialHandle $nCredPtr
$cred = $critCred.GetCredential()
$UserName = $cred.UserName;
$Password = $cred.CredentialBlob;
$Password = ConvertTo-SecureString -String $Password -AsPlainText -Force
$objCreds = New-Object Management.Automation.PSCredential $UserName, $Password
}
####
If(@(Get-Module | ? { $_.Name -eq "MSOnline"}).Count -eq 0)
{
Import-Module MSOnline;
}
If(@(Get-Module | ? { $_.Name -eq "ActiveDirectory"}).Count -eq 0)
{
Import-Module ActiveDirectory;
}
$LoggingFile="C:\Office365-Scripts\LicenceLog.txt"
$POWER_BI_STANDARD="C:\Office365-Scripts\POWER_BI_STANDARD.txt"
$STANDARDWOFFPACK_FACULTY="C:\Office365-Scripts\STANDARDWOFFPACK_FACULTY.txt"
$OFFICESUBSCRIPTION_FACULTY="C:\Office365-Scripts\OFFICESUBSCRIPTION_FACULTY.txt"
$OFFICESUBSCRIPTION_STUDENT="C:\Office365-Scripts\OFFICESUBSCRIPTION_STUDENT.txt"
$STANDARDWOFFPACK_IW_STUDENT="C:\Office365-Scripts\STANDARDWOFFPACK_IW_STUDENT.txt"
$CRMSTANDARD="C:\Office365-Scripts\CRMSTANDARD.txt"
$INTUNE_A="C:\Office365-Scripts\INTUNE_A.txt"
$STANDARDWOFFPACK_IW_FACULTY="C:\Office365-Scripts\STANDARDWOFFPACK_IW_FACULTY.txt"
$PROJECTONLINE_PLAN_1_FACULTY="C:\Office365-Scripts\PROJECTONLINE_PLAN_1_FACULTY.txt"
$STANDARDWOFFPACK_STUDENT="C:\Office365-Scripts\STANDARDWOFFPACK_STUDENT.txt"
Connect-MsolService -Credential $objCreds
$Users=Get-MsolUser -all | where {$_.isLicensed -eq "True"}
ForEach ($User in $Users)
{
If($User.UserPrincipalName -notlike "*<YourTenancyName>.onmicrosoft.com" -and $User.UserPrincipalName -ne "DirSync@blah.com")
{
$AdUser=Get-ADUser -Filter {UserPrincipalName -eq $User.UserPrincipalName} -Properties extensionAttribute5, sAMAccountName
$List=$NULL
$Licences=$User.Licenses
$SKUIDs=$Licences.AccountSkuId
ForEach ($SKU in $SKUIDs)
{
If ($SKU -eq "<YourTenancyName>:POWER_BI_STANDARD")
{
Add-ADGroupMember -Identity POWER_BI_STANDARD_Users -Members $AdUser.sAMAccountName
#Add-Content $POWER_BI_STANDARD $User.UserPrincipalName
}
If ($SKU -eq "<YourTenancyName>:STANDARDWOFFPACK_FACULTY")
{
Add-ADGroupMember -Identity STANDARDWOFFPACK_FACULTY_Users -Members $AdUser.sAMAccountName
#Add-Content $STANDARDWOFFPACK_FACULTY $User.UserPrincipalName
}
If ($SKU -eq "<YourTenancyName>:OFFICESUBSCRIPTION_FACULTY")
{
Add-ADGroupMember -Identity OFFICESUBSCRIPTION_FACULTY_Users -Members $AdUser.sAMAccountName
#Add-Content $OFFICESUBSCRIPTION_FACULTY $User.UserPrincipalName
}
If ($SKU -eq "<YourTenancyName>:OFFICESUBSCRIPTION_STUDENT")
{
Add-ADGroupMember -Identity OFFICESUBSCRIPTION_STUDENT_Users -Members $AdUser.sAMAccountName
#Add-Content $OFFICESUBSCRIPTION_STUDENT $User.UserPrincipalName
}
#If ($SKU -eq "<YourTenancyName>:STANDARDWOFFPACK_IW_STUDENT")
#{
##Add-Content $STANDARDWOFFPACK_IW_STUDENT $User.UserPrincipalName
#}
If ($SKU -eq "<YourTenancyName>:CRMSTANDARD")
{
Add-ADGroupMember -Identity CRMSTANDARD_Users -Members $AdUser.sAMAccountName
#Add-Content $CRMSTANDARD $User.UserPrincipalName
}
If ($SKU -eq "<YourTenancyName>:INTUNE_A")
{
Add-ADGroupMember -Identity INTUNE_A_Users -Members $AdUser.sAMAccountName
#Add-Content $INTUNE_A $User.UserPrincipalName
}
#If ($SKU -eq "<YourTenancyName>:STANDARDWOFFPACK_IW_FACULTY")
#{
##Add-Content $STANDARDWOFFPACK_IW_FACULTY $User.UserPrincipalName
#}
If ($SKU -eq "<YourTenancyName>:PROJECTONLINE_PLAN_1_FACULTY")
{
Add-ADGroupMember -Identity PROJECTONLINE_PLAN_1_FACULTY_Users -Members $AdUser.sAMAccountName
#Add-Content $PROJECTONLINE_PLAN_1_FACULTY $User.UserPrincipalName
}
If ($SKU -eq "<YourTenancyName>:STANDARDWOFFPACK_STUDENT")
{
Add-ADGroupMember -Identity STANDARDWOFFPACK_STUDENT_Users -Members $AdUser.sAMAccountName
#Add-Content $STANDARDWOFFPACK_STUDENT $User.UserPrincipalName
}
$List = $List + $SKU + "|"
}
Sort-object $List
Write-Host $List
$Content=$User.UserPrincipalName + "|" + $List
#Add-Content $LoggingFile $Content
}
}