Category Archives: Active Directory

Offline Domain Join and Direct Access – A real life example

A few days ago I got a new computer at work. I took it home to install it, but to finish the installation and log on with my domain account I needed access to the corporate network.

I had to find a solution to finalize the installation without going back to the office. Luckily we have Direct Access configured at my company, and all I need to connect is a domain joined computer. A perfect opportunity to use the Offline Domain Join feature.

For this to work we need some prerequisites in place:

  • A Windows Server 2012 or newer Direct Access server
  • A domain controller with at least Windows Server 2008 R2
  • At least Windows 8 on the client computer

Note: Offline Domain Join works in Windows 7 as well, but these are the minimum requirements to use it in conjunction with Direct Access

This process is performed in two steps: First we have to provision the computer in Active Directory. We create a computer account, and store metadata about the computer in a file. The metadata file is then used on the computer to finish the process.

The information we have to provide during the provisioning is:

  • The name of the domain that we want to join the new computer to
  • The name of the computer
  • Where to save the metadata file
  • The Direct Access Group Policy, to get all settings to connect using Direct Access
  • We also need to issue a computer certificate for Direct Access, so we have to specify what Certificate Template to use.

Let’s use Djoin.exe to provision the new computer. This command must run on a machine that is already a member of the domain, and you need rights to add workstations to the domain:

Djoin.exe /provision /domain corp.tailspintoys.com /machine PC5441 /savefile c:\OfflineDomainJoin.txt /policynames "DirectAccess Client Settings" /policypaths "\\corp.tailspintoys.com\SYSVOL\corp.tailspintoys.com\Policies\{GUID}\Machine\Registry.pol" /certtemplate Machine

Now, I copy OfflineDomainJoin.txt to the new computer and run Djoin.exe again to insert the metadata into the computer. Please be aware that this file contains sensitive information and should not be sent unencrypted.

Djoin.exe /requestODJ /loadfile C:\OfflineDomainJoin.txt /windowspath %systemroot% /localos 

Finally, a restart is required. After the restart I’m connected with Direct Access and it is possible to log on with my domain credentials.

/ Andreas

Advertisements

PowerShell GPO Reporting: WSUS

In addition to my earlier creations that gives you an inventory of your GPO Deployed Printers and GPP Drive Maps, I’ve now created a similiar script that makes inventory of WSUS settings in all your GPO’s.
This can very much come in handy when having an extensive amount of GPO’s that are controlling WSUS settings (e.g. for different maintenance schedules).

See the example below of the output:
2014-02-05 07-17-52

<#
.SYNOPSIS     
Function that find certain information about all your WSUS related GPO's.
.NOTES     
           File Name: Get-GPOWsusInfo    
           Author   : Johan Dahlbom, johan[at]dahlbom.eu     
           The script are provided “AS IS” with no guarantees, no warranties, and it confer no rights. 
           Blog     : 365lab.net
#>
function Get-GPOWsusInfo {

try
{
Import-Module GroupPolicy -ErrorAction Stop
}
catch
{
throw "Module GroupPolicy not Installed"
}
        $GPO = Get-GPO -All

        foreach ($Policy in $GPO){

                $GPOID = $Policy.Id
                $GPODom = $Policy.DomainName
                $GPODisp = $Policy.DisplayName

                [xml]$xml = Get-GPOReport -Id $GPOID -ReportType xml
                $GPOSec = Get-GPPermissions -All -Guid $GPOID
                $WSUSBase = Get-GPRegistryValue -Guid $GPOID -Key HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\WindowsUpdate -ErrorAction SilentlyContinue
                $WSUSAU = Get-GPRegistryValue -Guid $GPOID -Key HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\WindowsUpdate\AU   -ErrorAction SilentlyContinue

                    if ($WsusBase) {

                        New-Object PSObject -Property @{
                           	GPOName = $GPODisp
                            GPOLinks = $xml.gpo.Linksto.SOMPath
                            GPOFilter = ($GPOSec | Where-Object {$_.Permission -eq "GpoApply"}).trustee.name
                            ScheduleDay = ($WSUSAU | Where-Object {$_.ValueName -eq "ScheduledInstallDay"}).Value.tostring().Replace("0","0 - Every Day").Replace("1","1 - Sunday").Replace("2","2 - Monday").Replace("3","3 - Tuesday").Replace("4","4 - Wednesday").Replace("5","5 - Thursday").Replace("6","6 - Friday").Replace("7","7 - Saturday")
                            Installtime =  ($WSUSAU | Where-Object {$_.ValueName -eq "ScheduledInstallTime"}).Value.tostring() + ':00'
                            AutoUpdateSetting = ($WSUSAU | Where-Object {$_.ValueName -eq "AUOptions"}).Value.tostring().Replace("2","2 - Notify for download and notify for install").Replace("3","3 - Auto download and notify for install").Replace("4","4 - Auto download and schedule the install").Replace("5","5 - Allow local admin to choose setting")
                            WSUSTargetGroup = ($WSUSBase | Where-Object {$_.ValueName -eq "TargetGroup"}).Value
                            WSUSServer = ($WSUSBase | Where-Object {$_.ValueName -eq "WUServer"}).Value
                        }
                    }

           }
}

