Wednesday, November 29, 2017

VMware Datastore Tracker

I used an old script by Hugo Peeters to create this script as an "idiot light" for VMware SAN volumes.  It tracks them over time, compares to the previous one and stores the data for future processing.  I have another script that can ingest the data to produce graphs of data use over months.

The script was recently updated to support the new v6 commandlets.  It produces an nice clean HTML email that has gains color coded green, losses color coded red, and the percentage free in a color scale from dark green to red.

Again, the script was produced from numerous sources.  Here is a sanitized example of the email message generated by the script.  The color change in the "vCenter" column indicates data from 2 seperate vCenter instances.  Note that the vCenter instance names are hard coded half way down (I got lazy...) so they need to be edited in the config file and script.

My scripts are all on the MS PowerShell Gallery now so that I'm not editing these posts with each version update.   You can find this script, named "Datastore-Tracker.ps1", at the main library link of: https://www.powershellgallery.com/profiles/Kcmjr/

Edit as needed for your environment before running.



Detect current PDC and notify if it changes.

I've harped on Windows Time before and I assume it will plague me until the day I die.  We (once again) are seeing issues with time sync in our domain.  I "think" I finally got things stable but in the interim I wanted to keep an active eye on our PDC so I know if it moved to a different domain controller. 

I created the below script to do that for me.   I set a recurring scheduled task that fires every 15 minutes on my task engine server.   The script uses a fixed PDC but could be tweaked to track it if it moves.  I simply wanted an "idiot light" that annoyed me if the PDC wasn't where I expected it to be.  The first 4 alerts come as detected, then it backs off to once every hour.

Edit it as needed before running.

Param(
      [Switch]$Console = $false                            #--[ Set to true to enable local console result display. Defaults to false ]--
)

<#==============================================================================
         File Name : Get-PDC.ps1
   Original Author : Kenneth C. Mazie (kcmjr AT kcmjr.com)
                   :
       Description : The domain PDC is determined using DNS SRV records and is then
                   : compared to a preset.  If they don't match an email is sent.
                   :
             Notes : Normal operation is with no command line options.  Run from a
                   : recurring scheduled task every 15 minutes or as desired.  Outbound
                   : email messages are sent every 15 minutes first hour, then once an hour
                   : until the expected PDC is restored or the script is edited.
                   :
         Arguments : Command line options for testing:
                   : - "-console $true" will enable local console echo
                   :
          Warnings : None
                   :  
             Legal : Public Domain. Modify and redistribute freely. No rights reserved.
                   : SCRIPT PROVIDED "AS IS" WITHOUT WARRANTIES OR GUARANTEES OF
                   : ANY KIND. USE AT YOUR OWN RISK. NO TECHNICAL SUPPORT PROVIDED.
                   :
           Credits : Code snippets and/or ideas came from many sources including but
                   :   not limited to the following:
                   :
    Last Update by : Kenneth C. Mazie    
   Version History : v1.0 - 10-26-17 - Original
    Change History : v1.1 - 11-13-17 - Edited console output.  Changed count method.
                   : v1.2 - 11-27-17 - Added AD module import
                   : v1.3 - 11-18-17 - Changed PDC detection method.
                           #>               
                   $CurrentVersion = 1.3                             <#--[ Denotes current version
                   :
==============================================================================#>
#Requires -version 4

Clear-Host
Import-Module ActiveDirectory
$Script:Recipient = "me@mydomain.com"                                 #--[ Email recipient address goes here ]--
$Script:Subject = 'PDC Change Detected'
$Script:MailServer = '10.10.5.5'                                      #--[ Mail server IP goes here ]--
$Script:MailFrom = 'Powershell@mydomain.com'                          #--[ Email sender goes here ]--
$ScriptName = ($MyInvocation.MyCommand.Name).split(".")[0]
$Script:LogFile = $PSScriptRoot+"\"+$ScriptName+"_{0:MM-dd-yyyy_HHmmss}.log" -f (Get-Date)
$Script:FlagFile = "$PSScriptRoot\$ScriptName.flag"
$Domain = ""
$Count = 0
$ExpectedName = "dc01"                                                #--[ Expected PDC name goes here ]--
$ExpectedIP = "10.10.5.100"                                           #--[ Expected PDC IP goes here ]--

