Powershell : Move Enabled Computers to Holding OU

Introduction

This script loops through the $SourceOUs (a list of OUs from AD), identifies any enabled computers therein and moves them to the defined $TargetOU . It then emails $emailTo with a list of all affected computers and asks that the relevant IT teams are informed.

The reason for this action is to ensure that all bound and active computers fall under the scope of SCCM control which currently only affects machines within the prescribed OU structure.

This is far from ideal but it’s a temporary fix.

Input

None

Output

An email is generated where a local SMTP server is available to route the email request

Logging

Currently the logging is managed from within the script and will need to have the related variables customized to the script’s location.

$LOGTIME is s timestamp in the format “yyyy-MM-dd_hh-mm-ss”

Log call within the script:
“$LOGTIME <<TEXT TO LOG>>” | Out-File $LOG -Append -Force

The script is then contained with a try{} catch{} parameter with logging output for all exception messages and items.

I’ve also tried to log throughout each action within the script so both SUCCESS and ERRORs are logged which provides a debugging method (at least I should know where it falls over!).

Usage Examples

Call the script, no parameters defined.

Dependancies

emailer.psm1

The Script

# Name      : computerObjectManage
# Author    : Dave 
# Created   : 31/05/2019
# UPDATED   : 04/07/2019
# Version   : v2.0
# Ticket#   : XXXX
# Scope     : AD Computer Objects which are enabled and located in the AD OUs
# Input(s)  : None
# Output(s) : Log file of actions taken and an email to $emailTo if any computer objects were moved, including the account which bound them to the domain.
  
  
####################################################
# This script is designed to be located on [REDACTED] at  [REDACTED]  and run with user rights to move computer objects from the  [REDACTED] 
# It will be called nightly via a Scheduled Task defined on the server.
#
# 1. Identify all computer objects in AD which are enabled and which exist in either the OU
# 2. Parse the event logs on all Domain Controllers for Event ID 4741 and record the computername and username of the binder
# 3. Log those computer names, distinguished names, descriptions and username of the binder to a log file
# 4. Move those computer objects to the $TargetOU
# 5. Email $emailTo with a list of the computers moved, the username who bound them, the computer's distinguished names and their descriptions.
####################################################
  