Hope you find this useful!
When I find the time I’ll create a more complete set of GPO reporting functions with more functionality than they have today, maybe with help from Ramblingcookiemonster that has extended and created additions to the GP Preferences functions.

Until next time, Happy GPO Reporting!

/Johan

DirSync Configuration Error: “There is no such object on the server”

Recently, I ran across an issue deploying DirSync at a Customer.
The installation went well, but running the “Directory Sync Configuration Wizard” failed with the error message “There is no such object on the server”.
2014-02-03 09-14-26

Investigating the event logs, the configuration seemed to stop at the following event:
2014-02-03 09-19-06
Looking in to AD after the wizard had failed, no account had been created.
I then noticed that the Users container in the domain root was missing.
Since it is there DirSync by default tries to create the sync account and the container didn’t exist, it failed.

After recreating the Users container (can be done with either AdsiEdit or PowerShell), the Configuration Wizard completed successfully.

Recreate the users container with PowerShell

New-ADObject -Description "Default container for upgraded user accounts" ` 
             -Name Users ` 
             -Type Container `
             -ProtectedFromAccidentalDeletion $true

Hope this helps you if you are running in to this problem!

/Johan

Synchronizing users between two AD Domains without trust

AD LDS SyncRecently I had an interesting job at a customer, who used a web application with an address book. The web application used LDAP queries to Active Directory to get all users addresses and phone numbers. This information was then searchable on a public web site located in DMZ.

The problem here is that we had an internet facing server. We could not allow it to make queries directly to the internal Active Directory. We decided to install a new AD LDS (Lightweight Directory Services) partition in the DMZ where we could ask our queries.

Next problem: We have to populate our new LDS partition with users. Normally this would be done with a system designed for it, like Forefront Identity Manager. Here we needed a quick-and-dirty solution to get started, and I therefore wrote a PowerShell script that copied the attributes we needed from our Active Directory.

One of the first things to solve was to find who and what to sync. With ten thousands of users in Active Directory we needed a solution to select users, even though they were spread across several OUs. Also, Swedish laws regulate what information to publish, so we had to filter sensitive information.

We ended up with a solution where only users in a specific OU were synced, but they also needed to be member of a specific group. We also filtered disabled users.

$ADUsers = Get-ADGroupMember -Identity "grp_ADSyncUsers" -Recursive | Get-ADUser -Properties `
     SamAccountName,GivenName,Surname,DisplayName,Title,Office,Department,Company,EmailAddress,Mobile,OfficePhone,Modified,Enabled | `
     Where-Object { $_.Enabled -eq $true -and $_.DistinguishedName -like "*$sourceou" }

We now have too much data. Our sync will be too slow, so we need to filter again.

There is no need to process accounts that haven’t been changed recently. We simply checked the Modified attribute in active directory and skipped these.

if ((get-date $modified) -ge ($(get-date).AddDays(-1.5))) {
    ...
}

We also needed a method to detect deleted accounts. Here we simply got all users in AD LDS and compared it with the users in AD DS. Any extra users were removed.

Compare-Object $PhoneBookUsers.Name $ADUserNames | Where-Object { $_.SideIndicator -eq "<=" } | ForEach-Object {
    $id="cn=$($_.InputObject),$destou"
    Remove-ADUser -Identity $id -Server $destserver -Credential $cred -Partition $destpartition -Confirm:$false
}

Below is the entire script, but as I wrote, it is a quick-and-dirty solution just to get started.

$destserver = "10.10.10.5"
$destpartition = "dc=cloud,dc=net"
$destou = "ou=PhoneBook,dc=cloud,dc=net"
$sourceou = "ou=MyBusiness,dc=contoso,dc=com"

$cred = Get-Credential
$i = 0

Import-Module ActiveDirectory

# Define users to process

    Write-Host "Collecting user information..."

    $ADUsers = Get-ADGroupMember -Identity "grp_ADSyncUsers" -Recursive | Get-ADUser -Properties `
            SamAccountName,GivenName,Surname,DisplayName,Title,Office,Department,Company,EmailAddress,Mobile,OfficePhone,Modified,Enabled | `
            Where-Object { $_.Enabled -eq $true -and $_.DistinguishedName -like "*$sourceou" }

    Write-Host "$($ADUsers.Count) user(s) found in Active Directory."

# Create/update users

    $ADUsers | ForEach-Object {

        $sam = $_.Name
        $givenname = $_.GivenName
        $surname = $_.Surname
        $displayname = $_.DisplayName
        $email = $_.EmailAddress
        $department = $_.Department
        $office = $_.Office
        $title = $_.Title
        $company = $_.Company
        $tele = $_.OfficePhone
        $mobil = $_.Mobile
        $modified = $_.Modified

        if ((get-date $modified) -ge ($(get-date).AddDays(-1.5))) {
            # Process user accounts modified the last 36 hours

            $i++;

            try {
                # Create new account in AD LDS
                New-ADUser -SamAccountName $sam -Name $sam -DisplayName $displayname -GivenName $givenname `
                    -SurName $surname -EmailAddress $email -Title $title -Office $office -Department $department `
                    -Company $company -OfficePhone $tele -MobilePhone $mobil `
                    -Enabled $false -Path $destou –Type iNetOrgPerson `
                    -Server $destserver `
                    -Credential $cred

                Write-Host "Creating user $sam"

            } catch [System.Exception] {
                switch($_.Exception.GetType().FullName) {
                    'Microsoft.ActiveDirectory.Management.ADException' {
                        # User already exists. Update existing account
                        Set-ADUser -Identity "cn=$sam,$destou" -DisplayName $displayname -GivenName $givenname -SurName $surname `
                            -EmailAddress $email -Title $title -Office $office -Department $department `
                            -Company $company -OfficePhone $tele -MobilePhone $mobil `
                            -Server $destserver `
                            -Credential $cred `
                            -Partition $destpartition
                        Write-Host "Updating user $sam"
                    }
                }
            }
        }
    }

# Remove deleted accounts

Write-Host "$i user(s) processed."
Write-Host "Searching for users to delete..."
$i=0

    $PhoneBookUsers = Get-ADUser -Server $destserver -SearchBase $destou -Filter * -Credential $cred | Select -Property Name
    $ADUserNames += $ADUsers.Name

    Compare-Object $PhoneBookUsers.Name $ADUserNames | Where-Object { $_.SideIndicator -eq "<=" } | ForEach-Object {
        $id="cn=$($_.InputObject),$destou"
        Remove-ADUser -Identity $id -Server $destserver -Credential $cred -Partition $destpartition -Confirm:$false
        Write-Host "Deleting user $($_.InputObject)"
        $i++
    }

Write-Host "$i user(s) deleted."
Write-Host "Done!"

/ Andreas

Friendly Reminder – do NOT assign passwords through Group Policy Preferences

This is an issue that have been out for quite a while, but I see it a lot when checking customers environments, so it’s always good to remind yourself about this kind of important things.

Through GPP, you have the ability to do lots of stuff, one of them is fiddling with builtin local accounts on computers and even change password on those.

A common case is applying GPP settings as below to your client computers.
2014-01-09 11-48-522014-01-09 11-49-06
Note the warning about where the password will be stored.

It actually gets worse from there… with the script which can be downloaded here, you can extract ALL passwords from ALL GPP’s you have since they are stored in XML files located on the sysvol in your domain.
OK, who have access to read those files then?
Authenticated Users, which means whoever that has an account in your domain can do this.
2014-01-09 11-50-01

Conclusion: do not change password with GPP unless you do it and remove the policy directly after the policy has successfully been applied.

In this blog post series from Microsoft PFE’s, you find a great solution to handle and store local admin passwords in a secure, yet manageble way.

/Johan

Create random test users in a domain with PowerShell

Keeping up with the PowerShell posts!

This is a quick one I’ve created based on another one I once found to make it faster for me to create a number of test users when doing demos/labs.
It uses a .csv file as input with some 400 first and lastnames, which then is used for generating random usernames. (3+3 letters and incrementing number if needed. Based on a solution that can be done in many ways… )
We are also picking departments from the array $Departments (if you have the luck of getting a extra nice firstname, you get another department)

The only thing you need to do in order to get it working in your own lab or test domain (except for the script and the .csv file), is to insert the an OU distinguishedName to the $OU variable.

It will by default create 100 users, but that can be changed to a number of your choice with the -Numusers parameter.

2014-01-08 14-43-32
Download the script and the csv file here

CreateTestADUsers.ps1

<#
.SYNOPSIS
    The Script creates test users to your demo domain based on first and last namnes from csv. 
.PARAMETER NumUsers
    Integer - number of users to create, default 100.
.NOTES
    File Name: CreateTestADUsers.ps1
    Author   : Johan Dahlbom, johan[at]dahlbom.eu
    Blog     : 365lab.net
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.    
#>
param ([parameter(Mandatory=$false)]
[int]
$NumUsers = "100"
)
#Define variables
$OU = "OU=TestUsers,OU=Cloud Inc.,DC=cloud,DC=lab"
$Departments = @("IT","Finance","Logistics","Sourcing","Human Resources")
$Names = Import-CSV FirstLastEurope.csv
$firstnames = $Names.Firstname
$lastnames = $Names.Lastname
$Password = "Password1"

#Import required module ActiveDirectory
try{
Import-Module ActiveDirectory -ErrorAction Stop
}
catch{
throw "Module ActiveDirectory not Installed"
}

while ($NumUsers -gt 0)
{
      #Choose a 'random' department Firstname and Lastname
      $firstname = $FirstNames | Get-Random
      $lastname = $LastNames | Get-Random

      if (($firstname -eq "Johan") -or ($firstname -eq  "Andreas")) {
                $Department = "Tailspintoys - 365lab.net"
            } else {
                $Department = $Departments | Get-Random
            }
      #Generate username and check for duplicates

      $username = $firstname.Substring(0,3).tolower() + $lastname.Substring(0,3).tolower()
      $exit = 0
      $count = 1
        do
        {
             try {
                 $userexists = Get-AdUser -Identity $username
                 $username = $firstname.Substring(0,3).tolower() + $lastname.Substring(0,3).tolower() + $count++
             }
             catch {
                 $exit = 1
             }
         }
        while ($exit -eq 0)

      #Set Displayname and UserPrincipalNBame
      $displayname = $firstname + " " + $lastname
      $upn = $username + "@" + (get-addomain).DNSRoot

      #Create the user
      Write-Host "Creating user $username in $ou"
      New-ADUser –Name $displayname –DisplayName $displayname `
                 –SamAccountName $username -UserPrincipalName $upn `
                 -GivenName $firstname -Surname $lastname -description "Test User" `
                 -Path $ou –Enabled $true –ChangePasswordAtLogon $false -Department $Department `
                 -AccountPassword (ConvertTo-SecureString $Password -AsPlainText -force)

      $NumUsers--
}

Feel free to edit or change as you wish!

/Johan

Get all GPO deployed Printers with PowerShell

As a follow up to my last post on GPP, and per request on Technet, I’ve now created Another script to make inventory of all printers in a domain deployed with GPO (both GPP and Deployed Printers). It can be a pain to use those settings some times, here you have a way to make inventory of them at least. 🙂
Just like the other one it’s based on the GroupPolicy PowerShell-Module which works from 2008R2 and up.

See below screenshots on how to run the script and what output you get. (similiar to the GPP Drive maps output)
2014-01-07 19-25-35
2014-01-07 19-26-04

If you want to export all printer information to a csv file, use below row.

.\Get-GPOPrinters.ps1 | select printerpath,gpotype,gponame,FilterGroup  | Export-Csv c:\printers.csv -NoTypeInformation

Get-GPOPrinters.ps1

<#
.SYNOPSIS     
The script finds all shared printers deployed with GPO (both deployed printers GPP.) in your domain. 
.NOTES     
           File Name: Get-GPOPrinters.ps1     
           Author   : Johan Dahlbom, johan[at]dahlbom.eu     
           The script are provided “AS IS” with no guarantees, no warranties, and it confer no rights. 
           Blog     : 365lab.net
#>
#Import the required module GroupPolicy
try
{
Import-Module GroupPolicy -ErrorAction Stop
}
catch
{
throw "Module GroupPolicy not Installed"
}
        $GPO = Get-GPO -All

        foreach ($Policy in $GPO){

                $GPOID = $Policy.Id
                $GPODom = $Policy.DomainName
                $GPODisp = $Policy.DisplayName
                $PrefPath = "\\$($GPODom)\SYSVOL\$($GPODom)\Policies\{$($GPOID)}\User\Preferences"

                    #Get GP Preferences Printers
                    $XMLPath = "$PrefPath\Printers\Printers.xml"
                    if (Test-Path "$XMLPath")
                    {
                         [xml]$PrintXML = Get-Content "$XMLPath"

                                foreach ( $Printer in $PrintXML.Printers.SharedPrinter )

                                    {New-Object PSObject -Property @{
                                        GPOName = $GPODisp
                                        PrinterPath = $printer.Properties.Path
                                        PrinterAction = $printer.Properties.action.Replace("U","Update").Replace("C","Create").Replace("D","Delete").Replace("R","Replace")
                                        PrinterDefault = $printer.Properties.default.Replace("0","False").Replace("1","True")
                                        FilterGroup = $printer.Filters.FilterGroup.Name
                                        GPOType = "Group Policy Preferences"
                                    }
                                }
                   }
                   #Get Deployed Printers
                   [xml]$xml = Get-GPOReport -Id $GPOID -ReportType xml
                   $User = $xml.DocumentElement.User.ExtensionData.extension.printerconnection
                   $Computer = $xml.DocumentElement.computer.ExtensionData.extension.printerconnection

                        foreach ($U in $User){
                            if ($U){

                                    New-Object PSObject -Property @{
                                        GPOName = $GPODisp
                                        PrinterPath = $u.Path
                                        GPOType = "GPO Deployed Printer - User"
                                    }
                            }

                        }

                        foreach ($C in $Computer){
                            if ($c){

                                    New-Object PSObject -Property @{
                                        GPOName = $GPODisp
                                        PrinterPath = $c.Path
                                        GPOType = "GPO Deployed Printer - Computer"
                                    }
                            }

                        }
           }

This was done very quick, will probably do some things a bit nicer later on. If you find any direct issues or bugs, let me know!

Enjoy!

/Johan

Managing Office 365 e-mail addresses easy with PowerShell when using DirSync

In most cases you uninstall your local Exchange server after migrating your e-mail to Exchange Online. If you also choose to implement DirSync you place the administration in your local domain instead of the Office 365 Administration Portal.

This means that there is no longer a GUI tool to handle some of the settings related to your mailboxes. Of course you can keep a machine with your old Exchange 2010 Management Console, but often the reason for moving to the cloud is to reduce the complexity and minimize the number of machines.

We don’t have the same problem with other attributes, for example displayname or phone numbers that you’ll find in Users and Computers or the Administrative Center introduced in Windows Server 2008. It does get a bit trickier when you are about to modify primary email address or add alias addresses.

Currently there are no available GUI tools from Microsoft to handle these types of updates easy (but keep your eyes open, things might change 😉 ). However, there are a few third-party tools to achieve this, unless you want to use the Attribute Editor in Users and Computers, or Adsiedit.

Since I’m a big fan of PowerShell I wanted to build a small set of tools to handle simple changes of email addresses. These functions are easy for you to use in your own scripts.

First of all, a function that lists all email addresses of a user:

function Get-O365AliasAddress {
<#
.SYNOPSIS
    Displays all email addresses assigned to a user.
.PARAMETER Identity
    The user to query.
.EXAMPLE
    Get-O365AliasAddress -Identity user01
.EXAMPLE
    Get-ADUser user01 | Get-O365AliasAddress
.NOTES
    Author: Andreas Lindahl
    Blog: 365lab.net
    Email: andreas.lindahl[at]jsc.se
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
#>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$True,ValueFromPipeline=$True,
        HelpMessage="The name of the user to get addresses of")]
        [string]$Identity
    )

    Import-Module ActiveDirectory

    $result = @()

    (Get-ADUser -Identity $Identity -Properties proxyAddresses).proxyAddresses | foreach {
        $proxy =  $_.split(":")
        $object = New-Object System.Object
        $object | Add-Member –Type NoteProperty –Name Type –Value $proxy[0]
        $object | Add-Member –Type NoteProperty –Name Address –Value $proxy[1]
        $object | Add-Member –Type NoteProperty –Name IsPrimary –Value ($proxy[0] -ceq $($proxy[0].ToUpper()))
        $result += $object
    }
    return $result
}

Next, a function that adds an alias to an existing user

function Add-O365AliasAddress {
<#
.SYNOPSIS
    Adds an alias address to a user.
.PARAMETER Identity
    The user to modify.
.PARAMETER Address
    The address to add.
.PARAMETER Type
    The type of address. Default is smtp.
.PARAMETER SetAsDefault
    Indicates if the address should be de default address of the user
.EXAMPLE
    Add-O365AliasAddress -Identity user01 -Address test@365lab.net -SetAsPrimary
.NOTES
    Author: Andreas Lindahl
    Blog: 365lab.net
    Email: andreas.lindahl[at]jsc.se
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
#>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$True,ValueFromPipeline=$True,
        HelpMessage="The name of the user")]
        [string]$Identity,

        [Parameter(Mandatory=$True,
        HelpMessage="The address to add")]
        [string]$Address,

        [Parameter(
        HelpMessage="The type of the address to add")]
        [string]$Type="smtp",

        [Parameter(
        HelpMessage="Indicates that the address will be set as the default address")]
        [switch]$SetAsDefault
    )

    Import-Module ActiveDirectory

    $Type = $Type.ToLower()

    $defaultaddress = ''
    $proxyaddresses = ''
    $proxyaddress = ''

    #Get all existing proxyAddresses of the same type
    $proxyaddresses = (Get-ADUser -Identity $Identity -Properties proxyAddresses).proxyAddresses | where-object { $_ -like "$Type*" }

    #Get current default address of this type
    foreach ($proxyaddress in $proxyaddresses) {
        $pa = $proxyaddress.split(':')
        if ($pa[0] -ceq $pa[0].ToUpper()) {
            $defaultaddress = $proxyaddress
        }
    }

    #If this is the first address, it will be the default
    if ($proxyaddresses.count -eq 0) {
        $SetAsDefault = $true
    }

    if ($SetAsDefault) {

        #New default address to set. Start by removing the old one, but keep it as alias.
        if ($defaultaddress) {
            $pa = $defaultaddress.split(':')
            $newdefaultaddress = "$($pa[0].ToLower()):$($pa[1])"
            Set-ADUser -Identity $Identity -Remove @{proxyaddresses=$defaultaddress} -Add @{proxyaddresses=$newdefaultaddress}
        }

        #Set new default address. In case it already exists, remove it first (it might already be used as alias)
        if ($Type -eq 'SMTP') {
            Set-ADUser -Identity $Identity -Remove @{proxyaddresses="$($Type.ToLower()):$Address" } -Add @{proxyaddresses="$($Type.ToUpper()):$Address" } -EmailAddress $Address
        } else {
            Set-ADUser -Identity $Identity -Remove @{proxyaddresses="$($Type.ToLower()):$Address" } -Add @{proxyaddresses="$($Type.ToUpper()):$Address" }
        }

    } else {
        #Just add the new address
        Set-ADUser -Identity $Identity -Add @{proxyaddresses="$($Type):$Address" }
    }
}

Finally we also need a script to delete addresses

function Remove-O365AliasAddress {
<#
.SYNOPSIS
    Removes an alias address from a user.
.PARAMETER Identity
    The user to modify.
.PARAMETER Address
    The address to remove.
.PARAMETER Type
    The type of address. Default is smtp.
.EXAMPLE
    Remove-O365AliasAddress -Identity user01 -Address test@365lab.net
.NOTES
    Author: Andreas Lindahl
    Blog: 365lab.net
    Email: andreas.lindahl[at]jsc.se
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.
#>

    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$True,ValueFromPipeline=$True,
        HelpMessage="The name of the user")]
        [string]$Identity,

        [Parameter(Mandatory=$True,
        HelpMessage="The address to remove")]
        [string]$Address,

        [Parameter(
        HelpMessage="The type of the address to remove")]
        [string]$Type="smtp"

    )

    Import-Module ActiveDirectory

    $Type = $Type.ToLower()
    $defaultaddress = ''
    $newdefaultaddress = ''
    $proxyaddresses = ''

    #Get all existing proxyAddresses of the same type
    $proxyaddresses = (Get-ADUser -Identity $Identity -Properties proxyAddresses).proxyAddresses | where-object { $_ -like "$Type*" }

    #Get current default address of this type
    foreach ($proxyaddress in $proxyaddresses) {
        $pa = $proxyaddress.split(':')
        if ($pa[0] -ceq $pa[0].ToUpper()) {
            $defaultaddress = $proxyaddress
        }
    }

    if ($defaultaddress -eq "$($Type):$Address") {
        #We are trying to remove the default address. Now it becomes a bit tricky...
        #First, find the next address of the same type that we can use as default address
        foreach ($proxyaddress in $proxyaddresses) {
            if ($proxyaddress -ne "$($Type):$Address") {
                $newdefaultaddress = $proxyaddress
                break
            }
        }
    }

    #Now we can remove the address
    Set-ADUser -Identity $Identity -Remove @{proxyaddresses="$($Type):$Address"}
    if ($Type -eq 'smtp' -and $defaultaddress -eq "$($Type):$Address") {
        Set-ADUser -Identity $Identity -Clear mail
    }

    if ($newdefaultaddress) {
        #Set $newdefaultaddress as new default address
        Write-Warning "New default address set: $newdefaultaddress"
        Add-O365AliasAddress -Identity $Identity -Address $newdefaultaddress.split(":")[1] -Type $Type -SetAsDefault
    }
}

Now we can play with these functions:

O365AliasAddress

I have put together a PowerShell Module for you to import. Just load it with the Import-Module cmdlet.

Import-Module O365ProxyAddresses

The module can be downloaded here.

I hope that you will find these functions useful in your daily user administration tasks. Please leave your comments below and feel free to suggest improvements.

Happy coding!

/ Andreas

Getting all GPP Drive maps in a Domain with PowerShell

Group Policy Preferences are mostly great and frequently used to solve different kind of policy related problems in an it Environment.
There are a few PowerShell cmdlet for GPO’s (28 last time I checked) but only three of them are related to Group Policy Preferences.
2013-12-31 13-31-59.

Case and Solution
A customer of mine wanted to inventory their GPP Drive maps, and get information about what GPO, drive letter, drivepath, security Filtering and so on.

I know there are quite a few solutions that can do this for you, but why not use PowerShell when possible? 🙂

The script is using the GroupPolicy POSH-module which has been around since 2008R2, so this works even there.
It simply gets all Group policies in your domain, checks each policy for drive maps (by checking if Drives.xml exists) and gives you a bit of nice output. (ehh.. could be nicer, but anyway… )

See my examples below:
1. Gives you all Drive Map GPO’s plain and formats the output as a table.

2013-12-31 13-48-24
Note: The DriveAction object is not translated to a more friendly name at this time so U, stands for Update, D, for delete and so on. That is also applies on the DrivePersistent output (“Reconnect”).
Update: This is now updated so it gives you a bit of nicer output!

2. Searches for drive maps in a specific GPO.
2013-12-31 13-42-38

3. Exports the output to a csv.

.\Get-GPPDriveMaps.ps1 | Export-Csv DriveMaps.csv -NoTypeInformation

Get-GPPDriveMaps.ps1

<#
.SYNOPSIS     
           The script finds the GPP Drive Maps in your domain. 
.NOTES     
           File Name: Get-GPPDriveMaps.ps1     
           Author   : Johan Dahlbom, johan[at]dahlbom.eu     
           The script are provided “AS IS” with no guarantees, no warranties, and it confer no rights. 
#>
#Import the required module GroupPolicy
try
{
Import-Module GroupPolicy -ErrorAction Stop
}
catch
{
throw "Module GroupPolicy not Installed"
}
        $GPO = Get-GPO -All

        foreach ($Policy in $GPO){

                $GPOID = $Policy.Id
                $GPODom = $Policy.DomainName
                $GPODisp = $Policy.DisplayName

                 if (Test-Path "\\$($GPODom)\SYSVOL\$($GPODom)\Policies\{$($GPOID)}\User\Preferences\Drives\Drives.xml")
                 {
                     [xml]$DriveXML = Get-Content "\\$($GPODom)\SYSVOL\$($GPODom)\Policies\{$($GPOID)}\User\Preferences\Drives\Drives.xml"

                            foreach ( $drivemap in $DriveXML.Drives.Drive )

                                {New-Object PSObject -Property @{
                                    GPOName = $GPODisp
                                    DriveLetter = $drivemap.Properties.Letter + ":"
                                    DrivePath = $drivemap.Properties.Path
                                    DriveAction = $drivemap.Properties.action.Replace("U","Update").Replace("C","Create").Replace("D","Delete").Replace("R","Replace")
                                    DriveLabel = $drivemap.Properties.label
                                    DrivePersistent = $drivemap.Properties.persistent.Replace("0","False").Replace("1","True")
                                    DriveFilterGroup = $drivemap.Filters.FilterGroup.Name
                                }
                            }
                }
        }

The script can of course be extended with a lot more information, and make the output easier to read, but that’s a later project.

/Johan

Office 365 PowerShell Tip – Keeping EmailAddress and UPN in Sync

UPN’s are a nice way for our users to remember their usernames.  A UPN essentially has the same format as an email address.  Rather than having users login to their workstations or intranet resources as CLOUD\Username, they can login as user.lastname@cloud.com which can be the same as their email address.

Problem
When moving to Office 365 and are using DirSync with or without ADFS, your users need to have a UPN with a public routable domain in it. The easiest and most logical way to do that for your users sake is to keep UPN and primary email address the same.

Solution(s)
There are of course a lot of different solutions out there for achieveing the above, many of them include PowerShell and csv-files. My solution is based on PowerShell and are utilizing either Exchange Powershell cmdlets or Active Directory cmdlets.

Solution 1 (Exchange 2007 and above)
The below example is a very quick one that you run in Exchange Management Shell in Exchange 2007 and above. It simply copies the “WindowsPrimaryEmailAddress” for all your user mailboxes to the userPrincipalName attribute, without any question.

Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox | ForEach { Set-Mailbox -Identity $_.Guid.ToString() -UserPrincipalName $_.WindowsEmailAddress.ToString() } 

To verify that the change has been properly done, you can run the below command to list all mailboxes that have different primary email address and UPN:

Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox | Where-Object {$_.UserPrincipalName -ne $_.WindowsEmailAddress.ToString() }

Solution 2 (Exchange 2003 and above)
I had a case where the customer was running Exchange 2003, but had 2008R2 domain controllers. The customer also wanted a log file with the old and the new UPN after the change had been done. The script utilizes the ActiveDirectory module for PowerShell and copies the primary email address from the proxyAddresses attribute.
Running the script without any switches makes it run in test mode and do the AD-changes with the -WhatIf, so no changes will be done here.

2014-01-01 16-09-06

Running the script with the switch -Production makes it do the actual changes.2014-01-01 16-09-22

For backup purposes, the script are also creating a log file with the old and new attribute in the same folder where you run the script.

<#  
.SYNOPSIS 
    Script that copies the primary emailaddress from proxyAddresses to the userPrincipalName attribute.  
    It runs in test mode and just logs the changes that would have bee done without any parameters.  
    It identifies an exchange user with the legacyExchangeDN-attribute.  
.PARAMETER Production 
    Runs the script in production mode and makes the actual changes. 
.NOTES 
    Author: Johan Dahlbom 
    Blog: 365lab.net 
    Email: johan[at]dahlbom.eu 
    The script are provided “AS IS” with no guarantees, no warranties, and they confer no rights.     
#>
param(
[parameter(Mandatory=$false)]
[switch]
$Production = $false
)
#Define variables
$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
$DateStamp = Get-Date -Format "yyyy-MM-dd-HH-mm-ss"
$Logfile = $LogFile = ($PSScriptRoot + "\ProxyUPNSync-" + $DateStamp + ".log")
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
Write-Host $logstring
}
    try
    {
        Import-Module ActiveDirectory -ErrorAction Stop
    }
    catch
    {
        throw "Module ActiveDirectory not Installed"
    }

#For each AD-user with a legacyExchangeDN, look up primary SMTP: in proxyAddresses
#and use that as the UPN
$CollObjects=Get-ADObject -LDAPFilter "(&(legacyExchangeDN=*)(objectClass=user))" -Properties ProxyAddresses,distinguishedName,userPrincipalName

            foreach ($object in $CollObjects)
            {
                $Addresses = $object.proxyAddresses
                $DN=$object.distinguishedName
                    foreach ($Address In $Addresses)
                    {
                        $ProxyArray=($ProxyArray + "," + $Address)
                        If ($Address -cmatch "SMTP:")
                            {
                                $PrimarySMTP = $Address
                                $UserPrincipalName=$Address -replace ("SMTP:","")
                                    #Found the object validating UserPrincipalName
                                    If ($object.userPrincipalName -notmatch $UserPrincipalName) {
                                        #Run in production mode if the production switch has been used
                                        If ($Production) {
                                            LogWrite ($DN + ";" + $object.userPrincipalName + ";NEW:" + $UserPrincipalName)
                                            Set-ADObject -Identity $DN -Replace @{userPrincipalName = $UserPrincipalName}
                                        }
                                        #Runs in test mode if the production switch has not been used
                                        else {
                                            LogWrite ($DN + ";" + $object.userPrincipalName + ";NEW:" + $UserPrincipalName)
                                            Set-ADObject -Identity $DN -WhatIf -Replace @{userPrincipalName = $UserPrincipalName}
                                        }
                            }
                            else
                            {
                            Write-Host "Info: User $($object.UserPrincipalName) are already OK!"

                            }
                        }
                    }
            }

Hope the above was helpful to you, please let me know if you have any questions!

/Johan