VMware: VM löschen samt DNS, DHCP und Active Directory

Aktuell entwickeln wir eine Automatisierungslösung, die künfig den namen "ADDY" tragen soll, was für "Admins Buddy" steht. Die Lösung soll es ermöglichen Tasks automatisiert nach Vorgabe anzusteuern und auszuführen. 

Ein entscheidender Teil davon ist natürlich das Lifecycle-Management von virtuellen Maschinen. Denn irgendwann ist bei jeder viruellen Maschine die Lebenszeit vorbei und dann muss alles abgebaut bzw. deprovisioniert werden. Häufig vergisst man dabei DNS A Records zu löschen, hinterlässt eine DHCP Reservierung, die dazu beiträgt, dass die DHCP Range immer voller wird und das Computerobject im Active Directory liegt ohne Aufgabe rum.

Der folgende Auszug aus einem Powershell Script (Voraussetzung ist die PowerCLI von VMware) soll es einem ermöglichen, über ADDY VMs und Komponenten wie AD, DNS und DHCP aufzuräumen. Das ausführen des Powershell Snippets erfolgt auf eigene Gefahr. Bitte berücksichtigt, dann das hier nur ein Ausszug aus dem kompletten Workflow ist. Themen wir Zugriff auf Benutzerebene geben wir hier natürlich nicht bekannt.  



#initialize Data
$VMname = "vmtodelete" #this is the VM name in vSphere 
$debugScript = 1 #this will give you some time, to read the outputs


write-host "Cleanup VMware vSphere VM: $VMname"
write-host "Cleanup DNS, DHCP, Active Directory"

if ($debugScript -eq 1) { start-sleep 5 }


#read VM data (Name, IP und MAC ermitteln)
write-host "Checking the credentials. "
######getting the credentials for a different account
#HERE IS THE PART WHERE YOU SHOULD DO SOMETHING TO GET YOUR CREDENTIALS
$vcentercreds = New-Object System.Management.Automation.PSCredential -ArgumentList $credUsername,$credPassword


$VCenterServer = $TYPEINYOURVCENTERINFQDN

try {
    $connectViServer = Connect-VIServer $VCenterServer -Credential $vcentercreds 
    $ErrorMessage = ""
}
catch {
$ErrorMessage = "ErrorMessage: "+$Error[0]

}
finally {
    if ($connectViServer -eq $NULL) {
        write-host "Login to vCenter not successfully" -f red
        write-host "ErrorMessage: $ErrorMessage"
    } else {
        write-host "Login to vCenter successfully" -f green
    }
}

if ($debugScript -eq 1) { start-sleep 5 }



write-host "Get some information about the VM $VMname" -f DarkYellow
$VMelement = Get-VM -name $VMname | Select-Object *, @{N="IP Address";E={@($_.guest.IPAddress[0])}},@{N="DnsName"; E={$_.ExtensionData.Guest.Hostname}}

$vmNetAdapterData = Get-NetworkAdapter -vm $VMname 

$tryToGetIPviaDNS = $false
if ($VMelement.PowerState -eq "PoweredOff") {
    write-host "The Machine is offline. Cannot fetch the IP Address from VM data"
    $tryToGetIPviaDNS = $true
} else {
    write-host "IP: $($VMelement.'IP Address')" -f Yellow
    if ($($VMelement.'IP Address') -as [IPAddress]  -as [Bool]) {
        write-host "IP Address is valid"
        write-host "Saving IP Address to variable"
        $VMipAddress = $($VMelement.'IP Address')

        write-host "DNS Name: $($VMelement.DnsName)"
    } else {
        write-host "IP Address is not valid. "
    }
}


write-host "MAC-Address: $($vmNetAdapterData.MacAddress)" -f DarkYellow
write-host "NetworkName: $($vmNetAdapterData.NetworkName)" -f DarkYellow
write-host "NumCpu: $($VMelement.NumCpu)" -f DarkYellow
write-host "MemoryGB: $($VMelement.MemoryGB)" -f DarkYellow
write-host "HardDisks Count: $($VMelement.HardDisks.Count)" -f DarkYellow


if ($tryToGetIPviaDNS -eq $true) {
    write-host "Trying to get the IP Address via DNS"

    $DNSresolveRecord = Resolve-DnsName -Name $VMname -ErrorAction 0 #$VMname

    if ($DNSresolveRecord) {
        write-host "DNS exists"
        $VMipAddress = $DNSresolveRecord.IPAddress
        $DeleteDNSRecord = $true
    } else {
        write-host "DNS Resolve failed"
        $DeleteDNSRecord = $false
    }

}




