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 :)
https://github.com/dbunn/pslab
Showing posts with label PowerShell. Show all posts
Showing posts with label PowerShell. Show all posts
Monday, June 15, 2015
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", "LDAP://OU=Students,DC=MyCollege,DC=edu", "LDAP://OU=Staff,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) { $x++; Write-Output $x.ToString(); #Retrieve DirectoryEntry for the User Account $deADUser = $srResult.GetDirectoryEntry(); #Null Check on the DirectoryEntry Object if($deADUser) { #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 = $deADUser.cn.ToString(); #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($srResult.properties["pwdlastset"][0])).ToString(); } else { $cstPCUser.PasswordChanged = "Not Set"; } #Account Status Check if($deADUser.userAccountControl) { switch([int]($deADUser.userAccountControl.ToString())) { 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) if($srResult.Properties["lastlogontimestamp"]) { $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 ###################
Tuesday, March 5, 2013
PowerShell: After ADMT Password Migration Fix
If you used Microsoft's Active Directory Migration Tool (ADMT) for any major migrations, then you know that migrated AD accounts are always set for user must change password upon next login. Well in the target domain we didn't want this setting set due to were migrating the password from the old domain and had complexity filter set so ADMT logs would tell us which accounts didn't meet the password criteria.
Below is the PowerShell code that met our after ADMT user migration fix needs. I used a switch statement on UAC so that account enabled status remained the same. Be advised that this code will set the user account so that user cannot change their password.
Below is the PowerShell code that met our after ADMT user migration fix needs. I used a switch statement on UAC so that account enabled status remained the same. Be advised that this code will set the user account so that user cannot change their password.
<# Script Name: AD_After_ADMT_User_Migration_Fix.ps1 Version: 1.0 Author: Dean Bunn Last Edited: 03/03/2013 Description: Corrects ADMT Account Changes Regarding Password Settings #> #Create NTAccounts for SELF and Everyone $ntaSelf = New-Object System.Security.Principal.NTAccount("NT AUTHORITY","SELF"); $ntaEveryone = New-Object System.Security.Principal.NTAccount("Everyone"); #AD Security Types $actDeny = [System.Security.AccessControl.AccessControlType]::Deny; $adrER = [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight; #GUID for Change Password AD Property $gapCP = [Guid]"ab721a53-1e2f-11d0-9819-00aa0040529b"; #Create AD Deny Rules $adrlDSPC = New-Object System.DirectoryServices.ActiveDirectoryAccessRule ($ntaSelf,$adrER,$actDeny,$gapCP); $adrlDEPC = New-Object System.DirectoryServices.ActiveDirectoryAccessRule ($ntaEveryone,$adrER,$actDeny,$gapCP); #Array of OUs to Run Against $adOUs = @("OU=People,DC=myCollege,DC=edu", "OU=External,DC=myCollege,DC=edu"); #Var for Counting Accounts Checked $nActCkd = 0; foreach($adOU in $adOUs) { #Var for OU ADsPath [string]$ouADsPath = "LDAP://" + $adOU; $deADOU = [ADSI]$ouADsPath; $dsSearch = New-Object DirectoryServices.DirectorySearcher($deADOU); $dsSearch.filter = "(&(objectClass=user)(sAMAccountName=*)(!objectClass=computer)(!objectClass=contact))"; $dsSearch.PageSize = 900; $dsSearch.SearchScope = "SubTree"; $srResults = $dsSearch.Findall(); #Loop Through Search Results foreach($srResult in $srResults) { #Null Check on Search Result if($srResult) { #Increment Counting and Display Current Number $nActCkd++ Write-Output $nActCkd.ToString(); #Pull Directory Entry $deADUser = $srResult.GetDirectoryEntry(); #Null Check on Directory Entry for User if($deADUser) { $deADUser.psbase.ObjectSecurity.AddAccessRule($adrlDSPC); $deADUser.psbase.ObjectSecurity.AddAccessRule($adrlDEPC); $deADUser.psbase.commitchanges(); #Set Account to Not Expire (If Necessary) #$deADUser.accountExpires = 0; #$deADUser.setInfo(); #Var for Account Status [int]$uUAC = [int]::Parse($deADUser.userAccountControl.ToString()); #Check for UAC Setting. switch($uUAC) { 512 { #Set Password Never Expires $deADUser.userAccountControl = 66048; $deADUser.setInfo(); } 514 { #Set Password Never Expires $deADUser.userAccountControl = 66050; $deADUser.setInfo(); } 544 { #Set Password Never Expires $deADUser.userAccountControl = 66048; $deADUser.setInfo(); } 546 { #Set Password Never Expires $deADUser.userAccountControl = 66050; $deADUser.setInfo(); } 66080 { #Set Password Never Expires $deADUser.userAccountControl = 66048; $deADUser.setInfo(); } 66082 { #Set Password Never Expires $deADUser.userAccountControl = 66050; $deADUser.setInfo(); } }#End of userAccountControl Switch }#End of Null Check on $deADUser }#End of Null Check on $srResult }#End of $srResults Foreach }#End of $adOUs Foreach
Labels:
ADMT,
Password Migration,
PowerShell,
UAC,
UserAccountControl
Thursday, January 10, 2013
PowerShell: AD Export OU Structure
Below is the PowerShell code I used to export the OU structure between two domains. We only needed a few of the source base OUs exported and not the whole domain. The source OUs had hundreds of various nested level OUs underneath them. The base target OU needs to be created first and DN values of the OUs are case sensitive. The script can be run numerous time and will only create OUs in the target OU for OUs that don't exist.
#Create a HashTable to Hold OUs $htOU = @{}; #Add Source and Target OU Pairs $htOU["OU=PEOPLE,DC=OldDept,DC=myCollege,DC=edu"] = "OU=PEOPLE,OU=NewDept,DC=childDomain,DC=myCollege,DC=edu"; $htOU["OU=RESEARCH,DC=OldDept,DC=myCollege,DC=edu"] = "OU=RESEARCH,OU=NewDept,DC=childDomain,DC=myCollege,DC=edu"; $htOU["OU=EQUIPMENT,DC=OldDept,DC=myCollege,DC=edu"] = "OU=EQUIPMENT,OU=NewDept,DC=childDomain,DC=myCollege,DC=edu"; #Loop Through the OU HashTable foreach($key in $htOU.keys) { #Vars for OU DNs [string]$srcOUDN = $key.ToString(); [string]$tgtOUDN = $htOU[$key].ToString().Trim(); #Var for DN Path to Remove When Creating New Target OUs [string]$rmvPath = "," + $tgtOUDN; #Array To Hold Source OU DNs $arrSrcOUs = @(); #Vars for ADsPath of Source and Target OUs [string]$srcADsPath = "LDAP://" + $srcOUDN; [string]$tgtADsPath = "LDAP://" + $tgtOUDN; #Retrieve Directory Entries for Source and Target OU $deSourceOU = [ADSI]$srcADsPath; $deTargetOU = [ADSI]$tgtADsPath; #Search Source OU for All OUs (Excluding Source OU) $dsSearch = New-Object DirectoryServices.DirectorySearcher($deSourceOU); $dsSearch.filter = "(&(objectClass=organizationalUnit)(!(distinguishedName=$srcOUDN)))"; $dsSearch.PageSize = 900; $dsSearch.SearchScope = "SubTree"; $srResults = $dsSearch.Findall(); #Loop Through All Source OU Search Results foreach($srResult in $srResults) { #Pull Directory Entry for Search Result and Store DN in Source OU Array $deOU = $srResult.GetDirectoryEntry(); $arrSrcOUs += $deOU.distinguishedName.ToString(); } #Loop Through Source OUs DN Values foreach($srcOU in $arrSrcOUs) { #Var for Target OU DN Path (Replacing Source OU Path with Target OU Path) [string]$uTgtOUDN = $srcOU.ToString().Replace($srcOUDN,$tgtOUDN); #Var for Target OU ADsPath (Used for Checking Existance) [string]$uTgtOUADsPath = "LDAP://" + $uTgtOUDN; #Check to See If Target OU Exists. If Not Create It. if(![ADSI]::Exists($uTgtOUADsPath)) { #Add New OU and Save $newOU = $deTargetOU.create("organizationalUnit",$uTgtOUDN.Replace($rmvPath,"")); $newOU.setInfo(); } }#End of $arrSrcOUs Foreach }#End of $htOU.keys Foreach ######### End of Script #############
Friday, November 23, 2012
PowerShell: Exiting Admin Account(s) Check
To prevent issues with when an admin leaves an organization and we disable or delete his or her AD account(s) which would then causes a service, scheduled task, or process running under those credentials on one of servers to fail. I created a PowerShell script to remotely check a list of servers for any service, process, or scheduled task using those AD account(s). The script has to be ran from an elevated PowerShell session.
Reference:
http://myitforum.com/cs2/blogs/yli628/archive/2008/07/28/powershell-script-to-retrieve-scheduled-tasks-on-a-remote-machine-task-scheduler-api.aspx
############################################################################## # Script Name: Exiting_Admin_Check.ps1 # Version: 1.0 # Author: Dean Bunn # Last Edited: 11/22/2012 # Description: Check Remote Systems for Any Processes, Scheduled Tasks, # Servics Running with Credentials of an Exiting Admin ############################################################################## #Error Handling Setting $ErrorActionPreference = "SilentlyContinue"; #Array of Domain\UserIDs to Check Against [array]$userIDs = @("ad\adminAccount1","ad\adminAccount2","ad\adminAccount3"); #Vars for Email Notice [string]$smtpServer = "smtpServer.mycollege.edu"; [string]$fromAddress = "scriptAccount@mycollege.edu"; [string]$toAddress = "adAdmins@mycollege.edu"; #Array of Server Names $servers = @( "SERVER1.MYCOLLEGE.EDU", "SERVER2.MYCOLLEGE.EDU", "SERVER3.MYCOLLEGE.EDU", "SERVER4.MYCOLLEGE.EDU", "SERVER5.MYCOLLEGE.EDU", "SERVER6.MYCOLLEGE.EDU" ); #Function for Checking Domain\UserIDs function Check_Domain_Account([string]$dmnID,[array]$userIDs) { foreach($userID in $userIDs) { #Check to See If Domain User IDs Match if([string]::Equals($userID.ToString().ToLower(),$dmnID.ToString().ToLower())) { return $true; } } return $false; }#End of Check_Domain_Account Function #Array to Hold Custom Reporting Objects $summary = @(); foreach($server in $servers) { #Vars for Reporting [string]$pingStatus = ""; [string]$serviceStatus = ""; [string]$processStatus = ""; [string]$taskStatus = ""; #Display Current Server Working Against Write-Output ("Working on " + $server.ToString().ToLower()); #Ping Computer Before Attempting Remote WMI if(test-connection -computername $server -quiet) { $pingStatus = "passed"; #Pull Services on Remote System $uServices = Get-WMIObject win32_service -Computer $server; #Null Check on Returned Services Collection if($uServices) { foreach($service in $uServices) { #Check the Account the Service is Running Under if(Check_Domain_Account $service.StartName.ToString() $userIDs) { $serviceStatus = "found"; break; } } } else { $serviceStatus = "RPC failed"; }#End of $uServices Null Check #Pull Processes from Remote System $uProcesses = Get-WmiObject win32_Process -ComputerName $server #Null Check on Return Processes Collection if($uProcesses) { foreach($process in $uProcesses) { #Pull Domain and User Info from Process $prcOwnerDomain = $process.GetOwner().Domain; $prcOwnerUserID = $process.GetOwner().User; #Verify that Both Domain and User ID Information Exists if($prcOwnerDomain -ne $null -and $prcOwnerUserID -ne $null) { #Var for Domain\UserID Format [string]$tmpDmnUserID = $prcOwnerDomain.ToString() + "\" + $prcOwnerUserID.ToString(); #Check the Account the Process is Running As if(Check_Domain_Account $tmpDmnUserID $userIDs) { $processStatus = "found"; break; } }#End of Null Check on Domain and UserID }#End of Foreach Process } else { $processStatus = "RPC failed"; }#End of $uProcesses Null Check #Scheduled Tasks try { #Connect to Schedule Service on Remote System and Pull Tasks $schedService = New-Object -ComObject Schedule.Service; $schedService.Connect($server); $rootTasks = $schedService.GetFolder("").GetTasks(""); foreach ($task in $rootTasks) { #Create XML Object From Task XML Settings [XML]$taskXML = $task.Xml; #Check to Account the Task will Run As if(Check_Domain_Account $taskXML.Task.Principals.Principal.UserId.ToString() $userIDs) { $taskStatus = "found"; break; } }#End of Foreach Task } catch { $taskStatus = "COM failed"; }#End of Tasks Try\Catch } else { $pingStatus = "failed"; }#End of Test Connection #Create Custom Reporting Object and Assign Values $uEntry = New-Object PSObject $uEntry | Add-Member -MemberType NoteProperty -Name "Server" -Value $server.ToString().ToLower(); $uEntry | Add-Member -MemberType NoteProperty -Name "Ping_Status" -Value $pingStatus; $uEntry | Add-Member -MemberType NoteProperty -Name "Service_Status" -Value $serviceStatus; $uEntry | Add-Member -MemberType NoteProperty -Name "Process_Status" -Value $processStatus; $uEntry | Add-Member -MemberType NoteProperty -Name "Task_Status" -Value $taskStatus; #Add Reporting Object to Reporting Array $summary += $uEntry; }#End of Foreach Computer #Sort Report by Server Name $summary = $summary | Sort-Object Server; ######## Configure HTML Report ######## #Var for HTML Message Body $msgBody = "<html> <body> <h4>Exiting Admin Account(s) Report</h4> <span style=""font-size:8pt;font-family:Arial,sans-serif""> <strong>Accounts Checked:</strong> <br />"; #Add Each Account Checked to Report foreach($usrID in $userIDs) { $msgBody += $usrID.ToString().ToLower() + "<br />"; } #Format the Report HTML Table $msgBody += "</span><br /> <table border=""1"" cellpadding=""4"" cellspacing=""0"" style=""font-size:8pt;font-family:Arial,sans-serif""> <tr bgcolor=""#000099""> <td><strong><font color=""#FFFFFF"">Server</font></strong></td> <td align=""center""><strong><font color=""#FFFFFF"">Ping</font></strong></td> <td align=""center""><strong><font color=""#FFFFFF"">Services</font></strong></td> <td align=""center""><strong><font color=""#FFFFFF"">Processes</font></strong></td> <td align=""center""><strong><font color=""#FFFFFF"">Scheduled Tasks</font></strong></td> </tr> "; #Var for Table Row Count [int]$x = 1; #Loop Through Custom Object Collection foreach($srv in $summary) { #Determine Even\Odd Row if($x%2) { $msgBody += "<tr bgcolor=""#FFFFFF"">"; } else { $msgBody += "<tr bgcolor=""#E8E8E8"">"; } $x++; $msgBody += "<td>" ` + $srv.Server ` + "</td><td align=""center"">" ` + $srv.Ping_Status ` + "</td><td align=""center"">" ` + $srv.service_Status ` + "</td><td align=""center"">" ` + $srv.process_Status ` + "</td><td align=""center"">" ` + $srv.task_Status ` + "</td></tr> "; } #Close HTML Table and Message $msgBody += "</table> </body> </html>"; #Settings for Email Message $messageParameters = @{ Subject = "Exiting Admin Account(s) Report" Body = $msgBody From = $fromAddress To = $toAddress SmtpServer = $smtpServer }; #Send Report Email Message Send-MailMessage @messageParameters –BodyAsHtml; ################################################################ #Export to CSV Section (Leaving In Case Needed Later) #Get Current Short Date #$rptDate = Get-Date -Format d; #Configure Report Name #$reportName = "Exiting_Admin_Check_" ` # + $rptDate.ToString().Replace("/","-") + ".csv"; #Export Report to CSV #$summary | Export-CSV $reportName -NoTypeInformation; ################################################################
http://myitforum.com/cs2/blogs/yli628/archive/2008/07/28/powershell-script-to-retrieve-scheduled-tasks-on-a-remote-machine-task-scheduler-api.aspx
Subscribe to:
Posts (Atom)