Friday, September 24, 2010

Consultant Names and Email Address Report

Yesterday I posted a script showing how to change consultant passwords and email the new password to them. This is great until someone asks for the list of people that it will affect and you try to verify the email addresses. The solution is another very simple PowerShell script to just kick out that data which I just put into an email for whomever is asking.
Import-Module ActiveDirectory

$Consultants = Get-ADGroupMember "Role_Consultants" -Recursive

$Allusers = New-Object System.Collections.ArrayList

foreach ($user in $Consultants) {
    $tempUser = Get-ADUser -Identity $user -Properties emailaddress

$Allusers | Select-Object Name, EmailAddress

Thursday, September 23, 2010

Resetting Consultant Passwords

We have an issues with certain consultants that only logon once every few months and therefore their password has expired. They can’t change it because they never logged on during the grace period so now the account is locked out. Normally they could call the helpdesk during regular working hours and have the password reset and it wouldn’t be a big deal, however some of these accounts are for offshore remote monitoring services that only logon when there is a problem. When there is a problem they need to logon and they need to logon NOW!
I solved this issue for our organization by creating a script that will query AD and return the users from the ‘Consultants’ group and if the password is about to expire (7 days) it will reset it and email it to the address stored in the ‘emailaddress’ attribute of AD. I know the idea of emailing passwords plaintext over the internet is a horrid security practice, however it was deemed acceptable by people of a higher rank than I…
Import-Module ActiveDirectory

Function CreatePassword {
    param ( $Characters )
    [System.Security.SecureString]$password = New-Object System.Security.SecureString
    #Set up random number generator
    $rand = New-Object System.Random
    #Generate a new 10 character password
    [int]$int = $Characters/4
    1..$int | ForEach { $NewPassword = $NewPassword + [char]$,90) }
    ($int+1)..($int*2) | ForEach { $NewPassword = $NewPassword + [char]$,64) }
    (($int*2)+1)..($int*3) | ForEach { $NewPassword = $NewPassword + [char]$,122) }
    (($int*3)+1)..$Characters | ForEach { $NewPassword = $NewPassword + [char]$,57) }
    $secureString = new-object Security.SecureString
    $NewPassword.ToCharArray() | % { $secureString.AppendChar($_) }