#--[ Get Domain ]-------------------------------------------------------------

While ($Domain -eq ""){
      $Domain = (Get-ADDomain).DNSroot
      Sleep -millisec 500
      $Count++
}

#--[ Read data file to determine what to do ]---------------------------------
If (Test-Path -Path $Script:FlagFile -PathType Leaf -ErrorAction SilentlyContinue){
    $Script:ErrorCount = [int](Get-Content -Path $Script:FlagFile  -ErrorAction SilentlyContinue)
 }Else{
    $Script:ErrorCount = 0
    Add-Content $Script:FlagFile -Value $Script:ErrorCount
}

#[array]$Lookup = (nslookup -type=SRV _ldap._tcp.pdc._msdcs.$Domain)
#$PDCname = ($Lookup[8].split('.'))[0]
#$PDCip = (($Lookup[8].split('='))[1]).trim()

$FSMO = Get-ADDomain | Select-Object DistinguishedName, SchemaMaster, DomainNamingMaster, InfrastructureMaster, PDCEmulator, RIDMaster 
$DetectedName = ($FSMO.PDCEmulator).Split(".")[0]
$DetectedIP = (Test-Connection $PDC -count 1 | select Ipv4Address).Ipv4Address

If ($Console){
    Write-Host "Previous Errors  :"$Script:ErrorCount -ForegroundColor Yellow 
    Write-Host "Expected PDC     : $ExpectedName" -ForegroundColor cyan
    Write-Host "Expected IP      : $ExpectedIP" -ForegroundColor cyan
    Write-Host "Detected PDC     : $DetectedName" -ForegroundColor yellow
    write-host "Detected IP      : $DetectedIP" -ForegroundColor yellow
}   

$Script:Message = @()
$Script:Message += '<html><br><font face="verdana,arial,sans-serif" color=red><strong>---===¦ ATTENTION ¦===---</strong></font><hr><br>
<strong>A change was detected in the normally assigned domain PDC.</strong><br>
<ul><li>Domain name           : <strong>'+$Domain+'</strong></li></ul>
<ul>
<li>Expected PDC DNS name : <strong>'+$ExpectedName+'</strong></li>
<li>Expected PDC IP       : <strong>'+$ExpectedIP+'</strong></li>
</ul><ul>
<li>Detected PDC DNS name : <strong>'+$DetectedName+'</strong></li>
<li>Detected PDC IP       : <strong>'+$DetectedIP+'</strong></li>
</ul>
- This check is run every 15 minutes to detect the domain PDC.<br
- Due to the way time services are configured in the domain, changes to which domain controller holds the role can affect time sync.<br>
- If this change was intentional, no harm done, however this alert will continue until stopped or the proper DC is selected/detected as PDC.<br>
- To minimize email spam, after the first 4 alerts emails will go out once per hour.<br>
- Script executed from and must be paused on : '+$env:computername+'
'

If (($PDCname -eq $TargetName) -and ($PDCip -eq $TargetIP)){
    remove-item -Path $Script:FlagFile -Force -Confirm:$false
    Add-Content $Script:FlagFile -Value "0"
    If ($Console){Write-Host "`nValues Match.  Resetting Error Count"`n -ForegroundColor Green }
}Else{   
    remove-item -Path $Script:FlagFile -Force -Confirm:$false
    Add-Content $Script:FlagFile -Value (($Script:ErrorCount -as [int]) + 1)
    $Script:Message += '
    <br>Running error count $Script:ErrorCount
    </HTML>
    '
    IF (($Script:ErrorCount -le 4) -or (!($Script:ErrorCount%4))){
        $SMTP = new-object System.Net.Mail.SmtpClient($Script:MailServer)
        $Email = New-Object System.Net.Mail.MailMessage
          $Email.Body = $Script:Message
          $Email.IsBodyHtml = $true
          $Email.To.Add($Script:Recipient)
          $Email.From = $Script:MailFrom
          $Email.Subject = $Script:Subject
        $SMTP.Send($Email)
          $Email.Dispose()
          $SMTP.Dispose()     
        $Script:ErrorCount = [int](Get-Content -Path $Script:FlagFile  -ErrorAction SilentlyContinue)
        If ($Console){Write-Host "New Error Count  :"$Script:ErrorCount`n -ForegroundColor Yellow}
    }
}



