DevOps meets VMware Horizon. Automating the daily busy tasks out of the way. Part 3 of 4 Automation the update install process.

Over the last few months, I have been working heavily on building a model of managing the process to patch for Horizon Linked and Instant Clone masters. After some long hard work, I have managed to create a process to install updates and 3rd party application updates.

Our process was built around using SCCM to install Windows updates to the master image, and also 3rd Party Software. After talking about the concept of automating the Recompose/Push process. I realized how many people could use this. So in the process, I decided to write the code to install Windows Updates from Update Manager, and 3rd party application from other PowerShell scripts.

The Script I created does the following:

  • Runs a report of what the current installed 3rd party software is before updating
  • Captures Credentials for VM Masters
  • Copy Files from Share to Master VM
  • Install updates from SCCM
  • Install 3rd Party from SCCM (Requires Automatic install of Software)
  • Install Windows updates (If not managed by SCCM)
  • Install 3rd Party updates (If not managed by SCCM, and requires you to find appropriate scripts to install updates. There are many things on GitHub for the installs.)
  • Setup an AutoLog On process to the Master VM. This will Log on to the VM after reboot and run the shutdown script LINK HERE.
  • Run a report of what the current installed 3rd party software is after updating.
  • Run a report of what the installed updates are since the last recompose
  • Create a custom registry key to store the last recompose update.

This script is highly dependent on the folder structure of the following.

  • C:\VDI_Tools
    • Logs (This is where all the log files will be saved)
    • Scripts (Save this script and run it from this location)
    • CloneTools (Place your VMware Optimizer and templates in that location.)

This has been tested on Win7 and Win10 running PowerShell version 5.1. The reason I have not included the 3rd party update process is that each environment requires different features or specs. Some may want to disable some things where others may want to keep them on.

**Does make the call to run the Shutdown Script that was posted in post 4 of 4 HERE **

# Recompose Master Image (Step2)

# Chris Hildebrandt

# 10-19-2018

# Ver 1.5

# Script will Copy Files to Image, Enable SCCM service, Install SCCM updates, Wait X seconds, and Reboot

#______________________________________________________________________________________

#Varibles

$ScriptDate = Get-Date -Format MM-dd-yyyy

$ScriptDate2 = Get-Date -UFormat %m/%d/%y #Date used for custom Registry Key and Windows Updates Report

$FQDN = (Get-WmiObject win32_computersystem).DNSHostName+"."+(Get-WmiObject win32_computersystem).Domain

$LogLocation = "C:\VDI_Tools\Logs\$ScriptDate\$FQDN"

$SleepTimeInS = "60" #Sleep time in seconds

$AdobeUpdate = "C:\VDI_Tools\Scripts\AdobeAcrobatUpdate.ps1" #Adobe Acrobat Update Script

$FlashUpdate = "C:\VDI_Tools\Scripts\FlashUpdate.ps1" #Flash Update Script

$FirefoxUpdate = "C:\VDI_Tools\Scripts\FireFoxUpdate.ps1" #Firefox Update Script

$JavaUpdate = "C:\VDI_Tools\Scripts\JavaUpdate.ps1" #Java Update Script

$ShutdownScript = "C:\VDI_Tools\Scripts\ShutdownScript.ps1" #Shutdown Script Location

$CustomRegPath = "HKLM:\Software\Company\Horizon\" #Custom Registry Location. Replace Company with your Company Name

#______________________________________________________________________________________

#Copy Locations

$CopyshareLocation = "\\share\VDI_Tools\" #Remote Share location

$DestinationLocation = "C:\VDI_Tools\"

#______________________________________________________________________________________

#Start the Debug Logging

$ErrorActionPreference="SilentlyContinue"

Stop-Transcript | out-null

$ErrorActionPreference = "Continue"

Start-Transcript -path "$loglocation\RecomposeMSTRInstallUpdates_$ScriptDate.txt"

#______________________________________________________________________________________

# Define functions

#______________________________________________________________________________________

# Import Function Get-Software
# Created By Boe Prox HTTPS://MCPMAG.COM

Function Get-Software {

[OutputType('System.Software.Inventory')]

[Cmdletbinding()]

Param(

[Parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]

[String[]]$Computername=$env:COMPUTERNAME

)

Begin {

}

Process {

ForEach ($Computerin$Computername){

If (Test-Connection-ComputerName $Computer-Count 1-Quiet) {

$Paths=@("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall","SOFTWARE\\Wow6432node\\Microsoft\\Windows\\CurrentVersion\\Uninstall")

ForEach($Pathin$Paths) {

Write-Verbose"Checking Path: $Path"

# Create an instance of the Registry Object and open the HKLM base key

Try {

$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer,'Registry64')

} Catch {

Write-Error$_

Continue

}

# Drill down into the Uninstall key using the OpenSubKey Method

Try {

$regkey=$reg.OpenSubKey($Path)

# Retrieve an array of string that contain all the subkey names

$subkeys=$regkey.GetSubKeyNames()

# Open each Subkey and use GetValue Method to return the required values for each

ForEach ($keyin$subkeys){

Write-Verbose"Key: $Key"

$thisKey=$Path+"\\"+$key

Try {

$thisSubKey=$reg.OpenSubKey($thisKey)

# Prevent Objects with empty DisplayName

$DisplayName=$thisSubKey.getValue("DisplayName")

If ($DisplayName-AND$DisplayName-notmatch'^Update for|rollup|^Security Update|^Service Pack|^HotFix') {

$Date=$thisSubKey.GetValue('InstallDate')

If ($Date) {

Try {

$Date= [datetime]::ParseExact($Date,'yyyyMMdd',$Null)

} Catch{

Write-Warning"$($Computer): $_ <$($Date)>"

$Date=$Null

}

}

# Create New Object with empty Properties

$Publisher=Try {

$thisSubKey.GetValue('Publisher').Trim()

}

Catch {

$thisSubKey.GetValue('Publisher')

}

$Version=Try {

#Some weirdness with trailing [char]0 on some strings

$thisSubKey.GetValue('DisplayVersion').TrimEnd(([char[]](32,0)))

}

Catch {

$thisSubKey.GetValue('DisplayVersion')

}

$UninstallString=Try {

$thisSubKey.GetValue('UninstallString').Trim()

}

Catch {

$thisSubKey.GetValue('UninstallString')

}

$InstallLocation=Try {

$thisSubKey.GetValue('InstallLocation').Trim()

}

Catch {

$thisSubKey.GetValue('InstallLocation')

}

$InstallSource=Try {

$thisSubKey.GetValue('InstallSource').Trim()

}

Catch {

$thisSubKey.GetValue('InstallSource')

}

$HelpLink=Try {

$thisSubKey.GetValue('HelpLink').Trim()

}

Catch {

$thisSubKey.GetValue('HelpLink')

}

$Object= [pscustomobject]@{

Computername=$Computer

DisplayName=$DisplayName

Version=$Version

InstallDate=$Date

Publisher=$Publisher

UninstallString=$UninstallString

InstallLocation=$InstallLocation

InstallSource=$InstallSource

HelpLink=$thisSubKey.GetValue('HelpLink')

EstimatedSizeMB= [decimal]([math]::Round(($thisSubKey.GetValue('EstimatedSize')*1024)/1MB,2))

}

$Object.pstypenames.insert(0,'System.Software.Inventory')

Write-Output$Object

}

} Catch {

Write-Warning"$Key : $_"

}

}

} Catch {}

$reg.Close()

}

} Else {

Write-Error"$($Computer): unable to reach remote system!"

}

}

}

}