# Variables:
# Files
$LOGFILEDIR = "<<INPUTE LOGFILE DIR HERE>>" # the full path of the log file for the script to output to
$LOGFILENAME =  "computerObjectManage.ps1.log" # the full name of the log file for the script to output to
$LOG = ($LOGFILEDIR + "\" + $LOGFILENAME)
$LOGTIME = Get-Date -Format "yyyy-MM-dd_hh-mm-ss"
  
# Text Strings
$SourceOUs =  [REDACTED] 
$TargetOU =  [REDACTED] 
$emailer = "<<FULL PATH OF emailer.psm1 HERE>>"
 
# eventlog querying variables
$domain = "you.domain.com"
$dcou =  [REDACTED] 
$dcs = get-adcomputer -filter * -properties name -searchbase $dcou | select name
# set the hashtable variable to record the eventlog results from the domain controllers
$domainBindEvents = @{}
  
# Email settings
$emailTo = "your@email.com"
$emailFrom = "yourscript@email.com"
  
# Email subject and the first section of the body
$subject_output =  [REDACTED] 
  
Import-Module -Name $emailer #-Verbose
  
# Prepare the logging
##################################################################################
# Check for the $LOGFILEDIR & $LOGFILENAME and create them if they don't exist
# NOTE: No logging until this happens except to STDOUT (Screen)
# Test for the folder and create if it doesn't exist:
##################################################################################
  
Try
    {
      
        if(! (Test-Path -Path $LOGFILEDIR )){
                New-Item -ItemType directory -Path $LOGFILEDIR
            }
        if(! (Test-Path $LOGFILEDIR\$LOGFILENAME  )){
            New-Item -ItemType file -Path $LOGFILEDIR\$LOGFILENAME
        }
          
    }
Catch
    {
        $ErrorMessage = $_.Exception.Message
        $FailedItem = $_.Exception.ItemName
    }
  
"$LOGTIME BEGIN STARTING SCRIPT" | Out-File $LOG -Append -Force
  
  
##################################################################################
# Main
##################################################################################
  
Try
    {
        # Using the list of $SourceOUs (defined at the start of this script) loop through each OU and add any computers' distinguished names and which are ENABLED to the $targetcomps array.
        $targetcomps = foreach ($OU in $SourceOUs) { get-adcomputer -filter {enabled -eq "True"} -properties Name, DistinguishedName, Description -SearchBase "$OU" | select DistinguishedName, Name, Description}
        # test to see if there were any computers discovered and added to the $targetcomps array by inspecting it's length.
        # If computers were discovered then continue, else log that there weren't and exit the script.
          
        if ($targetcomps.length -eq 0) {
            "$LOGTIME COMPLETED No computers were discovered in $($SourceOUs)" | Out-File $LOG -Append -Force
            exit
            #write-host "no targetcomps"
        }
        else {
            # If there were computers in the default OUs:
             
            # Check the event logs on all DC's for the event ID 4741 in the Security eventlog and
            # add any entries' computer name and account which bound it to the the hashtable $domainBindEvents
            try {
             
                foreach ($dc in $dcs){
                 
                    # Loop through each DC in the $dcou OU and for each one:
                    # Set the FQDN to $dcfqdn
                    $dcfqdn = $dc.name + $domain
                     
                    try{
                     
                        # Add all events matching 4741 from the Security event log to the $dc4741events hashtable
                        "$LOGTIME INFO querying $($dcfqdn) for 4741 events..." | Out-File $LOG -Append -Force
                        $dc4741events += get-winevent -computername $dcfqdn -FilterHashtable @{logname='security';id=4741}
                    }
                    catch
                    {
                        # if there were no events continue to the next DC
                        $ErrorMessage = $_.Exception.Message
                        $FailedItem = $_.Exception.ItemName
                        "$LOGTIME ERROR $($ErrorMessage) $($FailedItem)" | Out-File $LOG -Append -Force #LOGACTION
                        continue
                    }
                     
                    # If events were found then:
                    if ($dc4741events) {
                     
                        foreach ($event in $dc4741events){
                                         
                            # expand the event and assign the 8th item to $computername and the 4th to $userwhobound
                            $computername = $event.Properties[8]
                            $userwhobound = $event.Properties[4]
                             
                            # Update the hashtable $domainBindEvents with the new values
                            $domainBindEvents[$computername.value.SubString(0,$computername.value.Length-1)] = $userwhobound.value
                            "$LOGTIME INFO Events Found on $($computername.value.SubString(0,$computername.value.Length-1)) bound by $($userwhobound.value) " | Out-File $LOG -Append -Force
                        }
                    }
                }
 
            }
            catch
            {
                $ErrorMessage = $_.Exception.Message
                $FailedItem = $_.Exception.ItemName
                "$LOGTIME ERROR $($ErrorMessage) $($FailedItem)" | Out-File $LOG -Append -Force #LOGACTION
            }
             
            # Loop through the list of computers which were discovered in a default OU
            foreach ($comp in $targetcomps) {
             
                # Loop through the $boundcompevents.keys
                foreach ($boundcompevent in $domainBindEvents.keys){
                    #write-host "$($boundcompevent)"
                    # Compare the $comp with the key values of the $domainBindEvents to see if there's a match:
                    #write-host "DOES $($boundcompevent) EQUAL $($comp.Name.tolower())?  "
                    if ($boundcompevent.tolower() -eq $comp.Name.tolower()){
                        #write-host true
                        # If there is a match then:
                        # Assign the username of the account which bound the matching computer to the domain and break out of this sub-forloop
                        $usernamebinder = $domainBindEvents.$boundcompevent
                        break
                    }
                    else
                    {
                        #write-host false
                        # If no matching computer was identified in the event log IDs 4741 assign the fixed string to the $usernamebinder requesting the receiver of the email alert investigate.
                        $usernamebinder = "UNKNOWN, could not identify a user from the 4741 event ID's on the DCs - manual identification required"
                        #Log the
                        "$LOGTIME INFO No matching event log for binding $($comp.name.tolower()) - it didn't match $($boundcompevent.tolower())"| Out-File $LOG -Append -Force
                    }
                }
                 
                # Move the Computer Object to the $TargetOU
                move-adobject -identity $comp.DistinguishedName  -targetpath $TargetOU  
                  
                #Log the move computer object action
                "$LOGTIME INFO $($comp.Name) LOCATED AT $($comp.DistinguishedName) WAS MOVED TO THE _WindowsClients\WindowsClientsNeedOURelocation OU"| Out-File $LOG -Append -Force
                 
                # Add the computer name, distinguishedName, Description and the username of the person who bound the computer to the domain to the email body
                $body_output_complist += "`n" + "COMPUTER_NAME = " + $comp.Name +  "    USERNAME_THAT_BOUND_COMPUTER = " + $usernamebinder +  "    COMPUTER_DESCRIPTION = " + $comp.Description +  "    COMPUTER_DISTINGUISHED_NAME=" + $comp.DistinguishedName + "`n"
            }
                          
        #Email the results
        $body = $body_output + " " + $body_output_complist
        "$LOGTIME INFO Sending email to $($emailTo) reporting the computers that were discovered and moved ....  "| Out-File $LOG -Append -Force
        emailer $emailTo $emailFrom $subject_output $body
        "$LOGTIME SUCCESS Email sent  "| Out-File $LOG -Append -Force         
        "$LOGTIME FINISH The script has completed  "| Out-File $LOG -Append -Force
         
        }
    }
Catch
    {
        $ErrorMessage = $_.Exception.Message
        $FailedItem = $_.Exception.ItemName
        "$LOGTIME ERROR $($ErrorMessage) $($FailedItem)" | Out-File $LOG -Append -Force #LOGACTION
        exit
    }

Powershell : Module – Emailer

Introduction

A small emailer module which can be used to send emails using a locally available SMTP server.

Input

None

Output

An email is generated where a local SMTP server is available to route the email request

Logging

None

Usage Examples

Import the module to your script and then call it as per the example in the script

Dependancies

None

The Script

# Name      : Powershell Emailer when local SMPT available
# Author    : Dave 
# Date      : 
# Ticket#   : NONE
# Scope     : Power Shell Module, to the called by scripts
# Input(s)  : $emailFrom > [STR] > the sender's email address,
#             $emailTo   > [STR] > the target email addres,
#             $subject   > [STR] > the subject of the email 
#             $body  > [STR] > the email body
# Output(s) : An email is sent to the address specified.
#
# Example call:
# emailer "to@something.com" "from@somewhere.com" "email subject" "email body"
####################################################
 
# Notes:
########################
 
Function emailer ($emailTo, $emailFrom, $emailSubject, $emailBody)
<# This is a simple function that that sends an smtp email using the variables to define the email
i.e. "Function emailer ($emailFrom, $emailTo, $subject, $body) "
 
#>
 
{
$emailSmtpServer = "smtp.SOME.AVAILABLE.DOMAIN.COM"
Send-MailMessage -To $emailTo -From $emailFrom -Subject $emailSubject -Body $emailBody -SmtpServer $emailSmtpServer
}

PowerShell : DHCP Log Backup Script

Introduction

This script is designed to backup DHCP logs from a Windows DHCP Server to another location.

Input

InputsType
A text file with a list of target computer names, one per line. Variable assignment within the script

Output

outputsTypeItemDescription
Copy of DHCP Logs File(s)Logdefined by the script

Logging

Logging is basic for now, a log file is defined within the script by the controlling variables at the top and it logs all errors and successes from each command within the script.

Usage Examples

To call this script:

  1. Ensure the variables under the heading “Set the variables” are configured for
    1. A valid scriptlog directory
    2. A valid input file listing the target DHCP servers, one per line
  2. Ensure the target DHCP servers’s dhcp log directory is available to the calling security principal

Dependancies

emailer.psm1 module : https://www.oxfordshirecomputing.co.uk/powershell-module-emailer/

The Script

# Name      : Windows DHCP Log Backup
# Last Edit : 
# Author    : Dave 
# Date      : 
# Ticket#   :
# Scope     : All Windows DHCP Servers
# Inputs    : none
# Output(s) : Log of script actions in csv to $LOGFILE, targeted log files moved from the host server to the TARGET SERVER server.
 
####################################################
# This script is designed to take a list of DHCP server names or IP addresses as an input from an external text file via the
# $Computers variable and copy the previous day's DHCP logs from that server to the target backup server
# \\SERVER\Windows\DHCP\, creating a sub directory within that share in the DNS name or IP address of the DHCP server,
# leaving the original logs in place at source.
####################################################
 
# Notes:
########################
# To call this script:
# 1. Ensure the variables under the heading "Set the variables" are configured for
#    - A valid scriptlog directory
#    - A valid input file listing the target DHCP servers, one per line
# 2. Ensure the local dhcp folder is shared with read permissions for the calling principal (account) and has read file permissions enabled for that account.
# 3. Call the script via powershell, no arguments required.
 
# Set the variables
########################
# Log file variables
$LOGFILEDIR = "C:\scripts\Powershell\PSScriptLogs" # the full path of the log file for the script to output to
$LOGFILENAME =  "dhcplogbackup.ps1.log" # the full name of the log file for the script to output to
$LOG = ($LOGFILEDIR + "\" + $LOGFILENAME)
$LOGTIME = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
 
# Script variables
$Computers = Get-Content -Path "C:\scripts\Powershell\PSScriptInputs\dhcpservers.txt" # list of target computers
$dateYesterday = get-date -date $(get-date).adddays(-1) -format "MM/dd/yyyy"
$filedate =  get-date -date $(get-date).adddays(-1) -format "yyyy-MM-dd"
$today = get-date -format "MM/dd/yyyy"
$logdir = "Windows\System32\dhcp" #define the target log directory to be backed up / moved
# variables for the current computer -- > See below
 
 
# Email settings
$emailer = "C:\scripts\Powershell\Modules\emailer.psm1"
$emailTo = "it-alerts@something.com"
$emailFrom = "DHCPLogBackup@something.com"
# Email subject and the first section of the body
$subject_output = "DHCP Log Copy Script"
$body = "Ref: SOMEREF `n`nThe DHCP Log File backup script encountered an error. `n`nPlease complete the following actions:
`n`n
1. Log a ticket to record this alert and your response
`n
2. Visit the script execution server and examine the C:\Logs\dhcplogbackup.ps1.log file for clues
`n
3. Either correct the fault and re-run the script or manually copy the target DHCP logs which failed from \Windows\System32\Logs\DHCP\ to \\SERVER\Windows\DHCP\<SERVERNAME>\ and await the overnight scheduled re-run of the script
`n`n
This script runs to ensure we can retain a 90 day log of DHCP lease activity for our domain, it is critical in that regard until a centralised logging system has been established.
`n`n
Thanks.`n`n Dave
"
 
# Imported modules
Import-Module -Name $emailer #-Verbose
 
# Prepare the logging
##########################
# Check for the $LOGFILEDIR & $LOGFILENAME and create them if they don't exist
# NOTE: No logging until this happens except to STDOUT (Screen)
# Test for the folder and create if it doesn't exist
Try
    {
     
        if(! (Test-Path -Path $LOGFILEDIR )){
                New-Item -ItemType directory -Path $LOGFILEDIR
            }
    }
Catch
    {
        $ErrorMessage = $_.Exception.Message
        $FailedItem = $_.Exception.ItemName
        emailer $emailTo $emailFrom $subject_output "DHCP LOG BACKUP SCRIPT FAILED TO CREATE LOCAL SCRIPT LOGFILE!"
    }
     
# test for the file and create if it doesn't exist
Try
    {
        if(! (Test-Path $LOGFILEDIR\$LOGFILENAME  )){
            New-Item -ItemType file -Path $LOGFILEDIR\$LOGFILENAME
        }
    }
Catch
    {
        $ErrorMessage = $_.Exception.Message
        $FailedItem = $_.Exception.ItemName
        emailer $emailTo $emailFrom $subject_output "DHCP LOG BACKUP SCRIPT FAILED TO CREATE LOCAL SCRIPT LOGFILE!"
    }
     
"$LOGTIME BEGIN STARTING SCRIPT" | Out-File $LOG -Append -Force
 
# BODY
##########################
# Loop through the list of computers
$Computers | Foreach-Object {
     
    # Log the computer name in question
    "$LOGTIME START $($_)" | Out-File $LOG -Append -Force
     
    # variables for the current computer
    $hostname = $($_) # The current server's hostname
    $sourceLogs = "\\$hostname\dhcp" # the location of the DHCP logs ($logdir) on the current computer
    $targetdir = "\\SERVER FQDN\Windows\DHCP\$($hostname)" # Target dir on some server for current server
    $targetparent = split-path $targetdir # Parent dir of the target dir on some server for current server
     
    #Test access to the sourceLogs directory, if this fails log the failure and exit (the script can't copy from non-existent targets)
    try {
        if (test-path $sourceLogs -isvalid) {
            Try {
                # collect all filenames into the $files variable which begin with "Dhcp" and end with ".log" and which have a LastAccessTime value for yesterday
                $files = get-childitem $sourceLogs | where-object {($_.name -like "Dhcp*.log") -and ($_.LastAccessTime -ge $dateYesterday) -and ($_.LastAccessTime -lt $today)} | select name
            }
            catch{
                $ErrorMessage = $_.Exception.Message
                $FailedItem = $_.Exception.ItemName
                "$LOGTIME ERROR $($_) Unable to get-childitem for $($sourceLogs) $($ErrorMessage) $($FailedItem) " | Out-File $LOG -Append -Force
                emailer $emailTo $emailFrom $subject_output $body
                "$LOGTIME SUCCESS Email Alert Sent"| Out-File $LOG -Append -Force        
                "$LOGTIME FINISH The script has completed  "| Out-File $LOG -Append -Force
            }
             
            # check that files were found, if not generate an email alert for review of this script
            if (!($files)) {
                "$LOGTIME ERROR No files were located by the script : $($files) Review the logfile $($LOG) for clues - ensure the backup is completed!" | Out-File $LOG -Append -Force
                emailer $emailTo $emailFrom $subject_output $body
                "$LOGTIME FAIL Email Alert Sent"| Out-File $LOG -Append -Force        
                "$LOGTIME FINISH The script has completed  "| Out-File $LOG -Append -Force
            }
            }
             
             
            #Check the path \\SERVER\Windows\ contains a folder using this server's hostname, if not then create one.
            If (!(test-path $targetdir)) {
                Try {
                    New-Item -Path $targetparent -Name $hostname -ItemType "directory"
                }
                Catch {
                    $ErrorMessage = $_.Exception.Message
                    $FailedItem = $_.Exception.ItemName
                    "$LOGTIME ERROR $($_) Creating $($targetparent)\$($hostname) $($ErrorMessage) $($FailedItem)" | Out-File $LOG -Append -Force
                    emailer $emailTo $emailFrom $subject_output $body
                    "$LOGTIME FAIL Email Alert Sent"| Out-File $LOG -Append -Force        
                    "$LOGTIME FINISH The script has completed  "| Out-File $LOG -Append -Force
                }
            }
             
            #Copy the yesterday's DHCP log file into the target directory, prefixing the date to the filename
            Foreach ($file in $files) {
                try {
                    $fileName = $file.name
                    #write-host $fileName
                    copy-item $sourceLogs\$fileName -destination $targetdir\$filedate"-"$fileName
                }
                Catch {
                    $ErrorMessage = $_.Exception.Message
                    $FailedItem = $_.Exception.ItemName
                    "$LOGTIME ERROR $($_) executing copy-item or remove-item against $($sourceLogs)\$($fileName) : $($ErrorMessage) $($FailedItem)" | Out-File $LOG -Append -Force                 
                    emailer $emailTo $emailFrom $subject_output $body
                    "$LOGTIME FAIL Email Alert Sent"| Out-File $LOG -Append -Force        
                    "$LOGTIME FINISH The script has completed  "| Out-File $LOG -Append -Force
                }          
            }
        }  
        Catch {
        $ErrorMessage = $_.Exception.Message
        $FailedItem = $_.Exception.ItemName
        "$LOGTIME ERROR $($_) the $($sourceLogs) path was not found - no action taken, exiting..." | Out-File $LOG -Append -Force
        emailer $emailTo $emailFrom $subject_output $body
        "$LOGTIME FAIL Email Alert Sent"| Out-File $LOG -Append -Force        
        "$LOGTIME FINISH The script has completed  "| Out-File $LOG -Append -Force
    }
}

PowerShell : Delete Files Based on Path, Type & Age

Introduction

This script was created to clean up older IIS log files from a Windows IIS Server and is executed weekly on that server. It’s been written in as abstracted a manner as possible so could be used to delete any files in any target directory based on filetype and age by altering the variables.

Input

None, although IIS log files are required somewhere on disk!

Output

#SwitchesItemDescription
1NoneLog Log of files discovered and action(s) taken (if a file is deleted it will be recorded in the log).

Logging

Currently the logging is managed from within the script. The script currently resides on engs-svscc01.eng.ox.ac.uk and so the logging path variables have been designed to output within that server’s file system.

Log file location: C:\Logs\DeleteIISLogFilesOlderThan.ps1.log

$LOGTIME is s timestamp in the format “yyyy-MM-dd_hh-mm-ss”

Log call within the script:
“$LOGTIME <<TEXT TO LOG>>” | Out-File $LOG -Append -Force

The script is then contained with a try{} catch{} parameter with logging output for all exception messages and items.

I’ve also tried to log throughout each action within the script so both SUCCESS and ERRORs are logged which provides a debugging method (at least I should know where it falls over!).

Usage Examples

Contained in the header of the script – see below

The Script

# Name : DeleteIISLogFilesOlderThan.ps1
# Author : Dave 
# Date : 
# Ticket# : 
# version : 1.0
# Scope : Any directory defined by $TargetFolder, $filetype & $LastWrite defined by $Extension
#
# Input(s) : None
#
# Output(s) : Any files that match the $filetype in the $TargetFolder and are of $LastWrite age are logged in the $LOG and then deleted
#
# Usage Examples:
# PS> DeleteIISLogFilesOlderThan.ps1
 
 
########################
# Set the target variables for the folder, filetype and age
$Now = Get-Date
$Days = "<ENTER NUMBER OF DAYS HERE>"
$TargetFolder="<ENTER TARGET DIRECTORY HERE>"
$Extension = "*.log" # Change file type here
$LastWrite = $Now.AddDays(-$Days)
 
##########################
 
# Prepare the logging
##########################
# Check for the $LOGFILEDIR & $LOGFILENAME and create them if they don't exist
# NOTE: No logging until this happens except to STDOUT (Screen)
# Test for the folder and create if it doesn't exist:
 
$LOGFILEDIR = "C:\Logs" # the full path of the log file for the script to output to
$LOGFILENAME = "thisscriptlogfile.ps1.log" # the full name of the log file for the script to output to
$LOG = ($LOGFILEDIR + "\" + $LOGFILENAME)
$LOGTIME = Get-Date -Format "yyyy-MM-dd_hh-mm-ss"
 
Try
 {
 if(! (Test-Path -Path $LOGFILEDIR )){
 New-Item -ItemType directory -Path $LOGFILEDIR >$null
 }
 if(! (Test-Path $LOGFILEDIR\$LOGFILENAME )){
 New-Item -ItemType file -Path $LOGFILEDIR\$LOGFILENAME >$null
 }
 }
Catch
 {
 $ErrorMessage = $_.Exception.Message
 $FailedItem = $_.Exception.ItemName
 }
"$LOGTIME BEGIN STARTING SCRIPT" | Out-File $LOG -Append -Force
#########################
 
Try {
    if(Test-Path $TargetFolder )
    {
        $files = get-childitem -Path $TargetFolder -Include $Extension -Recurse | Where {$_.lastwritetime -lt "$LastWrite"}
        $filesFound = $false
        $files | Foreach-Object { $filesFound = $true }
 
        If($filesFound){
            "$LOGTIME  Non-Compliant, older files exist, removing and recording:" | Out-File $LOG -Append -Force
            foreach ($file in $files){
                "$LOGTIME  FILE DELETED : [$file.name]" | Out-File $LOG -Append -Force
                remove-item $file
            }
            "$LOGTIME SUCCESS Files deleted, exiting" | Out-File $LOG -Append -Force
        }
        Else
        {
            "$LOGTIME  Compliant, no files older than $($LastWrite) in $($TargetFolder) " | Out-File $LOG -Append -Force
        }
     }
     Else
     {
        #The Target Folder does not exists
        "$LOGTIME  Compliant, the folder $($TargetFolder) does not exist" | Out-File $LOG -Append -Force
     }
}
Catch
{
    $ErrorMessage = $_.Exception.Message
    $FailedItem = $_.Exception.ItemName
    "$LOGTIME ERROR $($ErrorMessage) $($FailedItem)" | Out-File $LOG -Append -Force #LOGACTION
}

PowerShell : Compress Folder (IN PROGRESS)

Introduction

This script was written to enable the backup of an application’s logs. The reason for this requirement is that the logs generated are overwritten between system or service restarts and we have a duty to maintain them for 5 years.

Input

#ItemRequired?Description / Location
1PARAMETERyes-folder <full path to target folder for compression>
2PARAMETERyes-zipfolder <full path to folder which will receive the zip file>

Output

# Item Description / Location
1 LOG All successful and failed operations will be logged in the log file defined by the script variable $LOG
2File Creation If successful a new *.zip file will be created in the -zipfolder <dir> defined by the caller

Logging

Currently the logging is managed from within the script.

Log file location: C:\Scripts\Powershell\Logs\compress-archive.ps1.log

$LOGTIME is s timestamp in the format “yyyy-MM-dd_hh-mm-ss”

Log call within the script:
“$LOGTIME <<TEXT TO LOG>>” | Out-File $LOG -Append -Force

The script is then contained with a try{} catch{} parameter with logging output for all exception messages and items.

I’ve also tried to log throughout each action within the script so both SUCCESS and ERRORs are logged which provides a debugging method (at least I should know where it falls over!).

TODO; incorporate an email alert for all failures

Usage Examples

Contained in the header of the script – see below

The Script

As this script is running at the lowest privileges (ENG\svEngScript via Task Scheduler) I had to create a new GPO to permit that pre-existing account “Log on as batch job” via ticket #26725. I also had to them provide read permissions to the Cadence log file source on svlic1801 (C:\Cadence\Logfiles) along with read+write access to the target directory for the zip file at “C:\srvinstall\Cadence\CadenceLogFileBackup”. After these changes the user account was able to execute the powershell script successfully.

# Name : Compress Archive
# Author : Dave 
# Date : 
# Ticket# : 
#
#
# Scope : The source folder to be compressed and an output folder to receive the compressed file (both passed via parameters)
#
# Input(s) : Parameters[-folder <full path to folder>, -zipfolder <full path to zip output FOLDER>]
#
# Output(s) : A compressed .zip file of the [-folder] specified at the [-zipfolder] location specified
 
 
####################################################
# This script provides a controlled method of zipping a folder
####################################################
 
####################################################
# USAGE EXAMPLES
#
# Compress a folder:
# C:\> compressarchive.ps1 -folder "c:\test" -zipfolder "C:\zipped"
# EXPECTED OUTPUT:
# 1of2: A log entry at the defined $LOGFILEDIR\$LOGFILENAME confirming success of the compression in the format:
# [DATETIME STAMP] SUCCESS <path to the source folder> compressed to <path to the target output dir>\2019-03-08_154256_<source folder name>.zip
# 2of2: A new .zip file at <path to the target output dir>\2019-03-08_154256_<source folder name>.zip should exist
####################################################
 
# Define the required parameter of an input file (will need to be in CSV format with 3 columns: Hostname,IPAddress,VendorName - tested later in the script)
##################################################################################
param(
 [Parameter(Mandatory=$true)]
 [string]$folder,
  
 [Parameter(Mandatory=$true)]
 [string]$zipfolder
)
 
# Define the Variables
##################################################################################
$datetime = get-date -Uformat "%Y-%m-%d_%H%M%S"
##################################################################################
$LOGFILEDIR = "SOME DIRECTORY OF YOUR CHOICE" # the full path of the log file for the script to output to
$LOGFILENAME = "compressarchive.ps1.log" # the full name of the log file for the script to output to
$LOG = ($LOGFILEDIR + "\" + $LOGFILENAME)
$LOGTIME = Get-Date -Format "yyyy-MM-dd_hh-mm-ss"
 
# Prepare the logging
##################################################################################
# Check for the $LOGFILEDIR & $LOGFILENAME and create them if they don't exist
# NOTE: No logging until this happens except to STDOUT (Screen)
# Test for the folder and create if it doesn't exist:
Try
 {
 if(! (Test-Path -Path $LOGFILEDIR )){
 New-Item -ItemType directory -Path $LOGFILEDIR >$null
 }
 if(! (Test-Path $LOGFILEDIR\$LOGFILENAME )){
 New-Item -ItemType file -Path $LOGFILEDIR\$LOGFILENAME >$null
 }
 }
Catch
 {
 $ErrorMessage = $_.Exception.Message
 $FailedItem = $_.Exception.ItemName
 }
"$LOGTIME BEGIN SCRIPT" | Out-File $LOG -Append -Force
 
# Main
##################################################################################
Try {
    # examine the variable $folder and remove any trailing \'s
    if ($folder.substring($folder.length -1) -eq "\"){
        "$LOGTIME INFO $($folder) needs the trailing \ removed" | Out-File $LOG -Append -Force
        $folder = $folder.substring(0,$folder.length -1 )
        "$LOGTIME INFO $folder has been renamed to $($folder)" | Out-File $LOG -Append -Force
    } else {
        "$LOGTIME INFO $($folder) matches the required format" | Out-File $LOG -Append -Force
    }
     
    # check the directory exists
    if (test-path $folder -pathtype Any) {
        "$LOGTIME SUCCESS $($folder) exists on the filesystem" | Out-File $LOG -Append -Force
    } else {
        "$LOGTIME ERROR $($folder) does not exist on the filesystem, exiting" | Out-File $LOG -Append -Force
        exit
    }
     
    # examine the variable $zipfolder and remove any trailing \'s
    if ($zipfolder.substring($zipfolder.length -1) -eq "\"){
        "$LOGTIME INFO $($zipfolder) needs the trailing \ removed" | Out-File $LOG -Append -Force
        $zipfolder = $zipfolder.substring(0,$zipfolder.length -1 )
        "$LOGTIME INFO $zipfolder has been renamed to $($zipfolder)" | Out-File $LOG -Append -Force
    } else {
        "$LOGTIME INFO $($zipfolder) matches the required format" | Out-File $LOG -Append -Force
    }
     
    # check the directory exists
    if (test-path $zipfolder -pathtype Any) {
        "$LOGTIME SUCCESS $($zipfolder) exists on the filesystem" | Out-File $LOG -Append -Force
    } else {
        "$LOGTIME ERROR $($zipfolder) does not exist on the filesystem, exiting" | Out-File $LOG -Append -Force
        exit
    }
     
    # create a unique filename $zip_file_name for the zip using the $datetime variable as part of the file
    # (eg: C:\output\2019-03-07_113010_logfiles.zip
    # where $zipfolder = c:\output and $folder = c:\someapplication\logfiles
    $zip_file_name = split-path $folder -leaf
    $zipfilepath = "$zipfolder\$datetime`_$zip_file_name.zip"
    "$LOGTIME SUCCESS `$zipfilepath assigned to '$($zipfilepath)' " | Out-File $LOG -Append -Force
     
    # compress the $folder outputting it to $zipfilepath and leaving the original folder in place
    compress-archive -path $folder -CompressionLevel Optimal -DestinationPath $zipfilepath
    "$LOGTIME SUCCESS $folder compressed to $($zipfilepath)" | Out-File $LOG -Append -Force
}
 
Catch {
 $ErrorMessage = $_.Exception.Message
 $FailedItem = $_.Exception.ItemName
 "$LOGTIME ERROR $($ErrorMessage) $($FailedItem)" | Out-File $LOG -Append -Force #LOGACTION
 }

Powershell : Add $COMPUTERS to $GROUP

Introduction

This script was created to add all computers in a given text file to a Windows AD security group

Output

New objects are added the defined AD security group – check the variables in the script for the input file and target group
Local log file of events and errors on the executing computer C:\Logs\PowershellScripts\add_computers_to_group.ps1.log

Logging

Currently the logging is managed from within the script.
Log file location: C:\Logs\ADCudIncomingDisableAccounts\cudincomingDisable.ps1.log
$LOGTIME is s timestamp in the format “yyyy-MM-dd_hh-mm-ss”
Log call within the script:
“$LOGTIME <>” | Out-File $LOG -Append -Force
The script is then contained with a try{} catch{} parameter with logging output for all exception messages and items.
I’ve also tried to log throughout each action within the script so both SUCCESS and ERRORs are logged which provides a debugging method (at least I
should know where it falls over!).

Usage Examples

Contained in the header of the script – see below
Dependancies
Powershell module “Import-Module ActiveDirectory”

The Script

# Name        : Add Computers to Group
# Author    : Dave
# Date        : 
# Ticket#    :
# Scope     :  Text file containing computer names and an existing AD security group
# Input(s)    : text file with a list of computer names
# Output(s)    : 1. Log of script actions in csv to $LOGFILE
#                 2. Computers added to the specified AD security group
####################################################
# This script is designed to
# 1.  Add all computers in the $COMPUTERS text file to into $ADGROUP specified
####################################################
  
# Notes:
########################
  
  
# Set the variables
########################
$COMPUTERS = Get-Content -Path "ENTER AN ACCESSIBLE PATH TO A TEXT FILE OF COMPUTER NAMES, ONE PER LINE" # list of target computers
$GROUP = "ENTER A VALID AD SECURITY GROUP NAME TO TARGET"
 
$LOGFILEDIR = "C:\Logs\PowershellScripts\" # the full path of the log file for the script to output to
$LOGFILENAME =  "add_computers_to_group.ps1.log" # the full name of the log file for the script to output to
$LOG = ($LOGFILEDIR + "\" + $LOGFILENAME)
$LOGTIME = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
  
# Prepare the logging
##########################
# Check for the $LOGFILEDIR & $LOGFILENAME and create them if they don't exist
# NOTE: No logging until this happens except to STDOUT (Screen)
  
# Test for the folder and create if it doesn't exist
Try
    {
      
        if(! (Test-Path -Path $LOGFILEDIR )){
                New-Item -ItemType directory -Path $LOGFILEDIR
            }
    }
Catch
    {
        $ErrorMessage = $_.Exception.Message
        $FailedItem = $_.Exception.ItemName
    }
      
# test for the file and create if it doesn't exist
Try
    {
        if(! (Test-Path $LOGFILEDIR\$LOGFILENAME  )){
            New-Item -ItemType file -Path $LOGFILEDIR\$LOGFILENAME
        }
    }
Catch
    {
        $ErrorMessage = $_.Exception.Message
        $FailedItem = $_.Exception.ItemName
    }
  
  
"$LOGTIME BEGIN STARTING SCRIPT" | Out-File $LOG -Append -Force 
 
# Log the computer name in question
    # query ad for the computer and append the results to an array named $BITLOCKEDCOMPS
    Try
    {
        foreach ($COMPUTER in $COMPUTERS) {           
            $obj = Get-ADComputer $COMPUTER
            Add-ADGroupMember -ID $GROUP -Members $obj
            "$LOGTIME $($COMPUTER) ADDED TO $GROUP" | Out-File $LOG -Append -Force
         }
    }
    Catch
    {
        $ErrorMessage = $_.Exception.Message
        $FailedItem = $_.Exception.ItemName
        "$LOGTIME ERROR $($_) Creating C:\$($TARGETDIR) $($ErrorMessage) $($FailedItem)" | Out-File $LOG -Append -Force
    }
      
    
 
      
"$LOGTIME END EXITING SCRIPT" | Out-File $LOG -Append -Force
exit

Powershell Domain Controller Assessment

To get a list of the FSMO Role holders for a Single Domain.

1 Get-ADDomain | Select-Object DistinguishedName, SchemaMaster, DomainNamingMaster, InfrastructureMaster, PDCEmulator, RIDMaster

To get a list of the FSMO Role holders in a Forest.

1 Get-ADForest | Select-Object Name,SchemaMaster, DomainNamingMaster,InfrastructureMaster, PDCEmulator, RIDMasterall

To get a nicely formatted list with all the Domain Controllers and who owns which particular role.

1 Get-ADDomainController -Filter * | Select-Object Name, Domain, Forest, OperationMasterRoles | Where-Object {$_.OperationMasterRoles}

ref: https://www.markou.me/2016/10/get-list-fsmo-role-holders-using-powershell-one-liners/

Powershell : Domain Binding & Description

To bind a machine to the domain, rename it and put it in the desired OU:

Add-Computer -DomainName $FQDN -NewName $COMPUTERNAME -Credential $DOMAINBINDACCOUNT -OUPath "OU=SOMEOU, DC=test, DC=com" -restart

To replace an AD Computer object’s Description field:

$description = "This is a test description"
$ADComputer = get-adcomputer <ENGS-XXXX> -properties Description
Set-ADComputer $ADComputer -Description "$($ADComputer.Description) $description"

Powershell : Useful Commands

Get members of a group:

get-adgroupmember -identity <GROUPNAME>

Get a list of user’s “PasswordLastSet” field has a date greater than 31/01/2000 along with their usernames and email addresses:

get-aduser -filter * -Properties PasswordLastSet | where {$_.passwordLastSet -ge [DateTime] "01/31/2000 00:01 AM"} | Select-Object Name, PasswordLastSet, SamAccountName, EmailAddress

Compare two CSV files for differences:

$refCSV = import-csv .\Source.csv 
$compCSV = import-csv .\Reference.csv 
compare-object -referenceobject $refCSV -DifferenceObject $compCSV | foreach { $_.InputObject}

Iterate over a text file of usernames (one per line) and query AD for some values, printing the useraccount’s containing OU in a easily readable form and output to results.csv:

$usersaffected = "c:\tmp\listofusernames.txt"
$output = foreach ($line in get-content $usersaffected) {get-aduser $line -Properties * | Select @{l='OU';e={$_.DistinguishedName.split(',')[1].split('=')[1]}},"whenCreated", "emailaddress", "passwordLastSet", "distinguishedName"}
$output | export-csv -path c:\tmp\results.csv