Wednesday, November 17, 2010

Another bad Win7 Mobile decision–Developer Tools

I have an idea for an application for my new phone. Nothing complicated but I want to try and build it and see if I can get it to work or not (more likely). I have a Windows 7 Phone so I’ll just download the tools from Microsoft and see what I can do.

I download the file and run it expecting that there will be an installation and I’ll have some nifty tools installed that I can try to give my dreams life with. Um…  Nope – Epic fail here…


Both my laptop and my desktop are running Windows Server 2008 R2 – Why? I need to run virtual machines for development and/or a lab environment. This is a great idea since I can’t run a x64 guest on Windows 7 and I got the idea from MS personnel while at TechEd. This isn't’ a foreign idea to anyone… Why would you not allow me to install developer tools here when I’m sure, there are people in Microsoft doing the exact same thing!!?!

The fix is simple enough, however why does there need to be a fix for something so simple? Seriously?

Here is the fix -

Tuesday, November 16, 2010

Windows Mobile 7 doesn’t connect to hidden wireless networks

I just purchased my Win7 Mobile phone. Honestly I’ve been waiting for this device to come out for over a year and I was pretty stoked that they are finally released. 

One of the cool things about modern mobile devices is that they can use other available networks besides just eating up your 3G data plan. This isn’t an issue for me at home, on campus or in most coffee shops since they have the wireless open or I have a the shared key. This is not the case at my place of employment, since it is our policy to keep the wireless networks from broadcasting the SSID. There are a few reasons for this and security really isn’t on that list. We are right across the street from a few retail stores, a hospital and some of our locations are in shopping malls – We see enough SSID broadcasts.

Microsoft does have a blog post about why NOT to hide your, however their reasoning is based on security and ignoring any other reasons for hiding a SSID.

This is so far the ONLY mobile device I’ve heard of or seen that cannot connect to a hidden network. There are enterprise customers  that I’m sure have a standard to hide the SSID besides us for very valid reasons besides a security fa├žade. This isn’t just the inability to connect, you can’t even type in a SSID and key so I can’t even pre-stage or configure a network if I needed to say… Send a device to one of my executives at a conference or remote meeting.

Bad choice and pulling the security card is another bad call…

Thursday, October 7, 2010

Remove Old Computer Accounts from the Domain

We have a manual process of retiring systems from the domain and like any manual process without checks and balances there are times is does not get completed properly. One of these “checklist items” is to remove the computer from the domain. If this fails to happen we will have a computer account somewhere in Active Directory forever or at least someone notices. My solution to keep Active Directory “reasonably” clean is of course – PowerShell!
We have computers that could be turned off sitting in the closet or some place else for more than the 30 day password reset period. This we have found in our environment could actually exceed 90 days regularly enough to notice. I don’t want these computers deleted from the domain, however I would like them to call the helpdesk to make sure it’s on and give it the once over for anti-virus, patches, etc.
My script is simple three lines:
  1. Get the date 90 days ago
  2. Disable the computer accounts that haven’t had their password reset in at least 90 days
  3. Delete the computer accounts that haven’t been modified in at least 90 days
I do it in this order and with the same date since disabling the computer account is a change that is registered. This way I will only be deleting the computer accounts that have not accessed the domain in at least 150 days and possibly up to 180 days.
$date = [DateTime]::Today.AddDays(-90)
Get-ADComputer -Filter 'PasswordLastSet -lt $date' -Properties PasswordLastSet | sort Name | Set-ADComputer -Enabled $false 
Get-ADComputer -Filter 'Modified -lt $date' | Remove-ADObject -confirm:$false -Recursive

Note: I have the sort on line two simply for troubleshooting and if I ever need to look at the output I just replace the last pipe with a Write-Host or Export-Csv.

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