Backing up UCM & How to run a PowerCLI Script with Windows Task Scheduler
At my job, at the time of this writing, I'm the backup sysadmin in charge of our virtual infrastructure (Cisco UCS blades + vSphere 5.1). As my boss, who's the primary for these things, is taking on a new role (same company & still my boss), I'm on track to become primary for these things. As a part of that effort I've been playing catch-up to learn more about vSphere, PowerCLI & Powershell among several other things.
One of my projects as of late has been to use PowerCLI to create a VM-level backup solution for our UCM appliances that don't like to be backed up with our Unitrends Virtual Backup appliances. As a newer VMware Admin, I had no idea how to run PowerCLI scripts. I knew that PowerCLI is basically Powershell, which I know a little bit about. Figuring out how to delete a clone and then creating a clone placed on a specific host & datastore wasn't that hard. Perfecting how to run the script reliably and emailing off the results was more difficult.
First the why. Why am I doing this? This need came from our VM-level backup solution, UVB, interrupting the UCM (Unified Communication Manager) appliances. This meant our phone system going down for 10 minutes every night. For many businesses, having a brief phone outage at 1am is acceptable. We're a university housing department with area offices opened 24 hours so having something as essential as our phone system go down for even 10 minutes isn't acceptable. Why even take a VM-level backup when Cisco already has it's own DRS backup & restore feature? A fair question. The main reason is because restoring the whole VM is faster & easier than DRS. Restoring a borked UCM appliance via DRS takes about 4 hours (depending on the size of your database). Restoring from a UVB backup takes about 40 minutes. Restoring from a clone like we're doing here takes about 5 minutes. We called Unitrends to see if anyone has had this issue with their UCM appliances and Unitrends informed us that they can't help with that because Cisco only supports backups & restores done via DRS.
Now the how. There's 2 ways to run a PowerCLI script in Windows Task Scheduler. First, you can create a task to run your script that loads the PowerCLI Snapin like below.
Secondly, and the way I'm going about it, is to call Powershell with the vim.psc1 console file from PowerCLI with the Windows Task Scheduler. That looks like below.
You might ask "What about permissions?" That's a great question because you still need to login to vSphere before you can do much of anything. You could store the credentials in a variable with Get-Cred but that would mean you'd have a password out there in plain-text which is just lazy. What I've done below is open PowerCLI as the user that will run this backup script, and run the following.
This will create a variable with your credentials. If you want to be lazy about it, that's all you need to do. If you want to do it right, keep going and run
This will create a hashed & salted credentials file you can use in your scripts. In our script you can put something like
In a production environment, you'll want to be using a specially created service account to handle all of this. If you're just playing around in a home lab, a service account isn't necessary. From here, we'll setup some variables for naming our backup clone and for specifying the VM we're cloning.
After that we have the 2 commands that actually do the majority of the magic.
Once that's finished, we want to get information about our new clone and into a variable. This is where things got a bit more hairy.
Now that we've gotten our clone details into a variable, we can email it out to the team to let them know the backup job has completed.
One of my projects as of late has been to use PowerCLI to create a VM-level backup solution for our UCM appliances that don't like to be backed up with our Unitrends Virtual Backup appliances. As a newer VMware Admin, I had no idea how to run PowerCLI scripts. I knew that PowerCLI is basically Powershell, which I know a little bit about. Figuring out how to delete a clone and then creating a clone placed on a specific host & datastore wasn't that hard. Perfecting how to run the script reliably and emailing off the results was more difficult.
First the why. Why am I doing this? This need came from our VM-level backup solution, UVB, interrupting the UCM (Unified Communication Manager) appliances. This meant our phone system going down for 10 minutes every night. For many businesses, having a brief phone outage at 1am is acceptable. We're a university housing department with area offices opened 24 hours so having something as essential as our phone system go down for even 10 minutes isn't acceptable. Why even take a VM-level backup when Cisco already has it's own DRS backup & restore feature? A fair question. The main reason is because restoring the whole VM is faster & easier than DRS. Restoring a borked UCM appliance via DRS takes about 4 hours (depending on the size of your database). Restoring from a UVB backup takes about 40 minutes. Restoring from a clone like we're doing here takes about 5 minutes. We called Unitrends to see if anyone has had this issue with their UCM appliances and Unitrends informed us that they can't help with that because Cisco only supports backups & restores done via DRS.
Now the how. There's 2 ways to run a PowerCLI script in Windows Task Scheduler. First, you can create a task to run your script that loads the PowerCLI Snapin like below.
Add-PSSnapin vmware.vimautomation.core
Secondly, and the way I'm going about it, is to call Powershell with the vim.psc1 console file from PowerCLI with the Windows Task Scheduler. That looks like below.
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -PSConsoleFile "C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\vim.psc1" "& "C:\Scripts\PowerCLI\UCM1Clone.ps1"
You might ask "What about permissions?" That's a great question because you still need to login to vSphere before you can do much of anything. You could store the credentials in a variable with Get-Cred but that would mean you'd have a password out there in plain-text which is just lazy. What I've done below is open PowerCLI as the user that will run this backup script, and run the following.
$cred = Get-Cred
This will create a variable with your credentials. If you want to be lazy about it, that's all you need to do. If you want to do it right, keep going and run
$cred.Password | ConvertFrom-Securestring | Set-Content C:\vSphere\scripts\powerclicred
This will create a hashed & salted credentials file you can use in your scripts. In our script you can put something like
$pwd = Get-Content C:\Scripts\PowerCLI\powerclicred | ConvertTo-SecureString $cred = New-Object System.Management.Automation.PsCredential “domain\username“, $pwd Connect-VIServer yourvcenter.whatever -Credential $cred
In a production environment, you'll want to be using a specially created service account to handle all of this. If you're just playing around in a home lab, a service account isn't necessary. From here, we'll setup some variables for naming our backup clone and for specifying the VM we're cloning.
$hostname = 'vm-you-want-to-backup' $hostnm = get-vm -name $hostname $server = $hostnm[0] $goodbackupname = $hostname + "-Backup" $bugn = $goodbackupname
After that we have the 2 commands that actually do the majority of the magic.
Get-VM -Name $goodbackupname | Remove-VM -DeletePermanently -Confirm:$false
New-VM -Name $goodbackupname -VM $hostname -VMHost specific-host -Datastore specific-datastore
Once that's finished, we want to get information about our new clone and into a variable. This is where things got a bit more hairy.
function Get-VMinventory { function Get-RDMDisk { [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [string[]]$VMName ) $RDMInfo = Get-VM -Name $VMName | Get-HardDisk -DiskType RawPhysical, RawVirtual $Result = foreach ($RDM in $RDMInfo) { "{0}/{1}/{2}/{3}"-f ($RDM.Name), ($RDM.DiskType),($RDM.Filename), ($RDM.ScsiCanonicalName) } $Result -join (", ") } function Get-vNicInfo { [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [string[]]$VMName ) $vNicInfo = Get-VM -Name $VMName | Get-NetworkAdapter $Result = foreach ($vNic in $VnicInfo) { "{0}={1}"-f ($vnic.Name.split("")[2]), ($vNic.Type) } $Result -join (", ") } function Get-InternalHDD { [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [string[]]$VMName ) $VMInfo = Get-VMGuest -VM $VMName # (get-vm $VMName).extensiondata $InternalHDD = $VMInfo.ExtensionData.disk $result = foreach ($vdisk in $InternalHDD) { "{0}={1}GB/{2}GB"-f ($vdisk.DiskPath), ($vdisk.FreeSpace /1GB -as [int]),($vdisk.Capacity /1GB -as [int]) } $result -join (", ") } foreach ($vm in (get-vm -name $bugn)) { $props = @{'VMName'=$vm.Name; 'IP Address'= $vm.Guest.IPAddress[0]; #$VM.ExtensionData.Summary.Guest.IpAddress 'PowerState'= $vm.PowerState; 'Domain Name'= ($vm.ExtensionData.Guest.Hostname -split '\.')[1,2] -join '.'; 'vCPU'= $vm.NumCpu; 'RAM(GB)'= $vm.MemoryGB; 'Total-HDD(GB)'= $vm.ProvisionedSpaceGB -as [int]; 'HDDs(GB)'= ($vm | get-harddisk | select-object -ExpandProperty CapacityGB) -join " + " 'Datastore'= (Get-Datastore -vm $vm) -split ", " -join ", "; 'Partition/Size' = Get-InternalHDD -VMName $vm.Name 'Real-OS'= $vm.guest.OSFullName; 'Setting-OS' = $VM.ExtensionData.summary.config.guestfullname; 'EsxiHost'= $vm.VMHost; 'vCenter Server' = ($vm).ExtensionData.Client.ServiceUrl.Split('/')[2].trimend(":443") 'Hardware Version'= $vm.Version; 'Folder'= $vm.folder; 'MacAddress' = ($vm | Get-NetworkAdapter).MacAddress -join ", "; 'VMX' = $vm.ExtensionData.config.files.VMpathname; 'VMDK' = ($vm | Get-HardDisk).filename -join ", "; 'VMTools Status' = $vm.ExtensionData.Guest.ToolsStatus; 'VMTools Version' = $vm.ExtensionData.Guest.ToolsVersion; 'VMTools Version Status' = $vm.ExtensionData.Guest.ToolsVersionStatus; 'VMTools Running Status' = $vm.ExtensionData.Guest.ToolsRunningStatus; 'SnapShots' = ($vm | get-snapshot).count; 'DataCenter' = $vm | Get-Datacenter; 'vNic' = Get-VNICinfo -VMName $vm.name; 'PortGroup' = ($vm | Get-NetworkAdapter).NetworkName -join ", "; 'RDMs' = Get-RDMDisk -VMName $VM.name #'Department'= ($vm | Get-Annotation)[0].value; #'Environment'= ($vm | Get-Annotation)[1].value; #'Project'= ($vm | Get-Annotation)[2].value; #'Role'= ($vm | Get-Annotation)[3].value; } $obj = New-Object -TypeName PSObject -Property $Props Write-Output $obj | select-object -Property 'VMName', 'IP Address', 'Domain Name', 'Real-OS', 'vCPU', 'RAM(GB)', 'Total-HDD(GB)' ,'HDDs(GB)', 'Datastore', 'Partition/Size', 'Hardware Version', 'PowerState', 'Setting-OS', 'EsxiHost', 'vCenter Server', 'Folder', 'MacAddress', 'VMX', 'VMDK', 'VMTools Status', 'VMTools Version', 'VMTools Version Status', 'VMTools Running Status', 'SnapShots', 'DataCenter', 'vNic', 'PortGroup', 'RDMs' # 'Folder', 'Department', 'Environment' 'Environment' } } $results = Get-VMinventory
Now that we've gotten our clone details into a variable, we can email it out to the team to let them know the backup job has completed.
Clear-Content "C:\Scripts\PowerCLI\results.txt" Out-File -InputObject $results -FilePath "C:\Scripts\PowerCLI\results.txt" $OutputFile = "C:\Scripts\PowerCLI\results.txt" $messageparam = @{ Subject = "This backup job has completed for $hostname" Body = (Get-Content $OutputFile| out-string ) From = "from-address" To = "to-address" SmtpServer = "your-SMTP-server" } send-MailMessage @messageparam #-BodyAsHtml
A big thanks to Magnus Andersson, Alan Renouf & SB for their direct & indirect help.
Comments
Post a Comment