if ($debugScript -eq 1) { start-sleep 5 }

#shutdown the vm if the VM is up
if ($VMelement.PowerState -eq "PoweredOn") {
    write-host "The VM is running. I will shutdown the VM..."

    Get-VM -name $VMname | Shutdown-VMGuest -Confirm:$false

    $maxTries = 20
    for ($i = 1; $i -lt $maxTries; $i++) { 
        write-host "Checking VM status... "
        if ($(Get-VM -name $VMname).PowerState -eq "PoweredOff") {
            write-host "VM powered off"
            $i = $maxTries
        } else {
            write-host "VM is not powered off yet"
            write-host "current VM power state: $($(Get-VM -name $VMname).PowerState)"
            start-sleep 5
        }

    }




    #check, if the machine is really down
    if ($(Get-VM -name $VMname).PowerState -ne "PoweredOff") {
        write-host "the $VMname is not powered off. " -f red 
        write-host "PowerState: $($(Get-VM -name $VMname).PowerState)" -f red
        start-sleep 2
        write-host "If there is a human how read THIS... You have now 50 seconds to shutdown the VM with your stuff. Why? I should delete only VMs with the power state off." -f darkyellow
        start-sleep 50
    }


    if ($(Get-VM -name $VMname).PowerState -ne "PoweredOff") {
        write-host "The VM $VMname has already an power state unlike PoweredOff." -f red 
        write-host "PowerState: $($(Get-VM -name $VMname).PowerState)" -f red
        write-host "I will abort the next steps"
        $errorCount++
    } else {
        write-host "The VM $VMname is powered off right now. Puh." -f green
    }
} 

if ($VMelement.PowerState -eq "PoweredOff") {
    write-host "The VM is already down. Go ahead."
}

if ($debugScript -eq 1) { start-sleep 5 }






###################################################### START: DNS DELETE A RECORD AND PTR  #################################################

#DNS - Remove DNS A Record
if ($DeleteDNSRecord -eq $true) {
    write-host "Remove the DNS Record"
    write-host "IPAddress: $($VMipAddress )"

    write-host "Checking credentials."
    ######getting the credentials for a different account
    #HERE IS THE PART WHERE YOU SHOULD DO SOMETHING TO GET YOUR CREDENTIALS
    $dnscreds = New-Object System.Management.Automation.PSCredential -ArgumentList $credUsername,$credPassword


    if ($debugScript -eq 1) { start-sleep 5 }


    $resultInvoke = Invoke-Command -scriptblock { 
        param($VMname,$VMipAddress)
        $DNSerrorCount = 0; $returnArray = @();
        $RecordName = $VMname
        $DNSServer = $PUTHEREYOURDNSSERVERFQDN
        $ZoneName = $PUTHEREYOURZONE
        $errorMessages = ""

        $NodeARecord = Get-DnsServerResourceRecord -ZoneName $ZoneName -ComputerName $DNSServer -Name $RecordName -ErrorAction SilentlyContinue

        if ($NodeARecord.RecordData.IPv4Address.IPAddressToString -eq $VMipAddress) {
            write-Output "IP Check successfully"
        } else {
            write-Output "IP Check not successfully"
            $errorMessages += "IP Check not successfully" #further with `n
            $DNSerrorCount++
        }

        if ($DNSerrorCount -eq 0) {
            if($NodeARecord){
                Remove-DnsServerResourceRecord -ZoneName $ZoneName -ComputerName $DNSServer -InputObject $NodeARecord -Force #-whatif:$bTest
                Write-Output "A record deleted: $($NodeARecord.HostName)"
            } else {
                write-Output "No record found"
            }
        } else {
            write-output "Some errors occured. I dont delete the A record"
        }

        #And now the PTR
        if ($DNSerrorCount -eq 0) {

            if ($NodeARecord){
                $IPAddress = $NodeARecord.RecordData.IPv4Address.IPAddressToString
                $IPAddressArray = $IPAddress.Split(".")
                $IPAddressFormatted = ($IPAddressArray[3]+"."+$IPAddressArray[2])
                $ZonePrefix = ($IPAddressArray[1]+"."+$IPAddressArray[0])
                $ReverseZoneName = "$ZonePrefix`.in-addr.arpa"

                $NodePTRRecord = Get-DnsServerResourceRecord -ZoneName $ReverseZoneName -ComputerName $DNSServer -Node $IPAddressFormatted -RRType Ptr -ErrorAction SilentlyContinue
                if($NodePTRRecord -eq $null){
                    Write-Output "No PTR record found"
                } else {
                    Remove-DnsServerResourceRecord -ZoneName $ReverseZoneName -ComputerName $DNSServer -InputObject $NodePTRRecord -Force #-WhatIf:$bTest
                    Write-Output "PTR Record Deleted: $($IPAddressFormatted)"
                    write-Output "Reverse Zone: $ReverseZoneName"
                }

            } else {
                Write-Output "No record for $RecordName found at $DNSServer"
            }
        } else {
            write-output "Some errors occured. I dont delete the PTR Record"
        }
        #return the errors
        $returnArray = @{
            'errorMessages' = $errorMessages
            'DNSerrorCount' = $DNSerrorCount
        }


        return $returnArray


    } -ArgumentList @($VMname,$VMipAddress) -ComputerName $DNSServer -Credential $dnscreds # -Session $DNSpssession
    
    
    if ($resultInvoke.DNSerrorCount -eq 0) {
        write-host "DNS Record (A Record and PTR Record) cleanup successfully"
    } else {
        write-host "DNS not successfully. $($resultInvoke.errorMessages)"
    }

    if ($debugScript -eq 1) { start-sleep 10 }

}
###################################################### END: DNS DELETE A RECORD AND PTR  #################################################







