Monthly Archives: January 2014

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

New kid on the block!

Great news!

We have a new kid on the block, Tailspintoys have as per today recruited a new writer, Andreas Lindahl. Andreas is also a Microsoft infrastructure guy that primarily focus on Office 365, Deployment and Active Directory. Read more about Andreas here.

Expect better continuity in amount of posts, since we now are two persons contributing!

/Johan

Quick Tip: Using a custom domain to connect to Office 365 Webmail

Office365-sign-inAfter migrating to Office 365 it can be hard to communicate the new webmail addresses to all end-users. Especially when performing a cut-over migration all users suddenly need to be informed about the new address to use.

Office 365 uses many different host names, for example portal.microsoftonline.com and login.microsoftonline.com, both taking you to the portal page where users can access all services or download desktop software. But you still have to use the Outlook link in the portal page to access your mail. How can we make this simpler for the end-users?

If your tenant is set up with ADFS you want to use the address outlook.com/yourdomain.com. This automatically logs you on directly to the webmail using your domain credentials, allowing single sign-on to Office 365. Unfortunately with a custom domain this can only be achieved using a local web server that handles the redirect to the correct web page.

So, what to do then? I don’t want to set up a web server just to handle a redirect. Luckily there is an easy workaround to this: Use DNS to create a CNAME (for example mail.mydomain.com) that point to mail.office365.com. This presents a login screen for the users, and they will then be logged on directly to the webmail page.

The CNAME can be created using PowerShell cmdlets for DNS, which was introduced in Windows Server 2012:

Add-DnsServerResourceRecordCName -HostNameAlias mail.office365.com -Name mail -ZoneName mydomain.com

If you are using ADFS the users must check the “Keep me signed in” checkbox to handle single sign-on in the future.

/ Andreas

Quick Tip: Self signed certificates made easy with PowerShell!

Most solutions today require certificates in some way, which means we need them even when setting up a lab/test environment.
If you for some reason don’t have a PKI/CA infrastructure in your lab environment you will most likely end up with a self signed certificates for web sites or other parts of your environment.

Since Windows 8/8.1 or Server 2012/2012 R2 there is a really nice PowerShell cmdlet that does that for us, without no hassle.
It can even handle multiple SAN’s.
It’s just to use the New-SelfSignedCertificate cmdlet from an elevated PowerShell window.

Example 1: Create and export one certificate with the name test.365lab.net:

New-SelfSignedCertificate -DnsName test.365lab.net -CertStoreLocation cert:\LocalMachine\My
#Export certificate to c:\test_365lab_net.pfx with the password 'Password'. (the thumbprint is found in the output from the New-SelfsignedCertificate command.)
Export-PfxCertificate -Cert cert:\LocalMachine\My\5D46460D29FE8E0C3F644D8ABA3C707AA83AFC79 -FilePath c:\test_365lab_net.pfx -Password (ConvertTo-SecureString -String "Password" -Force -AsPlainText)

2014-01-04 15-57-46

Example 2: Create self signed SAN certificate with the names test.365lab.net,sts.365lab.net and 365lab.net:

New-SelfSignedCertificate -DnsName test.365lab.net,sts.365lab.net,365lab.net -CertStoreLocation cert:\LocalMachine\My

2014-01-04 16-06-34

To check out your newly create certificates in the GUI, fire up the Computer Certificates Store mmc, which from Windows 8 / Server 2012 and above can be started with ‘certlm.msc‘ (OH YES!).
2014-01-04 16-10-55

Note that I generally never recommend doing self signed certificates in production environments, they are only for testing purposes!

/Johan

Mail flow rules for alias email addresses in Exchange Online

Stumbled upon an ‘issue/feature’ with mail flow rules (transport rules) that I’ve encountered before a couple of days ago and thought it was a good idea sharing.
It’s always good to get a reminder of things from time to time, even if it might be a bit obvious 🙂
The ‘issue’ do of course apply to Exchange On premise environments as well.

Case:
The user Kalle Kula have three email addresses as following:
SMTP:kalle.kula@corp.365lab.net (primary)
smtp:kalle.kula@spam.365lab.net
smtp:kalle.kula@365lab.onmicrosoft.com

If an email is sent to the address kalle.kula@spam.365lab.net (one of the alias email address for the user), we want to append a disclamer that states “This email was sent to the domain spam.365lab.net”

How to do it:
1. In Exchange Admin Center, under mail flow -> rules, create a new dislaimer rule.
2014-01-02 13-18-37
2. To be able to do more granular selection and actions, click “More Options” in the bottom left corner.

2014-01-02 13-22-46
3. Then create your rule with the following options.

The logical thing here would have been to apply the rule if the recipient address matches my particular address, but that does only work for primary email addresses. So therefore we need to apply it on the header “To” and match the text pattern kalle.kula@spam.365lab.net (the alias address)

2014-01-02 13-36-072014-01-02 13-37-172014-01-02 13-39-00  

Add your disclaimer and set fall back action.
2014-01-02 13-42-27
The only thing you need to do now for the policy to be applied on future emails is to choose mode: Enforce, save it and you’re done!

If you want to check whether a rule has been used or not, you can use EAC as well.
2014-01-02 14-07-11

PowerShell version:
To do the same as above with PowerShell, you can use the following PowerShell lines:

 
#Connect to Exchange Online with PowerShell
$cred = Get-Credential
$O365 = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential $cred -Authentication Basic -AllowRedirection 
$importcmd = Import-PSSession $O365 
#Create the Mail Flow rule (transport rule)
New-TransportRule -Name "Disclaimer - spam.365lab.net" `
                   -HeaderMatchesPatterns {kalle.kula@spam.365lab.net} `
                   -HeaderMatchesMessageHeader To `
                   -ApplyHtmlDisclaimerText "This email was sent to the domain spam.365lab.net" `
                   -ApplyHtmlDisclaimerLocation Append `
                   -ApplyHtmlDisclaimerFallbackAction Wrap `
                   -Mode Enforce 

Further documentation on mail flow rules (transport rules) you find on http://technet.microsoft.com/en-us/library/dd351127(v=exchg.150).aspx.

/Johan