#______________________________________________________________________________________

# Start Services Function

function Start-VDIservices ($SVCname) {

Set-Service-Name $svcname-StartupType Automatic

Start-Service-Name $svcname

Do {

$svc=Get-Service-Name $svcname

Start-Sleep2

} While ( $svc.Status -ne "Running" )

}

#______________________________________________________________________________________

#Check is VDI Masters Account Password file exists

if(-Not (Test-Path -Path "C:\VDI_Tools\Scripts\VDIMastersPassword.txt" ))

{

Write-Host"Credentials file not found proceeding to creation"

#______________________________________________________________________________________

#Create Secure Password File

Get-Credential-Message "Enter VDI Master Admin Account Domain\Username"|Export-Clixml"C:\VDI_Tools\Scripts\VDIMastersPassword.txt"

write-host"Create Credentials File by prompting for creds"

}

#______________________________________________________________________________________

#Import Secure Creds for use.

$VDIMSTRCreds = Import-Clixml "C:\VDI_Tools\Scripts\VDIMastersPassword.txt"

write-host "Import Secure Creds"

#______________________________________________________________________________________

#Copy Clone Tools and Scripts

Copy-Item $CopyshareLocation -Destination $DestinationLocation -Recurse

write-host "Copy Files from Share"

#______________________________________________________________________________________

# Capture installed Software before updating

get-software | Format-Table | Out-File "$LogLocation\InstalledSoftwareBefore.txt"

write-host "Capture Installed Software Prior to update and save to Logs Folder"

#______________________________________________________________________________________

#Check to see if machine is managed by SCCM

$CheckSCCM = get-wmiobject win32_Service | Where-Object {$_.Name -eq "CCMexec"} | Select-Object Name

write-host "Check if VM is managed by SCCM"

#______________________________________________________________________________________

#If Else statement based on if VM is managed by SCCM

if($CheckSCCM.Name -eq 'CCMexec')

{

write-host"VM is managed by SCCM"

#______________________________________________________________________________________

#Check Status of SCCM service

$SCCMStatus=get-service-name CCMexec |Select-Object Name,Status,Starttype

if($SCCMStatus.Status-eq'Stopped')

{

#______________________________________________________________________________________

#Set SCCM to Automatic and Start Service

Start-VDIservices CCMexec

write-host"Start SCCM Services"

}

#______________________________________________________________________________________

#Start SCCM Patching

$AppEvalState0="0"

$AppEvalState1="1"

$Application= (Get-WmiObject-Namespace "root\ccm\clientSDK"-Class CCM_SoftwareUpdate |Where-Object { $_.EvaluationState-like"*$($AppEvalState0)*"-or$_.EvaluationState-like"*$($AppEvalState1)*"})

Invoke-WmiMethod-Class CCM_SoftwareUpdatesManager -Name InstallUpdates -ArgumentList (,$Application) -Namespace root\ccm\clientsdk

write-host"Invoke SCCM install of Updates"

#______________________________________________________________________________________

#Pause Script to wait for Updates to install

Start-Sleep-Seconds $SleepTimeInS

write-host"Start Sleep Timer"




}

else

#______________________________________________________________________________________

#Else statemet for installing Windows updates through windows updates, and making external calls to other scripts to update

{

#______________________________________________________________________________________

#Check Status of Windows Update service

$WindowsUpdatateStatus=get-service-name wuauserv |Select-Object Name,Status,Starttype

if($WindowsUpdatateStatus.Status-eq'Stopped')

{

#______________________________________________________________________________________

#Set Windows Update to Automatic and Start Service

Start-VDIservices wuauserv

write-host"Started Windows Update Service"

}

#______________________________________________________________________________________

#Install 3rd Party updates (Java, Flash, Adobe Reader, Chrome, Firefox)

#______________________________________________________________________________________

#Check to see if Adobe Flash is installed

if ((Get-Software|Select-Object DisplayName) -like"*Flash*")

{

#______________________________________________________________________________________

#Install Flash Updates

Invoke-Expression$FlashUpdate

write-host"Installed Flash Update"

}




#______________________________________________________________________________________

#Check to see if Adobe Acrobat is installed

if ((Get-Software|Select-Object DisplayName) -like"*Acrobat Reader*")

{

#______________________________________________________________________________________

#Install Adobe Updates

Invoke-Expression$AdobeUpdate

write-host"Installed Adobe Acrobat Update"

}




#______________________________________________________________________________________

#Check to see if Firefox is installed

if ((Get-Software|Select-Object DisplayName) -like"*Mozilla Firefox*")

{

#______________________________________________________________________________________

#Install Firefox Updates

Invoke-Expression$FirefoxUpdate

write-host"Installed Firefox Update"

}




#______________________________________________________________________________________

#Check to see if Java is installed

if ((Get-Software|Select-Object DisplayName) -like"*Java*")

{

#______________________________________________________________________________________

#Install Java Updates

Invoke-Expression$JavaUpdate

write-host"Installed Java Update"

}

#______________________________________________________________________________________

#Check to see if Chrome is installed

if ((Get-Software|Select-Object DisplayName) -like"*Chrome*")

{

#______________________________________________________________________________________

#Install Chrome Updates

C:\"Program Files (x86)\Google\Update\GoogleUpdate.exe" /ua /installsource scheduler

write-host"Installed Google Chrome Update"

}

#______________________________________________________________________________________

#Prep for windows updates and Install updates

#______________________________________________________________________________________

#Hide Windows updates labled as "Preview"

Hide-WindowsUpdate-Title "Preview*"-Confirm:$false

write-host"Hide Windows Updates labled as Preview"

#______________________________________________________________________________________

#Auto instal windows updates

Get-WUInstall –AcceptAll -install

write-host"Install Windows Updates"




#______________________________________________________________________________________

#Start Sleep timer for update installs.

# Start-Sleep -Seconds $SleepTimeInS

write-host"Wait for Windows Updates to install"

}