###################################################### START: AD DELETE COMPUTER OBJECT  #################################################

#deleting computerobject if exists

    write-host "Cleanup Active Directory"
    if ($debugScript -eq 1) { start-sleep 5 }

    $ADcomputerObject = $NULL
    try{
        $ADcomputerObject = Get-ADComputer $VMname -ErrorAction Stop
        write-host "Computer object exists"
    }
    catch{
        write-host "Computer object does not exists"
    }

    if ($ADcomputerObject -ne $NULL) {
        write-host "The following object will be deleted from the Active Directory" 
        write-host $ADcomputerObject
        start-sleep 5

        if ($ADcomputerObject.DNSHostName -like "$($VMname).*") {
            write-host "The Computerobject DNShostname contains the Hostname of the VM"
        } else {
            write-host "The Computerobject DNS Hostname is not like the VM Hostname. Thats not good"
            $errorCount++
        }


        if ($errorCount -eq 0) {
            write-host "The computer object will be delete in 3 seconds"
            $ADcomputerObject
            if ($debugScript -eq 1) { start-sleep 8 }
            Remove-ADComputer -Identity $ADcomputerObject -Credential $dnscreds -Confirm:$false
            write-host "... delete done"
        }

    } else {
        write-host "There is nothing to delete"

    }
    if ($debugScript -eq 1) { start-sleep 5 }
###################################################### END:  AD DELETE COMPUTER OBJECT #################################################










