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

Batch : Service Status & Recovery

@echo off
 
REM Change the "servicename" value to match your desired service below
REM Change the "logfilename" value to your own logfile
REM change the %processLinked% value to the process name associated with the target service 
 
set servicename="explorerService"
set processLinked="explorer.exe"
set logfilename="C:\Logs\thisscript.log"
 
rem get the date and time
For /f "tokens=1-3 delims=/ " %%a in ('date /t') do (set mydate=%%c-%%b-%%a)
For /f "tokens=1-2 delims=/:" %%a in ('time /t') do (set mytime=%%a%%b)
 
rem execute the "sc query <service>" command and loop through the output
for /F "tokens=3 delims=: " %%i in ('sc query "%servicename%" ^| findstr " STATE"') do (
 
  rem log the datetime and service state to the logfile
  echo %mydate%_%mytime% >> %logfilename%
  echo "%servicename% Service's current state is: %%i" >> %logfilename%
 
  rem restart the service after killing off the associate %processLinked%
  echo "%servicename% Service is forcibly restarting..." >> %logfilename%
  taskkill /IM %processLinked% /F >> %logfilename%
  net start %servicename% >> %logfilename%
   
)

VSS Admin Writers & Services

VSS WriterService NameService Display Name
ADAM $instanceName WriterADAM_$instanceName$instanceName
ASR WriterVSSVolume Shadow Copy
BITS WriterBITSBackground Intelligent Transfer Service
Certificate AuthorityCertSvcActive Directory Certificate Services
COM+ REGDB WriterVSSVolume Shadow Copy
DFS Replication service writerDFSRDFS Replication
DHCP Jet WriterDHCPServerDHCP Server
FRS WriterNtFrsFile Replication
FSRM writersrmsvcFile Server Resource Manager
IIS Config WriterAppHostSvcApplication Host Helper Service
IIS Metabase WriterIISADMINIIS Admin Service
Microsoft Exchange Replica WriterMSExchangeReplMicrosoft Exchange Replication Service
Microsoft Exchange WriterMSExchangeISMicrosoft Exchange Information Store
Microsoft Hyper-V VSS WritervmmsHyper-V Virtual Machine Management
MSMQ Writer (MSMQ)MSMQMessage Queuing
MSSearch Service WriterWSearchWindows Search
NPS VSS WriterEventSystemCOM+ Event System
NTDSNTDSActive Directory Domain Services
OSearch VSS WriterOSearchOffice SharePoint Server Search
OSearch14 VSS WriterOSearch14SharePoint Server Search 14
OSearch15 VSS WriterOSearch15SharePoint Server Search 15
Registry WriterVSSVolume Shadow Copy
Shadow Copy Optimization WriterVSSVolume Shadow Copy
SharePoint Services WriterSPWriterWindows SharePoint Services VSS Writer
SMS WriterSMS_SITE_VSS_WRITERSMS_SITE_VSS_WRITER
SPSearch VSS WriterSPSearchWindows SharePoint Services Search
SPSearch4 VSS WriterSPSearch4SharePoint Foundation Search V4
SqlServerWriterSQLWriterSQL Server VSS Writer
System WriterCryptSvcCryptographic Services
TermServLicensingTermServLicensingRemote Desktop Licensing
WDS VSS WriterWDSServerWindows Deployment Services Server
WIDWriterWIDWriterWindows Internal Database VSS Writer
WINS Jet WriterWINSWindows Internet Name Service (WINS)
Windows Server Storage VSS WriterWseStorageSvcWindows Server Essentials Storage Service
WMI WriterWinmgmtWindows Management Instrumentation

SCCM Site Console (Admin UI) Failure

Following a number of interferences with our beloved SCCM Primary Site it was in a broken state.

The “\Program Files\Microsoft Configuration Manager\AdminConsole\AdminUILog\SmsAdminUI.log” reported:

“Error Code:
ProviderLoadFailure
\r\nSystem.Management.ManagementException\r\nProvider load failure \r\n   at System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)
   at System.Management.ManagementObjectCollection.ManagementObjectEnumerator.MoveNext()
   at Microsoft.ConfigurationManagement.ManagementProvider.WqlQueryEngine.WqlQueryResultsObject.<GetEnumerator>d__74.MoveNext()\r\nManagementException details:
instance of __ExtendedStatus
{
    Operation = “ExecQuery”;
    ParameterInfo = “SELECT * FROM SMS_Site WHERE SiteCode = ‘XXX'”;
    ProviderName = “WinMgmt”;
};

Where “XXX” is the name of an SCCM site but not ours! This led me to https://docs.microsoft.com/en-us/previous-versions/system-center/configuration-manager-2007/bb633148(v=technet.10)?redirectedfrom=MSDN followed by https://docs.microsoft.com/en-us/previous-versions/system-center/configuration-manager-2007/bb932190(v=technet.10)?redirectedfrom=MSDN , this latter article walked me through examinig the SMS_Providor WMI class and it is there I found the spurious reference to the unknown XXX site.

Deleting that reference restored AdminUI Console functionality (yay!).

Another case with similar symptoms was the result of a WMI db corruption which caused the OS to automatically rebuild the db but which did not re-import the SCCM WMI references. That was resolved by:

  1. Run > Wbemtest
  2. Connect to “root\sms” –> Enum Classes–>Select recursive and check for SMS_ProviderLocation – it was not present
  3. From an elevated command prompt enter the following commands to add the SCCM .mof’s (excluding the _*.mofs) to the $mofs variable and then switch to the Wbem folder and import the WMI modules to the OS PS D:\> $mofs = get-childitem "D:\Program Files\Microsoft Configuration Manager\bin\X64" -filter "*.mof" -exclude "_*.mof" -recursePS D:\> cd C:\Windows\System32\WbemPS C:\Windows\System32\Wbem> foreach ($mof in $mofs){mofcomp $mof.FullName}
  4. Restart the Windows Management Instrumentation Service
  5. Repeated Steps#1 & 2 and noted SMS_ProviderLocation now exists in WMI
  6. Launched SCCM Admin Console locally successfully
  7. Launched SCCM Admin Console remotely successfully
  8. Launched Task Sequence Creation Wizard successfully

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"