#______________________________________________________________________________________

#Decrypt Password for imput into reg.

$VDIMSTRTextCreds = $VDIMSTRCreds.Password | ConvertFrom-SecureString

$VDIMSTRTextCredsPlain = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( (ConvertTo-SecureString $VDIMSTRTextCreds) ))

#______________________________________________________________________________________

#Set AutoLog on and auto kick off step3

$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"

$RegROPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"

Set-ItemProperty $RegPath "AutoAdminLogon" -Value "1" -type String

write-host"Set Registry Key for AutoLogon"

Set-ItemProperty $RegPath "DefaultUsername" -Value $VDIMSTRCreds.UserName -type String

write-host"Set Registry Key for AutoLogon Username"

Set-ItemProperty $RegPath "DefaultPassword" -Value $VDIMSTRTextCredsPlain -type String

write-host "Set Registry Key for AutoLogon Password"

Set-ItemProperty $RegROPath "(Default)" -Value "c:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy unrestricted -file $ShutdownScript" -type String

write-host "Set Registry Key for AutoLogon Logon Script"

#______________________________________________________________________________________

#Remote registry keys for legal notice

Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "legalnoticecaption"

Write-Host "Remove Legal Notice Title Registry Key"

Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "legalnoticetext"

Write-Host "Remove Legal Notice Text Registry Key "

#______________________________________________________________________________________

# Capture installed Software before updating

get-software | Format-Table | Out-File "$LogLocation\InstalledSoftwareAfter.txt"

write-host "Capture Installed Software After update and save to Logs Folder"

#______________________________________________________________________________________

# Check if Custom REG key is created if not create LastRecomposeDate REG key with 1/1/2000 date

if ( -not (Test-Path -Path $CustomRegPath)) {New-Item -path $CustomRegPath -Force | New-ItemProperty -Name LastRecomposeDate -Value "01/01/2000" -Force | Out-Null}

write-host "Check if Custom key has been created and if not create registry key and set value to 1-1-2000"

#______________________________________________________________________________________

# Get LastRecomposeData value

$LastRecomposeDate = Get-ItemProperty -Path $CustomRegPath -Name LastRecomposeDate | Select-Object -ExpandProperty LastRecomposeDate

write-host "Get Last recompose date from custom Registry key, Last Recompose date was $LastRecomposeDate"

#______________________________________________________________________________________

# Get Windows Updates installed since last recompose.

Get-HotFix | Where-Object -Property InstalledOn -GT $LastRecomposeDate | Out-File -FilePath "$LogLocation\WindowsUpdates.txt"

write-host "Create TXT file with Installed updates as of $LastRecomposeDate"

#______________________________________________________________________________________

#Set REG Key to store Last Recompose Date

Set-ItemProperty -Path $CustomRegPath -Name LastRecomposeDate -Value "$ScriptDate2" -Force

write-host "Set Last Recompose date to $ScriptDate2"

#______________________________________________________________________________________

# Stop Logging

Stop-Transcript

#______________________________________________________________________________________

#Reboot VM

Shutdown -r

Over the next few weeks, I hope to have part 2 and Part 1 out on this process and compile the entirety of the recompose script. Again if you find anything wrong or missing please let me know.

**As security disclaimer that when their Script adds the registry key for auto log on password it is saved in plain text. Will be editing the Shutdown script to remove the key when the machine finished its reboot and calls the shutdown script.

Please read the 4 of 4 located HERE

Advertisements
Posted in Virtulization, CLI and Powershell, VDI, DevOps, Blogtober | Tagged , , , , | Leave a comment

DevOps meets VMware Horizon. Automating the daily busy tasks out of the way. Part 4 of 4

Many people have created Shutdown or clean up scripts for the Linked Clone or Instant Clone VMs before, I am trying to do something a little different. I have been working on automating the process of Recompose or Image push from start to finish and here is my rendition of the Shutdown/Cleanup script in PowerShell. This is the last piece of a 3-piece script. I have delayed this rollout due to wanting to clean up the script and add some better logging with valid error logs.

So what have I done? I originally ran just a BAT script that worked well, but with times changing and wanting to do more in PowerShell, So I opted to tackle it this way. This gave the ability to do some logging with it and do more integration into other scripts if I decided.

Functions performed by the script:

  • Checks the version of windows. (This is used for the optimizer at the end.)
  • Runs Disk Cleanup
  • Runs Defragment
  • Pre-Compile .NET Framework
  • Runs SEP update, Scan, Forced Check-in, and Clone Prep.
  • Runs VMware Optimizer based on what OS version you are on and what templates you have defined. (Beings its ran via CMD there are no rolling back changes, it’s a bug/Feature in the Optimizer)
  • Disables or Stops Services like Windows Updates, SCCM, Adaptiva. AppVolumes
  • Clean out Downloads Cache folder
  • Clean out Windows Prefetch
  • Clear the event logs
  • Releases IP Addresses
  • Clears DNS
  • Shutdown the VM