###################################################### START:  DHCP DELETE RESERVATION #################################################
    write-host "Cleanup DHCP reservation"
    write-host "Checking credentials."
    ######getting the credentials for a different account
    #HERE IS THE PART WHERE YOU SHOULD DO SOMETHING TO GET YOUR CREDENTIALS
    $dhcpcreds = New-Object System.Management.Automation.PSCredential -ArgumentList $credUsername,$credPassword

    if ($debugScript -eq 1) { start-sleep 5 }


    $dhcpErrorCount = 0
    $pgVMvlan = $vmNetAdapterData.NetworkName
    
    $VMvlan = #DO HERE SOME THINGS TO GET YOUR VLAN#
    $DHCPscopeArray = Invoke-Command -scriptblock {
        Get-DhcpServerv4Scope  -ComputerName $YOURDHCPSERVERFQDN
    } -ComputerName $YOURDHCPSERVERFQDN -Credential $dhcpcreds

    $DHCPscopeElement = $DHCPscopeArray | Where-Object {$_.Name -eq $VMvlan}
    if ($debugScript -eq 1) { start-sleep 5 }
    if ($DHCPscopeElement -eq $NULL) {
        write-host "There is no DHCP-Scope with the name $VMvlan" -f Red
        $dhcpErrorCount++;
    } else {
        write-host "DHCP-Scope $VMvlan found" -f darkyellow
        write-host "Name: $($DHCPscopeElement.Name)" -f DarkYellow
        write-host "ScopeId: $($DHCPscopeElement.ScopeId)" -f DarkYellow
        write-host "SubnetMask: $($DHCPscopeElement.SubnetMask)" -f DarkYellow
        write-host "State: $($DHCPscopeElement.State)" -f DarkYellow
        write-host "StartRange: $($DHCPscopeElement.StartRange)" -f DarkYellow
        write-host "EndRange: $($DHCPscopeElement.ScopeId)" -f DarkYellow
        write-host "ScopeId: $($DHCPscopeElement.EndRange)" -f DarkYellow
        write-host "LeaseDuration: $($DHCPscopeElement.LeaseDuration)" -f DarkYellow
    }
    if ($debugScript -eq 1) { start-sleep 5 }
    if ($dhcpErrorCount -eq 0) {
        $scopeId = $DHCPscopeElement.ScopeId
        write-host "ScopeId identified: $scopeId "
    } else {
        write-host "Some errors occured when I taking some DHCP actions..."
    }




    if ($debugScript -eq 1) { start-sleep 10 }
    #go ahead and check the reservation
    write-host "I will now check, if there is an reservation" -f DarkYellow
    $DHCPgetReservationResult = Invoke-Command -scriptblock { 
        param($VMipAddress,$scopeId)
        $currentDHCPreservationData = Get-DhcpServerv4Lease -ComputerName $YOURDHCPSERVERFQDN -IPAddress $VMipAddress -ErrorAction SilentlyContinue
        $currentDHCPreservationData #send the return
    }  -ComputerName "$YOURDHCPSERVERFQDN" -Credential $dhcpcreds -ArgumentList @($VMipAddress,$scopeId) 



    write-host "Checking the DHCP result" -f DarkYellow
    if ($DHCPgetReservationResult -eq $NULL) {
        write-host "There is no DHCP Reservation or Lease for this IP" -f DarkYellow
        start-sleep 3
    } else {

        write-host "There is a result" -f DarkYellow
        start-sleep 1
        $DHCPgetReservationResult
        if ($debugScript -eq 1) { start-sleep 5 }
        

        write-host "Checking if the hostname sounds equal."
        if ($DHCPgetReservationResult.HostName -like "*$VMname*") {
            write-host "The hostname from the DHCP reservation is ($($DHCPgetReservationResult.HostName)) and contains the hostname from the VM ($VMname)" -f DarkYellow
        } else {
            write-host "the hostname from the DHCP reservation is ($($DHCPgetReservationResult.Hostname)) and DO NOT contains the hostname from the VM ($VMname)" -f Red
            #send a warning to ADDY
        }

        if ($debugScript -eq 1) { start-sleep 5 }


        write-host "Checking if the MAC-Address is the same (DHCP Reservation to VM) ." -f DarkYellow
     
        #convert the address to check
        # MAC Address 
        $dhcpreservationMacAddress = $DHCPgetReservationResult.ClientId
 
        # Delimiter in the current MAC address 
        $DhcpReservationDelimiter = "-" 

        $dhcpreservationMacAddress = $dhcpreservationMacAddress -replace "$DhcpReservationDelimiter","" #$vmMacAddress = $vmMacAddress -replace "$Delimiter",""
        Write-Host "DHCP Reservation MAC Address:  "$dhcpreservationMacAddress -f DarkYellow

        #convert the MAC address vom the VM
        $Delimiter = ":" # Delimiter in the current MAC address 
        $vmMacAddress = $vmNetAdapterData.MacAddress -replace "$Delimiter","" #$vmMacAddress = $vmMacAddress -replace "$Delimiter",""
        Write-Host "VMware MAC Address:  "$vmMacAddress -f DarkYellow

        if ($debugScript -eq 1) { start-sleep 5 }

        if ($dhcpreservationMacAddress -eq $vmMacAddress) {
            write-host "The MAC Adresses are equal. " -f DarkYellow
           
            $DHCPdeleteresult = Invoke-Command -scriptblock { 
                param($VMipAddress)
                    $returnRemove = Remove-DhcpServerv4Reservation -ComputerName $YOURDHCPSERVERFQDN -IPAddress $VMipAddress
                    $returnRemove
                } -ComputerName "$YOURDHCPSERVERFQDN" -Credential $dhcpcreds -ArgumentList @($VMipAddress) 
                $DHCPdeleteresult
                write-host "DHCP Reservation delete successfully"

        } else {
            write-host "The MAC Addresses are not the same. I will not delete anything on the DHCP. Please do it manually"
            #send a warning to ADDY
        }

    
    }
    if ($debugScript -eq 1) { start-sleep 5 }
###################################################### END:  DHCP DELETE RESERVATION #################################################







###################################################### START:  VMWARE DELETE VM #################################################
if ($errorCount -eq 0) {
    write-host "Removing VM from vSphere."
    Remove-VM -vm $VMname -DeletePermanently -Confirm:$false
}
###################################################### END:  VMWARE DELETE VM #################################################