Tuesday, November 21, 2017

Write user session data to AD

Ever wanted to be able to identify the computer a user is currently on?  Me too.  This small script can be run as a GPO logon script.  It reads the session environment variables and writes them to both the AD computer record and AD user record so you can easily find what PC the user is on.

Originally I used VB scripts for this but we're trying to move away from using those.  Also I had a similar logoff script to clear out the records on the AD computer at logoff.  that way you can see who is actively logged on to any PC.

In this case no external XML config file is used so the encrypted service user data in kept in the script.

Param(
      [switch]$Console = $False
      )
<#======================================================================================
         File Name : SessionInfo.ps1
   Original Author : Kenneth C. Mazie (kcmjr AT kcmjr.com)
                   :
       Description : Run as a GPO based logon script.  Writes user info to computer record and
                   : computer info to user record in AD.
                   :
         Operation : Requires PowerShell AD module.
                   : Looks for any text file in the same folder as the script.  If found it loads the
                   : list of IP addresses or system names and cycles through them.  It then renames
                   : test file to avoid redetection.  Can alternately enumerate a domain.  Original script
                   : used REG.EXE for all operations, this proved unreliable so the writes were switched to
                   : using .NET functions.  HTML logs are written to the script folder.  Only
                   : the previous 10 logs are retained.
                   :
         Arguments : Normal operation is with no command line options.
                   : -console $true : Displays status output to console - defaults to $false
                   :
          Warnings : None
                   :  
             Legal : Public Domain. Modify and redistribute freely. No rights reserved.
                   : SCRIPT PROVIDED "AS IS" WITHOUT WARRANTIES OR GUARANTEES OF
                   : ANY KIND. USE AT YOUR OWN RISK. NO TECHNICAL SUPPORT PROVIDED.
                   :
           Credits : Code snippets and/or ideas came from many sources including but
                   :   not limited to the following:
                   :
    Last Update by : Kenneth C. Mazie
   Version History : v1.0 - 11-13-17 - Original
    Change History : v2.0 - 00-00-00 -
                   :
=======================================================================================#>

Clear-Host
$DN = (Get-ADDomain).DNSroot
$EPW = '7649AYQBhAAAyADUAYQA2AGQA2d111GQAZANgA0zAGYANGIATgBaA/AWnCvLO+EeDcAYwBtAHAAWQB6AHoAZgBiAGMAYQBhAGEZAA2ADQAYwBkADYAZQBmAGQAOAA0ADEANgBiADAAZgBkAGYAZAA='
$BA = [System.Convert]::FromBase64String('kdhCh7AL+Ebie8674NwBkADEANAA4mE=')
$SC = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList '$DN\serviceaccount', ($EPW | ConvertTo-SecureString -Key $BA)
$ThisComputer = $Env:ComputerName
$ThisUser = (Get-Aduser $ENV:UserName).Name
$ThisUserSAM = (Get-Aduser $ENV:UserName).sAMAccountName

If ($Console){
    Write-host "This User Name :"$ThisUser
    Write-host "This User SAM  :"$ThisUserSAM
    Write-host "This Computer  :"$ThisComputer
}

Try{
      Set-ADUSer -Identity $ENV:UserName -Replace @{wWWHomePage=$Env:ComputerName;LogonWorkstation=$Env:ComputerName} -ErrorAction Stop -Credential $SC
      Get-ADComputer -Filter 'Name -like $ThisComputer' -Properties * | % {Set-ADComputer $Env:ComputerName -ManagedBy $ThisUserSAM -ErrorAction Stop -Credential $SC}
    #Get-ADComputer -Filter 'Name -like $ThisComputer' -Properties * | % {Set-ADComputer $Env:ComputerName -ManagedBy $ThisUserSAM -Description $ThisUser -ErrorAction Stop -Credential $SC}
}Catch{
      $ErrorMessage = $_.Exception.Message
      $FailedItem = $_.Exception.ItemName
    If ($Console){
    Write-Host "Error Message :"$ErrorMessage
      Write-host "Failed Item   :"$FailedItem
    }
}