These are the normal processes we run through when preparing a Master image for cloning. This has been tested on Win7, Win8, and Win10 running PowerShell version 5.1. Please pay attention to the folder structure and the template naming structure for the optimizer. I will eventually build an install package for this but will take some time.

For logs and files to save correctly I am using a nested file Structure of:

  • C:\VDI_Tools
    • Logs (This is where all the log files will be saved)
    • Scripts (Save this script and run it from this location)
    • CloneTools (Place your VMware Optimizer and templates in that location.)
# Shutdown Script to Prepare Master Image for Recompose or Image Push

# Chris Hildebrandt

# 10-18-2018

# Ver 1.5

# Script will Run Disk Cleanup, Defrag, Run SEP scan force update, Run Optimizer, and shutdown services, clear DNS and IP, and Shutdown VM

#______________________________________________________________________________________

# Check Version of OS

$winVersion = "0"

$OSVersion = [System.Environment]::OSVersion.Version

if ($OSversion.Major -eq "10" -and $OSversion.Minor -eq "0"){ $winVersion = "10" }

elseif ($OSversion.Major -eq "6" -and $OSversion.Minor -eq "2") { $winVersion = "8" }

elseif ($OSversion.Major -eq "6" -and $OSversion.Minor -eq "1") { $winVersion = "7" }

Write-host "Windows Version is $winVersion"

#______________________________________________________________________________________

# Varibles

$ScriptDate = Get-Date -Format MM-dd-yyyy

$FQDN = (Get-WmiObject win32_computersystem).DNSHostName+"."+(Get-WmiObject win32_computersystem).Domain

$LoginFile = "C:\VDI_Tools\Logs\$ScriptDate\$FQDN\ShutdownScript_$ScriptDate.txt"

$defragbat = "C:\VDI_Tools\Scripts\dfrag.bat"       #Temporary Location where it saves the Defrag.bat file.

#______________________________________________________________________________________

# Optimizer Varibles

$RunOptimizer = 1   # To run Optimizer enter 1, If you do not want to run enter 0

$OptimizerLocation = "C:\VDI_Tools\CloneTools\VMwareOSOptimizationTool"

$OptimizerTemplate = "C:\VDI_Tools\CloneTools\ARDx_Windows${winVersion}.xml"

$OptimizerLogLocation = "C:\VDI_Tools\Logs\$ScriptDate\$FQDN\"

$OptimizerErrorLogLocation = "C:\VDI_Tools\Logs\$ScriptDate\$FQDN\OptimizationErrorLog.txt"

$OptimizerbatFile = "C:\VDI_Tools\Scripts\Optimizer.$fqdn.bat"         #Temporary Location where it saves the Optimizer.bat file.

$RunSEP = 1 # To run Optimizer enter 1, If you do not want to run enter 0

#______________________________________________________________________________________

# Start the Debug Logging

$ErrorActionPreference="SilentlyContinue"

Stop-Transcript | out-null

$ErrorActionPreference = "Continue"

Start-Transcript -path $LoginFile

Write-host "Windows Version is $winVersion"

#______________________________________________________________________________________

# Define functions

#______________________________________________________________________________________

# Start Services Function

function Start-VDIservices ($SVCname) {

    Set-Service -Name $svcname -StartupType Automatic

    Start-Service -Name $svcname

    Do {

    $svc = Get-Service -Name $svcname

    Start-Sleep 2

    } While ( $svc.Status -ne "Running" )

    }

#______________________________________________________________________________________

# Stop Services Function

function Stop-VDIservices ($SVCname) {

    Stop-Service -Name $svcname

    Do {

    $svc = Get-Service -Name $svcname

    Start-Sleep 2

    } While ( $svc.Status -ne "Stopped" )

    Set-Service -Name $svcname -StartupType disabled

    }

#______________________________________________________________________________________

# Do while proc Like Function

function DoWhile-LikeProcName ($Process) {

    Do

    {

    "Processes $Process is still running"

    $proc = Get-Process

    start-sleep 10

    } While ($proc.name -like "*$Process*")

    }

#______________________________________________________________________________________

# Do while Proc Equals Function

function DoWhile-EQProcName ($Process) {

    Do

    {

    "Processes $Process is still running"

    $proc = Get-Process

    start-sleep 10

    } While ($proc.name -eq "$Process")

    }    




#______________________________________________________________________________________

# Run Disk Cleanup to remove temp files, empty recycle bin and remove other unneeded files

Start-Process -Filepath "c:\windows\system32\cleanmgr" -argumentlist '/sagerun:1'

Write-host "Running Disk Cleanup"

#______________________________________________________________________________________

# Check status of Disk Cleanup

DoWhile-LikeProcName cleanmgr

#______________________________________________________________________________________

# Check status of Dfrag Service if stopped start.

$DfragStatus = get-service -name defragsvc | Select-Object Name,Status,Starttype

if($DfragStatus.Status -eq 'Stopped')

{

#______________________________________________________________________________________

# Start Defrag Service

Start-VDIservices defragsvc

Write-host "Start Defrag Service"

}

#______________________________________________________________________________________

# Create Temp Defrag BAT

"defrag c: /U /V" | Set-Content $defragbat

Write-host "Create Defrag Temp .bat file"

#______________________________________________________________________________________

# Start Defrag BAT

Start-Process $defragbat

Write-host "Running Defrag Cleanup"

#______________________________________________________________________________________

# Check status of Disk Defrag

DoWhile-EQProcName Defrag

#______________________________________________________________________________________

# Remove Temp Defrag BAT

Remove-Item -Path $defragbat

Write-host "Delete Defrag Temp .bat file"

#______________________________________________________________________________________

# Stop Defrag Service

Stop-VDIservices defragsvc

Write-host "Stop Defrag Service"

#______________________________________________________________________________________

# Pre-complile .NET framework Assemblies

Start-Process -Filepath "C:\Windows\Microsoft.NET\Framework\v4.0.30319\ngen.exe" -argumentlist 'update','/force'

New-ItemProperty -Name verbosestatus -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Force -PropertyType DWORD -Value "1"

Write-host "Running a Pre-Compile of .NET Framework"

#______________________________________________________________________________________

# Check status of the .NET recompile

DoWhile-LikeProcName ngen

#______________________________________________________________________________________ 

# Run SEP update and Cleanup