function SendEmail {

    $SMTPServer = ""
    # Create from/to addresses  
    $FromObject = New-Object System.Net.Mail.MailAddress $array[0]  
    $ToObject =   New-Object System.Net.Mail.MailAddress $array[1]
    # Create Message  
    $message = new-object  System.Net.Mail.MailMessage $FromObject, $ToObject  
    $message.Subject = $array[2]
    $message.Body = $array[3]
    $client = new-object $SMTPServer  
    # Send the message  
#    "Sending an e-mail message to {0} by using SMTP host {1} port {2}." -f $to.ToString(), $client.Host, $client.Port  
    try {  
#          "Message to: {0}, from: {1} has beens successfully sent" -f $from, $to  
    catch {  
          "Exception caught in CreateTestMessage: {0}" -f $Error.ToString()  

function ResetPassword() {
    # function taken from MSDN and modified for my needs
    Param ([Parameter(Mandatory=$true,  Position=0,  ValueFromPipeline=$true, HelpMessage="Identity of the Account")]
    [Object] $accountIdentity)
        $accountObj = Get-ADUser $accountIdentity -properties PasswordExpired, PasswordNeverExpires, PasswordLastSet
        if ($accountObj.PasswordExpired) {
            #echo ("Password of account: " + $accountObj.Name + " already expired!")
        } else { 
            if ($accountObj.PasswordNeverExpires) {
                #echo ("Password of account: " + $accountObj.Name + " is set to never expires!")
            } else {
                $passwordSetDate = $accountObj.PasswordLastSet
                if ($passwordSetDate -eq $null) {
                    #echo ("Password of account: " + $accountObj.Name + " has never been set!")
                }  else {
                    $maxPasswordAgeTimeSpan = $null
                    $dfl = (get-addomain).DomainMode
                    if ($dfl -ge 3) { 
                        ## Greater than Windows2008 domain functional level
                        $accountFGPP = Get-ADUserResultantPasswordPolicy $accountObj
                        if ($accountFGPP -ne $null) {
                            $maxPasswordAgeTimeSpan = $accountFGPP.MaxPasswordAge
                        } else {
                            $maxPasswordAgeTimeSpan = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
                    } else {
                        $maxPasswordAgeTimeSpan = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
                    if ($maxPasswordAgeTimeSpan -eq $null -or $maxPasswordAgeTimeSpan.TotalMilliseconds -eq 0) {
                        #echo ("MaxPasswordAge is not set for the domain or is set to zero!")
                    } else {
                        #echo ("Password of account: " + $accountObj.Name + " expires on: " + ($passwordSetDate + $maxPasswordAgeTimeSpan))
                        $ResetDate = Get-Date
                        if (($passwordSetDate + $maxPasswordAgeTimeSpan) -lt $ResetDate.AddDays(7)){
                        } else {

if ($Consultants.Length -gt 0) {
    Remove-Variable Consultants
$Consultants = Get-ADGroupMember "Role_Consultants" -Recursive

if ($NewPassword.Length -gt 0) {
    Remove-Variable NewPassword

foreach ($user in $Consultants) {
    if (ResetPassword($user)) {
        $UserAccount = Get-ADUser -Identity $user -Properties EmailAddress
        $NewPassword = CreatePassword(12)
        $PlainPassword = $NewPassword.PlainPassword
        Set-ADAccountPassword -Identity $user -NewPassword $NewPassword.SecurePassword
        SendEmail("",$UserAccount.EmailAddress,"Domain User Account Update","Your network password has been changed to $PlainPassword")
        Write-Host $user.Name $NewPassword.PlainPassword
        Remove-Variable NewPassword
    } else {
        Write-Host "$User.Name password does not need to be reset"

Wednesday, September 8, 2010

PowerShell Script - Move Computers to the Correct OU

I'm not sure if this is a problem for many other organizations, however we have had a problem with "Domain Admin Bloat". Simply put we have over the course of many projects and many years ended up with many people in the Domain Admin group that don't have any need to be in there. One of these reasons was that we had problems with delegation of privileges (probably because we had too many Domain Admins).

I got this idea from Dan Holme's book Windows Administration Resource Kit: Productivity Solutions for IT Professionals. We delegated the proper permissions to the single OU, moved all the computers to the appropriate OU and now execute this script on a schedule. This has completely fixed our computer account permission problems.
A little setup our systems are named [W,V,L,M]##### so for this to work you will need to do the same or modify the script. Also in AD our computers go into the appropriate OU under the Workstations OU and this is where the delegation starts.

This should give some ideas on how to move or even some additional ideas on how to help manage AD with PowerShell.
Import-Module ActiveDirectory

$Domain = [ADSI]""
[string]$DomainName = $Domain.DistinguishedName

$NewComputers = Get-ADComputer -filter {Name -like "M*" -or Name -like "V*" -or Name -like "W*" -or Name -like "L*"} -SearchBase "OU=NewComputers,$DomainName"

ForEach ($Computer in $NewComputers){
    $Number = [string]$Computer.Name.Substring(1)
    $SubNum = $Number.Substring(0,1)
    If ($SubNum -eq (0) -or $SubNum -eq (1) -or $SubNum -eq (2) -or $SubNum -eq (3) -or $SubNum -eq (4) -or $SubNum -eq (5) `
        -or $SubNum -eq (6) -or $SubNum -eq (7) -or $SubNum -eq (8) -or $SubNum -eq (9)){
        [int]$Number = $Number
    $MemberType = $Number.GetType()
    If ($MemberType.Name -eq "Int32") {
        $Prefix = [string]$Computer.Name.Substring(0,1)
        write-host $Computer.Name, $Number, $Prefix
        Switch ($Prefix)
                M {Move-ADObject $Computer -TargetPath "OU=MobileDevices,OU=Workstations,$DomainName"}
                L {Move-ADObject $Computer -TargetPath "OU=Laptops,OU=Workstations,$DomainName"}
                V {Move-ADObject $Computer -TargetPath "OU=VirtualDesktops,OU=Workstations,$DomainName"}
                W {Move-ADObject $Computer -TargetPath "OU=Desktops,OU=Workstations,$DomainName"}
    Remove-Variable Number

Friday, September 3, 2010

Remove Disabled Accounts from AD Groups

I follow various blogs and one of them is Dmitry's PowerShell blog. He recently posted an article on how to remove disabled accounts from AD groups using the Quest cmdlets. While his solution is easy, I was wondering if I could do it in plain AD PowerShell. The solution was reasonably simple and here it is…
Import-Module ActiveDirectory
$GroupNameforeach ($Member in $GroupMembers) { 
    $User = Get-ADUser $Member -Properties Enabled     
     if ($User.Enabled -eq $False) {  
        Remove-ADGroupMember $GroupName $User -Confirm:$false

Quest has some great tools but I don't like installing things on my servers and that's just where I tend to run my AD cleanup jobs from