Friday, October 7, 2016

Python: working with Microsoft Active Directory using LDAP3 module

I'm giving a talk on programming against Microsoft Active Directory for my colleagues on Campus. Considering that most of them don't code in .NET, I decided it best to show them how to perform object queries and AD group membership tasks in a more common language. So I chose Python and the LDAP3 module.

It was fun figuring out the AD search filter format for objectGuids in a regular LDAP search and how to handle pulling membership for AD groups with over 1,500 members. The .NET way spoils me for sure. 

Here is a link to the demo code repo:

Friday, August 19, 2016

C#: Constant Contact List Sync Examples

My unit wanted to automate syncing of Constant Contact Lists. So to help other automation peeps using .NET I created a quick C# console application that shows you how to get all the contact lists for account, empty a specific list, and add contacts to a specific list by just providing a list of email addresses.

Wednesday, October 14, 2015

PowerShell: find recently modified AD groups

While working on the sync application for AD groups to Box groups, a need come up to find AD groups that were recently modified in the last 30 minutes. By simply using a filter on the "whenChanged" property, I was able to quickly find these groups and add them to the next sync run.

Posted a quick example to github

Monday, June 15, 2015

Beginning PowerShell Lab

A co-worker and I are giving a lab session called "Beginning PowerShell for Windows Systems Analysis" tomorrow at the UC Davis IT Security Symposium.

Below is a link to the GitHub repo for the lab PowerShell scripts. The lab systems are Win7 so I had to go back to the PowerShell 2.0 way of doing things. 

Since this course is a beginner session, I made most of examples one-liners. From doing the research for the lab, I was able to reduce my "Installed Applications" 40 line script down to 4 lines. Just have to appreciate the PowerShell Pipeline :)

Sunday, March 2, 2014

PowerShell: AD Pull DirectoryEntry for AD Group by Object Guid

There have been times in my AD scripting career that I've to deal with other AD admins moving AD groups and thus changing the distinguished name value that I had hard coded in a script. Well recently, I figured out how to pull an AD group via the object Guid for the AD group. So now when someone gets bit by the renaming or rearranging bug, I won't have to update my scripts. 

I posted the PowerShell script code in my PowerShellSumo repo. 

Friday, March 8, 2013

PowerShell: Find Old Accounts and Passwords

Yesterday, I got tasked with helping find all users accounts in an Active Directory domain that are older than four years and haven't changed their password or have passwords older than four years. The domain in question had only starting using password complexity four years ago and wanted an audit of which accounts might be non-compliant. I spent some time figuring out the proper syntax to the search filter but at least I know it now. Below is the script. Enjoy.

Script Name: Find_Old_Timers.ps1
Version: 1.0
Author: Dean Bunn
Last Edited: 03/08/2013
Description: Finds Old AD Accounts (in specific OUs) Older than Four Years Old
             with Passwords that have Never been Changed
             or are Older than Four Years Old

#Create DateTime Object for Four Years Ago
$dt4YrsAgo = (Get-Date).AddYears(-4);

#Var for Converted File Time Used for Password Last Set Filter 
$ftPLS = $dt4YrsAgo.ToFileTime().ToString();

#Constructing When Created Filter(Should Look Like 20130302000000.0Z)
$wcYear = $dt4YrsAgo.Year.ToString();
$wcMonth = "{0:D2}" -f $dt4YrsAgo.Month;
$wcDay = "{0:D2}" -f $dt4YrsAgo.Day;
$sWC = $wcYear + $wcMonth + $wcDay + "000000.0Z";

#Reporting Array
$report = @();

#Var for Showing Progress
$x = 0;