if($RunSEP -eq '1')

{

    Write-host "Starting the SEP Update and Cleanup Process"

#______________________________________________________________________________________

# Check to see if SEP is installed and prep for Recompose

#______________________________________________________________________________________

# If Check for SEP install

$CheckSEP = get-service -name SepMasterService | Select-Object Name,Status,Starttype

if($CheckSEP.Name -eq 'SepMasterService')

    {

    #______________________________________________________________________________________

    # Force SEP checking and Update

    Start-Process -Filepath "C:\Program Files (x86)\Symantec\Symantec Endpoint Protection\SepLiveUpdate.exe"

    Write-host "Liveupdate SEP Config"

    #______________________________________________________________________________________ 

    # Check status of SEP Update

    DoWhile-LikeProcName SepLiveUpdate

    #______________________________________________________________________________________ 

    # Run full SEP System scan

    Start-Process -Filepath "C:\Program Files (x86)\Symantec\Symantec Endpoint Protection\doScan.exe" -argumentlist '/scanname "Full System Scan"'

    Write-host "Run Full System SEP Scan"

    #______________________________________________________________________________________ 

    # Check status of SEP Scan

    DoWhile-LikeProcName doscan

    #______________________________________________________________________________________ 

    # Force SEP checking and Update

    Start-Process -Filepath "C:\Program Files (x86)\Symantec\Symantec Endpoint Protection\smc.exe" -argumentlist '-updateconfig'

    Write-host "Update SEP and force Checkin to report the recent completed Scan"

    #______________________________________________________________________________________ 

    # Check status of SEP Update

    DoWhile-LikeProcName doscan

    #______________________________________________________________________________________

    # Check status of Symantec Endpoint Protection Service.

    $SEPStatus = get-service -name SepMasterService | Select-Object Name,Status,Starttype

    if($SEPStatus.Status -eq 'Running')

    {

        #______________________________________________________________________________________

        # Stop SEP Service and Prep for Clone

        Start-Process -Filepath "C:\Program Files (x86)\Symantec\Symantec Endpoint Protection\smc.exe" -argumentlist '-stop'

        Write-host "Stopped SEP Service"

    }

        $filesToClean = @("C:\sephwid.xml","C:\communicator.dat","C:\Program Files\Common Files\Symantec Shared\HWID\sephwid.xml","C:\Program Files\Common Files\Symantec Shared\HWID\communicator.dat", "C:\Windows\Temp\sephwid.xml","C:\Windows\Temp\communicator.dat","C:\Documents and Settings\*\Local Settings\Temp\sephwid.xml","CC:\Documents and Settings\*\Local Settings\Temp\communicator.dat","C:\Users\*\AppData\Local\Temp\sephwid.xml","C:\Users\*\AppData\Local\Temp\communicator.dat")

        foreach($file in $filesToClean)

        {

            if(Test-Path $file) {Remove-Item -Path $file -Recurse -force}

            Write-Host "Removed File $File"

        }




        #______________________________________________________________________________________ 

        # Remove SEP Reg Keys

        #______________________________________________________________________________________ 

        # Remove 32bit SEP Reg Keys

        if (Test-Path -Path "HKLM:\SOFTWARE\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink")

        {

            #Remove-ItemProperty -Path "HKLM:\SOFTWARE\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "ForceHardwareKey"

            Remove-ItemProperty -Path "HKLM:\SOFTWARE\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "HardwareID"

            Remove-ItemProperty -Path "HKLM:\SOFTWARE\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "HostGUID"

            Write-host "Removed SEP 32 Bit Reg Keys"

        }

        if (Test-Path -Path "HKLM:\SOFTWARE\WOW6432Node\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink")

        {

            #Remove-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "ForceHardwareKey"

            Remove-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "HardwareID"

            Remove-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "HostGUID"

            Write-host "Removed SEP 64 Bit Reg Keys"

         }




    }

    Write-host "Finished SEP Cleanup and Prep for Cloneing"

}

#______________________________________________________________________________________ 

# Run VMware optimizer

if($RunOptimizer -eq '1')

{

    Write-host "Prepairing to run VMware Optimizer"

#______________________________________________________________________________________ 

# Create Temp Bat file to optimize OS

@"

start /wait $OptimizerLocation -t $OptimizerTemplate -r $OptimizerLogLocation

start /wait $OptimizerLocation -o recommended -t $OptimizerTemplate -v > $OptimizerErrorLogLocation 2>&1

start /wait $OptimizerLocation -t $OptimizerTemplate -r $OptimizerLogLocation

"@ | Set-Content $OptimizerbatFile

Write-host "Built Temporary VMware Optimizer .BAT file based on Template $OptimizerTemplate"

    #______________________________________________________________________________________ 

    # Run BAT file to optimize OS

    Start-Process $OptimizerbatFile

    Write-host "Running VMware Optimizer .BAT based on Template $OptimizerTemplate"

    #______________________________________________________________________________________ 

    # Check status of the Optimize and pause

    DoWhile-EQProcName CMD

    #______________________________________________________________________________________ 

    # Remove Temp BAT file

    Remove-Item -Path $OptimizerbatFile

    Write-host "Removing Temporary VMware Optimizer .BAT file"

}

#______________________________________________________________________________________

# Check if VM is managed by SCCM

$CheckSCCM = get-wmiobject win32_Service | Where-Object {$_.Name -eq "CCMexec"} | Select-Object Name

Write-host "Check if SCCM service is installed"

#______________________________________________________________________________________

# If Else statement based on if VM is managed by SCCM

if($CheckSCCM.Name -eq 'CCMexec')

{

    Write-host "VM is managed by SCCM"

    #______________________________________________________________________________________

    # Check status of SCCM Service.

    $SCCMStatus = get-service -name "CCMexec" | Select-Object Status

    Write-host "Check if SCCM service CCMexec is running"

    if($SCCMStatus.Status -eq 'Running')

        {   

            Stop-VDIservices CCMexec

            Write-host "Stop SCCM Service CCMexec"

        }




    #______________________________________________________________________________________

    # Check status of SCCM Service if stopped start.

    $AdaptivaStatus = get-service -name "adaptivaclient" | Select-Object Status

    Write-host "Check if Adaptiva Client is installed"

    if($AdaptivaStatus.Status -eq 'Running')

        {

            Stop-VDIservices adaptivaclient

            Write-host "Stop Adaptiva Services"

        }

}

#______________________________________________________________________________________

# Check status of Windows Update Service.

$WindowsUpdatateStatus = get-service -name wuauserv | Select-Object Name,Status,Starttype

if($WindowsUpdatateStatus.Status -eq 'Running')

    {

        #______________________________________________________________________________________

        # Stop Windows Update Service

        Stop-VDIservices wuauserv

        Write-host "Stop Windows Update Services"

    }

#______________________________________________________________________________________

# Clean out widows downloads cache

if (Test-Path "C:\Windows\SoftwareDistribution\Download\")

{

Remove-Item -Path "C:\Windows\SoftwareDistribution\Download\*" -force -Recurse

Write-host "Clean Out Windows Update Download Cache"

}

#______________________________________________________________________________________

# Check status of VSS Service if stopped start.

$DfragStatus = get-service -name VSS | Select-Object Name,Status,Starttype

if($DfragStatus.Status -eq 'Running')

{

    #______________________________________________________________________________________

    # Clean out Volume Shadow Copys

    .\vssadmin.exe delete shadows /all

    Write-host "Clean out Volume Shadow Copys"

}

#______________________________________________________________________________________

# Clean out Windows PreFetch

if (Test-Path "c:\Windows\Prefetch\")

{

Remove-item -Path "c:\Windows\Prefetch\*.*" -Force

Write-host "Remove windows Prefetch files"

}

#______________________________________________________________________________________

# Check status if AppVolumes is installed and stop the service.

$AppVStatus = get-service | Select-Object Name,Status,Starttype

if($AppVStatus.Name -eq 'svservice')

{

    Stop-Service -Name svservice

    Write-host "Stop AppVolumes Service"

}

#______________________________________________________________________________________

# Clear all event logs

wevtutil el | Foreach-Object {wevtutil cl "$_"}

Write-host "Clear Windows Event Logs"

#______________________________________________________________________________________

# Stop Logging

Stop-Transcript

#______________________________________________________________________________________

# Release IP, Flush DNS and Shutdown VM

ipconfig /release

ipconfig /flushdns

shutdown -s
I will continue to update and optimize this code so check back frequently. If There are any issues found please let me know.
Posted in Blogtober, CLI and Powershell, DevOps, VDI, Virtulization | Tagged , , , , | Leave a comment

Why automate day to day tasks VDI or other?

How many of you have never thought about automating any of the VDI tasks? Or think your environment is too small to automate? Or the best one of them all….I don’t have time to automate!

Slap….Wake up and smell the PowerCLI!

giphy[1]

At one point in time, I believed my environment was too small and I had no time to script. Wow was I wrong! Looking back that was the wrong way to be thinking. In reality, you should be thinking about how to be scripting everything. Even the simple things like create pool, or power on VM. You should really be scripting these for change control. Not only does it force you to do something the same way in a repeatable fashion but, it also creates an automatic record of what you did.

In the early years of my career I would write PowerShell scripts to create users, and groups, folders and stuff in Active Directory. But never dreamed of automating parts of Horizon. I really did not think there was away. Their kind of was but it was more of a hope and pray method a few years back. Not till recent code releases and PowerCLI began to mature did we get to open our wings and do more with it. There is still a ton to be desired with the PowerCLI interface for horizon but its growing quick.

It seems like it was just yesterday I was stumbling around trying to figure out how to make a linked clone pool or make an entitlement change. But really it kind of was. Once I realized how powerful it was to write small bits of PowerShell to create change documents it was so much simpler to document the process and show what you did. Not to mention the time savings as you have either learned how to do the task or you remember where you used it before.

So why write scripts at all? It saves you time, and effort in doing the actual task, not to mention it creates a repeatable process and documents the process for you in the process of writing the task out. And the more you do it the fast your become at pickup and creating new tasks.

My environments too small to automate! That’s BS. If you have to do a task more than once ever than its worth it. If you spend 10 mins figuring out how to create a new VDI pool would it not be worth it to save you 5 mins every time you have to do it from that point forward. Not to mention the above facts of your documentation.

I don’t have time to automate! I was once this person, and still am. But you know what, that’s when you take the time and set it aside, or create a fake meeting to write a few simple task out to free up 10 mins more of your day, and continue this process and create more and more to free up more time so you can take the time and build bigger and more complex tasks to free up more time. In turn, you are automating yourself into having more time to do more automation.

Over the last 8 months or so I have been working on a monster code to automate the update process of a master image for a VDI clone and then the recompose process. Really, it’s not a huge script or extremely complex but for me, it has been a huge learning experience for me. And the process through it has actually created time for me to write more of the code as I was able to use it in stages. The fruits of my labor and passion will, in turn, will save my company 100’s of Thousands of dollars a year in labor. And forced me to learn a ton in the process. More to come on that over the next few weeks.

Posted in Blogtober, CLI and Powershell, VDI | Tagged , , , , | Leave a comment

Blogtober!

So you may laugh but I am writing a blog about blogging. Why you ask? Well because I really need to get over the fear of blogging. I for one am not a huge fan of writing, or the proper English language. I have always just done my own dialect. And it’s hard at times to get office to interpret my version of bad English or really bad English.

So why did someone that hates writing agree to do Blogtober. Well it was more to force myself in a corner. Force me into a place where I would make up excuses on why I can not do it, and really it was me not wanting to write. I mean really that was the reason I created a blog was to force myself to look at it and say you need to get back at this. For me to get anywhere I need to feel uncomfortable and let it go.

One of the hard things for me is coming up with topic that I feel are interesting. I come across the things I am doing at the office and cant really blog about some of them. But I feel everyone is doing this. So its not that interesting. Were in turn you are doing things just a bit different. Maybe enough to help just the one person. To me that makes it worth it. So advice for people that are out there reading this. Get out there get uncomfortable and write down what you are doing. For me this started off with the idea of dumping my Notebooks. But them it kind of changed. I still need to dump my notebooks (Note to self). But I started taking the reviewer idea and opinion. Not sure what its going to evolve, but honestly, I like what its kind of become. Just all over the place with nothing as a niche.

I know this is a short one but its just a start. 🙂 Time to start the real fun. Next blog after Atlanta VMUG UserCon. Setting deadline for myself there.

Posted in Random Crap

VMworld Announcement of AppDefense

I was completely immersed when they announced AppDefense. I had to dig and find out some more. This is an over view of what I have found thus far.

AppDefense is designed to protect your virtual and cloud based applications. Traditional security is Network related. In the past VMware infrastructure, you had to run NSX to do micro segmentation and then use its Guest Introspection driver to use third party security tools to do Anti-Malware, Intrusion Prevention, Web Reputation, Log Inspection, and Integrity Monitoring. Don’t get me wrong this works well. But it only works after the fact. And its not a holistic approach. There are gaps missing. From where NSX gave you the Micro segmentation or leased privileged execution network. now AppDefense give you the ability to deploy the same leased privileged model to the application. This is where AppDefense fits in. AppDefense is a (SaaS) offering from VMware AWS. It can either be deployed in conjunction with NSX, or on its own. I would highly suggest you install side by side as you can not stop end users from clicking on malicious emails.

AppDefense is meant to take a 3 phase approach. Capture, Detect and Respond.

The capture process in AppDefense is brought to use through a engine that Looks directly into vCenter to find out the inventor. It also ties into your Provisioning systems, it also learns from the ESX host on what the VM is doing. Its starts learning the applications and what they do. At this point you have a general understanding of what the application is with some good data points. But this is not it. This is where the great part is. When you set a application owner you can have the security team ask specifics of what an application is doing and why from the metric that are being observed. This decreases the time to market for application and a true sense of what the application is doing.

The Detect in AppDefense uses a protected layer that uses the hosts, monitoring points. It compares what the applications are made of and what they do and need to talk to, to know manifests.  It waits for a change in the operating system, processes, how process talk to each other, how one VM needs to talk to another VM.

The Respond part in AppDefense is the true power of the system. This is where you can automate the response from a detection. You can use ESX to suspend a VM, revert from snapshot. With NSX you can do the fun stuff, like blocking network traffic, run Packet Capture, use the Guest Introspection and leveraging 3rd party tools like Deep Security Manager from Trend Micro to kick off scans, or Log Analysis, quarantine and once found clean throw the machine back into production.

During a session, I could see this somewhat work in a “Live” demo. And to me that sold me on this product. They could stop an application exploit that uses the Applications standard operations. For instance, uses a exploit on a web server using 443 to gain remote shell access on port 443. Doing normal port 443 actions the normal firewall or IDS systems would not have caught this behavior unless they had some analytics of what the applications were doing. And most systems today do not necessarily do this. I may be a bit naive on some of the other products out there but for VMware to step into this market and bring the Security approach to the application layer is a great move by VMware.

It might be a bit of a hurdle to get the Security teams of the world to use it but I think once they see the value of what it has to offer and what the time savings it will have with putting a application to market I think all will be happy.

I am not sure on what the cost is yet on this product but would be interested to find out. I have heard rumors that it may be a per CPU cost. If that is the case it can become expensive quick. So be on the look out for more coming down the pipe on this in the future as more documentation is release.

VMware launches long-anticipated AppDefense - Image

 

More Info Please check out the following.

https://blog.cloud.vmware.com/s/content/a1y6A000000e6lUQAQ/article-vmware-launches-appdefense

https://www.vmware.com/products/appdefense.html

 

 

Posted in Reviews, Security, Virtulization, VMworld | Tagged , ,

The Dirty Taboo topic of IT Debt! The Micro Series…. Maybe if I don’t see a Squirrel!

Why is it no one wants to talk about IT debt? Are they scared of saying hey I need help? Do they not have a clue that they are in debt? Maybe they feel like its job security? Maybe they are the ones putting you into debt?

Over the years I managed to put myself in IT debt, and try to start digging myself out of debt. But what is debt really. The best explanation I could find was from Gartner:

IT Debt is described as “applying a quantifiable measurement to the backlog of incomplete or yet-to-be-stated IT change projects.”

IT debt is no more than the projects that was cut short due to someone saying it works hurry up move on to the next thing. We will go back and fix that later. And little things that are miss configured for testing or troubleshooting also play a toll into this.  Over the years we have all skipped a corner knowing we will all go back and fix it later and here it is two years down the road and still have not done so. I’m not to proud to admit I have done the same thing. We all have if you dig deep. The question did you learn from doing this? I know I did, my shortcut turned out to be a land mine waiting for me to step on it during a upgrade. And that little hour short cut cost twenty plus hours of work to clean up and fix. So why now talk about this. To me it seems like this is what is holding the world back.

 

So, what are the causes of IT debt? If you ask people its due to Budget Cuts.fe2745f64f5f29d7609059e047e687dd Gartner likes to blame it on IT Budgets but I am not a huge fan of that statement. Is it budget cuts? Or is it pure laziness? Or is it being rushed? Or is it ignorance? Or not talking ownership in your service? It believes it’s a mixture of All with Budget Cuts as the start of the chain. I see it working like so:

A project is put together with a projected cost. Things are submitted to a group of people that have no clue about IT and they say, do this for %20 less money and we will sign off. We say okay we will make it happen. Pause Here and think about this!

q514ygWe take this plan back to the team and say here is what we need to get this done, and the timeline of when it needs to happen. The team sets out to acquire what is needed and start setting up what is needed. As the time line and budget is growing to exhaustion, we start cutting corners. Just doing the bare minimum to make it work. And we may skip a page in the install and best practices guides thinking we may know better. Pause Here and think about this!

yoda-hurry-up-you-must

Now we are in the last days of the project we have our manager hounding us to hurry up finish this project. We have other things that we need to get done. Under pressure and reluctantly we say well its ready for production. When really, we know it’s not ready and have another week’s worth of work and tweaking and testing to be done.  Pause Here and think about this!

 

So, what happened in this situation?

First breakout, they start of the chain was the people of the board approving the projects saying we approve only 80% of your cost. Well where does that savings come from? It comes from labor to do the project. Not so much the cost of the asset, as you can only get so much of a discount. This project was doomed from the beginning. Then as good IT people we said yes, we will make this happen, this is another flaw in the chain. Sometimes you need to stand up and say no we can’t do it for this kind of cash. If you are presenting this to the board for approval its your job to keep the IT operations afloat. And running this model will only cause it to sink.

Second Breakout, cutting corners to just make it work. This ends up killing everyone in the long run. Not to mention the ignorance/laziness to properly do the task at hand. This in turn creates a huge amount of what I like to call Land Mines. A little lost change or forgot setting that comes back and blows up in your face at the most in opportune time.

Third Breakout, this is where it all comes together. This is where the pressure from above comes down hard and making sure this project is in budget and on the correct timeline. And us as fearing that there will be reproductions for missing the deadline or going over budget we say yes, we are on time and on budget. But would it really be our fault. We gave them the original numbers to make this happened and the board cut it, and the manager agreed to it. So to me I think it’s the managers fault. Why are we scared? Well it’s the old philosophy that “Shit rolls down Hill” and the stupid blame game. Now it is 100% our fault for saying this project is production ready. As in fact we know that is not. We have knowingly cut corners, not optimized, not properly tested, and so on. This is all signs that the team doing the install wants to take no ownership in it.

 

After this project where does this put us? This gives us a new thing, that works, but is full of so many flaws that is prone to break down. images (1).jpg bucket with holesSo not only did you create X amount of back log due to the short cuts your team took, but now you must fight fires on top of that, and also keep the lights on for the rest of the environment. In turn digging the IT debt hole deeper. P1030026Just to throw a number on it. Really that 20% cost savings the board thought was a great idea has cost them 5 times that. The difference is that they don’t see that cost. No one brings those numbers to them. We do not bring the numbers to them correlating that the 20% they tried to save cost us x dollars this month.

Good news we all start like the pic above. And end up like this…

tumblr_mhk91im5tb1qdj4tho1_r1_500

So how do we solve all this brokenness? I vote Nuke it and start over! To be continued in the next post.

 

Posted in Random Crap

How does all this IT infrastructure stuff actually work?

So looking at this from a high level a day to day job of a admin is basically to keep the lights on and things moving forward. We put in a huge amount of time and effort in making sure what we are doing is done to a level we expect. But for some reason the projects always seem to get blurred, or rushed along. This leads to sub-par work and maybe some check boxes left un-ticked and so on. Eventfully we catch these or go back and fix them later. But there is a landmine there waiting to catch someone because we as admins got distracted. We have all ran across IT landmines some of our own doing and some of our predecessors. It’s always enviable to run across these.

Over the last month or so I have had some time to think and reflect on what is going on. And as I sit and look at what a good portion of my work day is wrapped around I realized its amazing how anything actually works in IT. Think about it…..  and hear me out on this.

When building a Rube Goldberg Machine, you pick what you want to accomplish and what your thyme will be, and what materials you will use. I think this would be the key starting place with this. Then you start to layout the design roughly, research and figure the physics of said design. You spend countless hours researching and documenting what 2016_0914_125032 Panorama_kleinthe plan is. Then it becomes time to build. This is your time to shine with all the research you do and you think you can take on the world. You start laying out the pieces, start putting together the individual parts, have one team work on one section you work on another, another team work on another part and so on. Your teams start testing individual parts of the contraction and finds flaws and adopts as necessary. This is where your design and actual end product begin to differ. Things keep changing, tests keep failing and succeeding. Then finally you get a fully tested individual parts and segments. Its ready for the big moment. Let’s run this thing. This is where all this time and effort is lead up to. You start the first action in the chain in seemingly never-ending events. Things go smooth till the one part. And it fails. You assess the situation and reset make some modifications and go again. This continues in a repetitive motion till you finally get it to succeed all the way through.

o-GOOGLE-DATA-CENTERS-facebookNow compare this story to building up a new Data Center. You build your business requirements, your design, and your hardware choices. And begin formulating the complete build design. You research every best practice documents, check every driver version to make sure its supported and on and on. You order your hardware, wait for it to show. When it does is when the real work begins. You start building out your new data center. One team does storage, one team does the network, one team does the compute and so on. You start testing find some issues make some changes to correct the errors, and continue till each team is satisfied with there part. Then you start to test the whole system. Things fail and you make more changes, and repeat this process a few times.

If you look the things are vastly different on what they are doing, but extremely similar in the build and design process.

So, take your Rube Goldberg Machine that you built. Think of running that same machine billions of times a day on repeat. One single slight blow of wind, or a shake in the floor could through the contraption off and it would fail. In IT you build redundant systems, but that’s really like building two of these things, and trying to keep it running all day every day, 365 days a year. All it takes is one thing that is not correct from a malformed packet, to a wrong SQL query and they whole contraction could go up in flames.These-11-Rube-Goldberg-Machines-Will-Inspire-You-to-Create-Something-Awesome

Also let’s not forget about all the changes you made from the original design. Now sit back and think. Did you document every change? Did you test every setting? Did you…. landmine-imageAnd so on. I am sure there are a few changes in there that were not documented. These are what I like to call Land Mines. These are the little settings that you forgot you changed for some reason or another. Maybe out of frustration because some process was taking to long, like disabling user acknowledgements, or disabling spanning tree. Right now, they might not make a difference, but in the future, they make someone’s day a nightmare.

Think about it a year down the road. And you go to plug in a device to a switch for a redundant link, and all the sudden you create a switching loop. Or you go to do a firmware update and upload it, and then all the sudden all your servers reboot at once. All because you changed one setting a year ago. These are what you call landmines. Lying and waiting for someone to step on them.explosion4_mini

So here lately I have come with the conclusion that IT infrastructure is a Rube Goldberg Machine built on a field of land mines. We are all just waiting on one thing to go wrong and make another change and hope we did not alter the path of something and step on a landmine in the process.

So what is the moral of this story…I really don’t know just thought I should write about it. Maybe Document your changes no matter how small. It could come back and bite you or someone else later down the road. and Make your you understand the full ramifications of your changes before you make them. As it may alter the path of something a few steps down the process chain, in-turn blowing everything up.

Posted in Opinion, Random Crap | Tagged ,