#Array of OUs to Check Against
$arrOUs = @("LDAP://OU=External,DC=MyCollege,DC=edu",

foreach($ouADsPath in $arrOUs)
    #Directory Entry for OU to Search
    $deOU = [adsi]$ouADsPath;
    #Search OU for All User Accounts Meeting the Search Criteria
    $dsSearch = New-Object DirectoryServices.DirectorySearcher($deOU);
    $dsSearch.filter = "(&(objectClass=user)(sAMAccountName=*)(whenCreated<=$sWC)(|(pwdlastset<=$ftPLS)(pwdlastset=0)(pwdlastset=9223372036854775807)))";
    $dsSearch.PageSize = 900;
    $dsSearch.SearchScope = "SubTree";
    $srResults = $dsSearch.Findall();
    #Loop Through All Search Results
    foreach($srResult in $srResults)
        Write-Output $x.ToString();
        #Retrieve DirectoryEntry for the User Account
        $deADUser = $srResult.GetDirectoryEntry();
        #Null Check on the DirectoryEntry Object
            #Create Custom Object for Reporting
            $cstPCUser = New-Object PSObject;
            $cstPCUser | Add-Member -MemberType NoteProperty -Name "UserID" -Value "";
            $cstPCUser | Add-Member -MemberType NoteProperty -Name "UPN" -Value "";
            $cstPCUser | Add-Member -MemberType NoteProperty -Name "CN" -Value "";
            $cstPCUser | Add-Member -MemberType NoteProperty -Name "UAC" -Value ""
            $cstPCUser | Add-Member -MemberType NoteProperty -Name "PasswordChanged" -Value "";
         $cstPCUser | Add-Member -MemberType NoteProperty -Name "LastLoginTimeStamp" -Value "";
            #Pull Basic Account Information
            $cstPCUser.UserID = $deADUser.sAMAccountName.ToString().ToLower();
            $cstPCUser.UPN = $deADUser.userPrincipalName.ToString().ToLower();
            $cstPCUser.CN = $;
            #Check Last Password Change
            if($srResult.Properties["pwdlastset"][0].ToString() -ne "9223372036854775807" -and $srResult.Properties["pwdlastset"][0].ToString() -ne "0")
                $cstPCUser.PasswordChanged = ([System.DateTime]::FromFileTime($["pwdlastset"][0])).ToString();
                $cstPCUser.PasswordChanged = "Not Set";
            #Account Status Check
                   512 {$cstPCUser.UAC = "Enabled"}
                   514 {$cstPCUser.UAC = "Disabled"}
                   520 {$cstPCUser.UAC = "Enabled"}
                   522 {$cstPCUser.UAC = "Disabled"}
                   544 {$cstPCUser.UAC = "Enabled"}
                   546 {$cstPCUser.UAC = "Disabled"}
                   66048 {$cstPCUser.UAC = "Enabled"}
                   66050 {$cstPCUser.UAC = "Disabled"}
                   66080 {$cstPCUser.UAC = "Enabled"}
                   66082 {$cstPCUser.UAC = "Disabled"}
                   8388608 {$cstPCUser.UAC = "Password Expired"}
                   default {$cstPCUser.UAC = "unknown"}
               #Convert Last Logon Timestamp (If Exists)
                $cstPCUser.LastLoginTimeStamp = ([System.DateTime]::FromFileTime($srResult.Properties["lastlogontimestamp"][0])).ToShortDateString();
            #Add Custom Object to Report Collection
            $report += $cstPCUser;
        }#End of $deADUser Null Check
    }#End of $srResults Foreach
}#End of $arrOUs Foreach

#Get Current Short Date
$rptFileDate = Get-Date -Format d;
#Var for CSV File Name
$fileName = "Old_Timers_Password_Change_Report_" + $rptFileDate.ToString().Replace("/","-") + ".csv";
#Export CSV Report
$report | Sort-Object UserID | Export-CSV $fileName -NoTypeInformation ;

###### End of Script ###################

Thursday, March 7, 2013

ADMT: "Unable to establish a session with the password export server"

About a month ago, I ran into an issue when using Microsoft's Active Directory Migration Tool (ADMT) between a Windows 2003 and a Windows 2008 R2 domain when using the Password Export Server. The error I received was:

"Unable to establish a session with the password export server. Either the currently logged on user does not have sufficient permissions to call the Password Export Server or the account that the Password Export Server Service is running under does not have sufficient permissions on the target domain controller. Verify that the logged on user is a member of the Administrators group in the source domain and that the Password Export Server Service account can change passwords of user accounts in the target domain."

Well after a few hours of working with Microsoft Support, we figured out the issue was due to PAC Validation and that we needed to make registry change on the source DC running the Password Export Server. 

Here is the registry change: