Tag Archives: Powershell

Scheduling Veeam Backup Free Edition backups

veeamlogoAs you might be aware Veeam has released Update 2 for it’s Backup and Replication software.  With that comes a slew of updates, integration with Endpoint Backup, vSphere 6 support, features, enhancements, bug fixes – you know the usual suspects that you might find inside of an update pack – you can see them all in the release notes here.  Speaking of release notes – it’s always a good idea to read completely through them before even considering an upgrade – not just to find any known problems or gotchya’s, but at times, mostly all the time you will find a feature or change to the product that isn’t marketed and publisized as much as the rest.  Now Veeam B&R update 2 is largely about Endpoint Backup integration and support for vSphere 6.0 –which is awesome – but as I was doing my once over of the release notes I noticed this….

veeamfree

Veeam has a long history of releasing so-called Freemium products – giving a way a scaled back portion of their complete solution absolutely free, while offering a paid license for those looking for enterprise features.  Veeam Backup Free Edition is exactly this – allowing administrators to create full backups of their VMs using VeeamZip technologies – absolutely free.

The one caveat to this was you were never able to schedule your VeeamZips – so creating a backup was something that had to be manually triggered.  I’m sure many of you (as have I) have tried – only to see the infamous “License is not installed” message when running the Start-VBRZip PowerShell cmdlet.  Well, as of update 2 you can kiss that message goodbye and begin scheduling that cmdlet to your hearts delight.

Start-VBRZip

This is a relatively easy process but in the interest of completeness let’s go over it anyways.  First up we need to create a PowerShell script that will execute the Start-VBRZip cmdlet, which inturn VeeamZips our VM.  The script I used is below…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Param(
  [Parameter(Mandatory=$true)][string]$VM,
  [Parameter(Mandatory=$true)][string]$Destination,
  [Parameter(Mandatory=$true)][ValidateSet(0,4,5,6,9)][int]$Compression,
  [bool]$DisableQuiesce=$true,
  [Parameter(Mandatory=$true)][ValidateSet("Never","Tonight","TomorrowNight","In3days","In1Week","In2Weeks","In1Month")][string]$Autodelete
)
#Load Veeam Toolkit
& "C:\Program Files\Veeam\Backup and Replication\Backup\Initialize-VeeamToolkit.ps1"
#Validate any parameters
$vmentity = Find-VBRViEntity -Name $VM 
if ($vmentity -eq $null)
{
  Write-Host "VM: $VM not found" -ForegroundColor "red"
  exit
}
if (-Not (Test-Path $Destination))
{
  Write-Host "Destination: $vmname not valid" -ForegroundColor "red"
  exit
}
if ($DisableQuiesce -eq $true)
{
    Start-VBRZip -Entity $vmentity -Folder $destination -Compression $Compression -AutoDelete $Autodelete -DisableQuiesce
}
else
{
    Start-VBRZip -Entity $vmentity -Folder $destination -Compression $Compression -AutoDelete $Autodelete
}

A couple things about the script – you can see that it takes 5 parameters; the VM to backup, the destination to back it up to, the level of compressions to apply, whether or not to queiesce the VM and the auto-delete policy to apply to the backup.  From there we simply load the Veeam toolkit, do a little error checking and then initiate the backup with Start-VBRZip.  Pretty simple stuff – you can go ahead and try it by saving the script and calling it like so…

VeeamZip.ps1 –VM “VM1” –Destination “E:\backups” –AutoDelete “Never” –Compression 5 –DisableQuiesce $false

Scheduling the script

scheduledtaskPick your poison when it comes to scheduling this script to run – I’ve chose the standard Windows Task Scheduler to do the job. So go ahead and create a scheduled task with whatever schedule you like within Windows –  The only really tricky part is passing the arguments to the script – the way I have done it is by selecting ‘Start a program’ as my action, passing the path to PowerShell.exe in my program script, then enclosing my string arguments in single quotes, and the complete arguments string in double quotes like below

Program/script: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

Add arguments: “c:\VeeamZip.ps1 –VM ‘VM1’ –Destination ‘E:\backups’ –AutoDelete ‘Never’ –Compression 5 –DisableQuiesce $false”

From there it’s a matter of creating as many scheduled tasks as you have VMs you want backed up, or modifying the script to backup all your VMs – Either way, as you can see, the Veeam Backup Free edition has received a nice little feature buried within the Update 2 release notes!!!!

Migrating workflows from one vCO server to another!

Although vCenter Orchestrator does offer an upgrade path for their appliances there are times where I have found myself needing to deploy a new one and migrate all of my configuration and workflows to it.  vCO has some great built in workflows that can configure itself, but nothing that really deals with workflows.  Sure, you can export and import workflows one at time using the client, which may be ok if you have 10 workflows, but what if you have 20, 40, 100 that you need to migrate.  That could get pretty monotonous.

The shear energy of a mouse click.

That’s right – who wants to exert all that energy of clicking on the mouse to get these workflows migrated when we can just orchestrate or automate it – after all, this is vCenter Orchestrator we are talking about.  vCO has a REST plugin that allows us to create workflows around any application that offers one, but did you know that vCO also has it’s own REST API available for us to use?  So that’s where I started with my task of migrating workflows and by the time I got to writing this post it truly ended up being a community effort.

“Steal from the best”

This was a quote that I seen on one of Alan Renouf’s slides during a VMworld presentation on PowerCLI.  “Steal from the best”, “Don’t re-invent the wheel” – sayings that have resonated with me for my entire career – Why re-do something if it has already been done.  So when I set out on this small project I ended up using two main sources; This post by William Lam on VirtuallyGhetto on how to use curl to export a single vCO workflow and this forum reply by igaydajiev who “showed me the light” on how to import a workflow back in!  Without these two guys I wouldn’t have been able to do any of this.

Enough already let’s get to it!

So I chose to go the PowerShell route to accomplish this as I’m not too familiar with the REST plugin for vCO.  As well, I am targeting only a certain category – so basically what the script does is take in the following parameters

  • OldvCOServer – the old vCO appliance
  • OldvCOUser/OldvCOPass – credentials for the old appliance
  • OldvCOCategory – Category Name to export workflows from
  • TempFolder – Location to store the exported workflows
  • NewvCOServer – The new vCO appliance
  • NewvCOUser/NewvCOPass – credentials for the new appliance
  • NewvCOCategory – Category name on new server where you would like the worfkflows imported.

As far as an explanation I’ll just let you follow the code and figure it out.  It’s basically broke into two different sections; the export and the import.  During the import routine there is a little bit of crazy wonky code that gets the ID of the targeted category.  This is the only way I could figure out how to get it and I’m sure there is a way more efficient way of doing so, but for now, this will have to do.  Anyways, the script is shown below and is downloadable here.  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
Param(
  [string]$oldvCOServer="localhost",
  [string]$oldvCOUser="vcoadmin",
  [string]$oldvCOPass="vcoadmin",
  [string]$oldvCOCategory="MyOldWorkflows",
  [string]$newvCOServer="localhost",
  [string]$newvCOUser="vcoadmin",
  [string]$newvCOPass="vcoadmin",
  [string]$newvCOCategory="MyWorkflows",
  [string]$TempFolder
)
 
# vCO Port
$vcoPort="8281"
 
# Type to handle self-signed certificates
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
[byte[]]$CRLF = 13, 10
 
function Get-AsciiBytes([String] $str) {
    return [System.Text.Encoding]::ASCII.GetBytes($str)            
}
 
function ConvertTo-Base64($string) {
   $bytes  = [System.Text.Encoding]::UTF8.GetBytes($string);
   $encoded = [System.Convert]::ToBase64String($bytes); 
 
   return $encoded;
}
 
clear
##############  EXPORT OLD WORKFLOWS ##############################
Write-Host "Beginning Export Routine" -ForegroundColor Black -BackgroundColor Yellow
Write-Host ""
# build uri
$uri = "https://"+$oldvCOServer+":"+$vCOPort+"/vco/api/workflows/?conditions=categoryName="+$oldvCOCategory
 
# Authentication token or old server
$token = ConvertTo-Base64("$($oldvCOUser):$($oldvCOPass)");
$auth = "Basic $($token)";
$header = @{"Authorization"= $auth; };   
 
#execute API call
$workflows = Invoke-RestMethod -URi $uri -Method Get -ContentType "application/xml" -Headers $header
Write-Host "Exporting $($workflows.total) workflows from $oldvCOCategory"
Write-Host "-------------------------------------------------------------------------------------------"
#loop through each workflow and export to TempFolder
foreach ($href in $workflows.link.href)
{
    #retrieve information about the specific workflow
    $header = @{"Authorization"= $auth; };   
    $workflow = Invoke-RestMethod -URi $href -Method Get -ContentType "application/xml" -Headers $header
 
    #replace all spaces in workflow name
    $workflowname = [System.Text.RegularExpressions.Regex]::Replace($($workflow.name),"[^1-9a-zA-Z_]","")
    $filename = $TempFolder + $workflowname + ".workflow"
    # setup new header
    $header = @{"Authorization"= $auth;
                "Accept"="application/zip"; }; 
    Write-Host "Exporting $($workflow.name) to $filename - " -NoNewline
    Invoke-RestMethod -URi $href -Method Get -ContentType "application/xml" -Headers $header -OutFile $filename
    Write-Host "Done" -ForegroundColor Green  
}
Write-Host ""
Write-Host "Export Routine Complete" -ForegroundColor Black -BackgroundColor Yellow
##################################################################
 
##############  IMPORT WORKFLOWS ##############################
Write-Host ""
Write-Host ""
Write-Host "Import Routines to new server" -ForegroundColor Black -BackgroundColor Yellow
Write-Host ""
 
#Generate auth for new vCO Server
$token = ConvertTo-Base64("$($newvCOUser):$($newVCOPass)");
$auth = "Basic $($token)";
 
#Get Category ID
$header = @{"Authorization"= $auth; };   
$uri = "https://"+$newvCOServer+":"+$vCOPort+"/vco/api/categories/"
$categories = Invoke-RestMethod -URi $uri -Method Get -Headers $header -ContentType "application/xml" 
foreach ($att in $categories.link)
{
    if ($att.attributes.value -eq "HPEDSB")
    {
        foreach ($newatt in $att.attributes )
        {
            if ($newatt.name -eq "id")
            {
                $categoryID = $newatt.value
            }
        }
    }
}
 
$impUrl = "https://$($newvCOServer):$($vcoPort)/vco/api/workflows?categoryId=$($categoryId)&overwrite=true";
$header = @{"Authorization"= $auth;
            "Accept"= "application/zip";
            "Accept-Encoding"= "gzip,deflate,sdch";};    
 
$workflows = Get-ChildItem $TempFolder -Filter *.workflow
Write-Host "Importing $($workflows.count) workflows to $newvCOCategory"
Write-Host "-------------------------------------------------------------------------------------------"
foreach ($workflow in $workflows)
{
    Write-Host "Importing $($workflow.name) - " -NoNewline
    $body = New-Object System.IO.MemoryStream
    $boundary = [Guid]::NewGuid().ToString().Replace('-','')
    $ContentType = 'multipart/form-data; boundary=' + $boundary
    $b2 = Get-AsciiBytes ('--' + $boundary)
    $body.Write($b2, 0, $b2.Length)
    $body.Write($CRLF, 0, $CRLF.Length)           
    $b = (Get-AsciiBytes ('Content-Disposition: form-data; name="categoryId"'))
    $body.Write($b, 0, $b.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $b = (Get-AsciiBytes $categoryId)
    $body.Write($b, 0, $b.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $body.Write($b2, 0, $b2.Length)
    $body.Write($CRLF, 0, $CRLF.Length)     
    $b = (Get-AsciiBytes ('Content-Disposition: form-data; name="file"; filename="$($workflow.Name)";'))
    $body.Write($b, 0, $b.Length)
    $body.Write($CRLF, 0, $CRLF.Length)            
    $b = (Get-AsciiBytes 'Content-Type:application/octet-stream')
    $body.Write($b, 0, $b.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $b = [System.IO.File]::ReadAllBytes($workflow.FullName)
    $body.Write($b, 0, $b.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $body.Write($b2, 0, $b2.Length)
    $b = (Get-AsciiBytes '--');
    $body.Write($b, 0, $b.Length);
    $body.Write($CRLF, 0, $CRLF.Length);
 
    $header = @{"Authorization"= $auth;
            "Accept"= "application/zip";
            "Accept-Encoding"= "gzip,deflate,sdch";};    
    Invoke-RestMethod -Method POST -Uri $impUrl -ContentType $ContentType -Body $body.ToArray() -Headers $header
    Write-Host "Done" -foregroundcolor Green
}
 
Write-Host ""
Write-Host "Import Routine complete" -ForegroundColor Black -BackgroundColor Yellow
##################################################################

Friday Shorts – Log Insight, @vNoob’s tail, ViPR, Best Practices

Tigers love pepper.  They hate cinnamon.  – Alan Garner (Zach Galifianakis) from The Hangover

It's been a while since the last Friday Shorts – no excuses – I've been slacking 🙂  Whatever – here it is!

Log Insight jumps on the free fundamentals training bandwagon

VMW-BXSHT-vCNTR-LOG-INSIGHT_eStoreI'm certainly a huge fan of the 'fundamental' self-paced training that VMware has been offering for free.  In fact, if you go and have a look at the Top Free Courses on mylearn you will see that the fundamental series occupies the complete top 10.  Well, another on you can now add to that list is VMware Log Insight Fundamentals.  This course will give you a great introduction and overview as to what VMware Log Insight is as well as go through some installation, configuration and analysis.  Certainly a great way to get introduced to the product – oh, and for free!

vNoob Guide to Getting Tail

tailI tried to think of a catchy title here but I couldn't think of anything Conrad hasn't already said in his latest post vNoob Guide to Getting Tail.  Now I have no idea how well Conrad does with the ladies, but he certainly gives a great overview on how to use the 'tail' command, not only in Linux but in Powershell as well!!   This is most certainly not your average man page so get ready to be amused by some of Conrad's wittiness!!!  Good job!

Best Practices are only best practices until they are not!

reevaluateHere's a great example on the vSphere Blog on why you always need to re-evaluate any settings or best practices you have implemented in the past.  The fact of the matter is, with every new release comes new best practices – and things that you think you might not need to revisit you do!  Things like your power savings settings in the BIOS no longer calling for High Performance but now being recommended to be set at 'OS Controlled'.

Feel like testing out EMC's ViPR in your lab?  Go ahead!

EMC-LogoEMC has opened the flood gates for anyone to go and grab ViPR for use in a non-production environment.  That's right, if you feel like testing out EMC's take on software defined storage just head on over here, grab a copy and take it for a test drive.  I've often blogged about other companies dishing out NFR licenses for their products and how I think it is a great idea – it gives people the chance to get these bits into their labs and test them out without all of the salesy webinars, conference calls, and pestering emails :).   No matter what the play of EMC is here, I intend to get my hands on this so stay tuned!

Kerberos authentication for the PowerShell plugin in vCO 5.5

1 The ability to have vCO kick off PowerShell scripts is pretty awesome!  And the fact that you can kick these off contextually inside of the vSphere Web Client is even more awesome!  Even more awesome than that, yes, that’s a lot of awesome is the new features offered with vCenter Orchestrator 5.5 – So, I’ve taken the plunge on one of my environments and upgraded.  Since then I’ve been slowly migrating workflows over – one of which utilized the PowerShell plug-in.  Now, since the appliance mode of vCO requires you to do a rip and replace rather than an upgrade (because I’m using the embedded database) I had to reinstall the PS plugin, therefore forcing me to reconfigure the Kerberos settings on vCO.   During this I realized that things are a little bit different than when I first blogged about vCO and PowerShell here.  Below is how I got it to work…

First up is the WinRM setup on your PowerShell host.  This process  hasn’t changed from 5.1, however I’ll still include the steps and commands that need to be run below.  Remember these are to be executed on the Windows box that you wish to run the PowerShell script from.

  • To create a winrm listener and open any required firewall ports
  • winrm quickconfig
  • To enable kerberos authentication
  • winrm set winrm/config/service/auth @{Kerberos=”true”}
  • Allow transfer of unencrypted data
  • winrm set winrm/config/service @{AllowUnencrypted=”true”}
  • Up the max memory per shell – I needed to do this to get things working
  • winrm set winrm/config/winrs @{MaxMemoryPerShellMB=”2048″}

No on to the krb5.conf file – this is where things get a bit different.  In vCO 5.1 we were required to edit the krb5.conf file located in /opt/vmo/jre/lib/security/ – well, if you go looking for that directory on 5.5 you won’t find it.  Instead, we need to create our krb5.conf file in /usr/java/jre-vmware/lib/security/  As far as what goes in the file it is the same and is listed below…(obviosoly substituting your own domain for lab.local and your own dc for the kdc definition).

[libdefaults]
default_realm = LAB.LOCAL
udp_preferences_limit = 1   [realms]
LAB.LOCAL = {
kdc = dc.LAB.LOCAL
default_domain = LAB.LOCAL
}   [domain_realms]
.lab.local=LAB.LOCAL
lab.local=LAB.LOCAL

After you have saved the file in the proper directory we need to modify the permissions.  The following line should get you the proper permissions to get everything working.

chmod 644 /usr/java/jre-vmware/lib/security/krb5.conf

Just a few other notes!  You might want to modify your /etc/hosts file and be sure that you are able to resolve the fqdn’s of both your dc and the PowerShell host you plan to use.  Also, when adding the PowerShell host be sure to select Kerberos as your authentication type and enter in your credentials using the ‘user@domain.com’ format.

For now, that should get you automating like a champ!

Some updates to the PowerCLI Shutdown VMs Script – Now w/ a virtualized vCenter option

shutdown-button-mdOver that past few months I've been getting quite a few emails and comments as it pertains to the scripts I have shared to shutdown and power on VMs in the event of power loss.  I assume since the storm weather is kicking in people are realizing that they need to have some kind of protection in regards to gracefully getting the VMs down.  Now I've had the (privilege?) of watching my script run multiple times in a production environment and it has gotten to the point where it is working flawlessly.  One caveat to the script though – whichever Windows box is executing the PowerShell must stay powered on longer than your vCenter Server.

That being said the script would be pretty useless if you had a virtualized vCenter Server running on the same cluster as your production VMs that your are shutting down – this is what all of the emails and comments that I've been getting have been based around.  So, without further ado I've added a few updates to the script to better support those with a virtualized vCenter.  A few explanations below…

So first off (Line 50) I've added a variable called $vCenterVMName and assigned this a default value of NA.  This variable should hold the VM Name of your vCenter Server.  I've tried to keep just one version of the script supporting both virtualized and non-virtualized vCenter's thus is why the default to "NA".  So if you don't have a virtualized vCenter just leave the variable set to NA and the script will run as it always had, otherwise if you do, put its' VM name in here.

Secondly down on Lines 107 to 1118 I do a few things.  First, check to see if you have changed that default value and if you have we need to figure out which ESXi host vCenter is running on in order to shut it down last.  This is what's done here.

Then, in each of the shutdown loops (Line 125 and Line 162) I've added an if statement.  Basically it just checks to see if the VM it is processing is the vCenter Server.  If so, it doesn't process the shutdown.

Lastly I've done a couple more things.  On Line 186 I've added a check while shutting down the hosts.  If the host that is being processed is the host that vCenter is running on I want to leave it till the end, so it is skipped during this loop.  Then, the finally the block of code starting on Line 196, if needed, will send the shutdown command to the host that is running vCenter Server.

As you may of noticed I've added a some logging as well, nothing bit, just a record of what happened in a txt file rather than relying on Write-Host.  The script in its' entirety is below or you can download it here.  Watch for updates to the power on script to support the vritualized vCenter as well.  Just not sure how I'm going to tackle that one yet 🙂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#################################################################################
# Power off VMs (poweroffvms.ps1)
#
# This is a complete update to my previous shutdown script.  No longer do we
# loop through the esx hosts and shut down VMs per host.  We now just shut them
# all down up front.  Also, no more $vmstoleaveon variable.  I'm assuming here 
# that your vCenter is not virtual.  If it is you can most likely grab version
# 1 of this script and take the code to leave the vCenter host till last.  Maybe
# someday I'll get around to updating it to merge the two...but for now, this is
# it!
#
# This script does have a 'partner' script that powers the VMs back on, you can
# grab that script at http://blog.mwpreston.net/shares/
#
# Created By: Mike Preston, 2012 - With a whole lot of help from Eric Wright 
#                                  (@discoposse)
#
# Variables:  $mysecret - a secret word to actually make the script run, stops 
#                         the script from running when double click DISASTER
#             $vcenter - The IP/DNS of your vCenter Server
#             $username/password - credentials for your vCenter Server
#             $filename - path to csv file to store powered on vms
			  used for the poweronvms.ps1 script.
#             $cluster - Name of specific cluster to target within vCenter
#             $datacenter - Name of specific datacenter to target within vCenter
#
#
# Usage: ./poweroffvms.ps1 "keyword"
#        Intended to be ran in the command section of the APC Powerchute Network
#        Shutdown program before the shutdown sequence has started.
#
#################################################################################
 
param($keyword)
 
Add-PSSnapin VMware.VimAutomation.Core
#some variables
$vcenter = "vcenterServer"
$username = "user"
$password = "password"
$cluster = "clusterName"
$datacenter = "datacenterName"
$filename = "c:\path\to\poweredonvms.csv"
$mysecret = "kerfuffle"
$date = ( get-date ).ToString('yyyyMMdd')
$logfile = New-Item -type file "C:\ShutdownLog-$date.txt"
 
#for use with a virtualized vCenter - bring it down last :) - if you don't need this functionality
#simply leave the variable set to NA and the script will work as it always had
$vCenterVMName = "NA"
 
Write-Host "A Powerchute network shutdown command has been sent to the vCenter Server." -Foregroundcolor yellow
Write-Host "This script will shutdown all of the VMs and hosts located in $datacenter" -Foregroundcolor yellow
Write-Host "Upon completion, this server ($vcenter) will also be shutdown gracefully" -Foregroundcolor yellow
Write-Host ""
Write-Host "This script has also has a counterpart (powervmsbackon.ps1) which should" -Foregroundcolor yellow
Write-Host "be setup to run during the startup of this machine ($vcenter). " -Foregroundcolor yellow
Write-Host "This will ensure that your VMs are powered back on once power is restored!" -Foregroundcolor yellow
Sleep 5
 
Add-Content $logfile "mwpreston.net PowerOff Script Engaged"
Add-Content $logfile ""
 
if ($keyword -ne $mysecret) 
{ 
    Write-Host "You haven't passed the proper detonation sequence...ABORTING THE SCRIPT" -ForegroundColor red
    Add-Content $logfile "You haven't passed the proper detonation sequence...ABORTING THE SCRIPT"
    exit
}
 
#connect to vcenter
Write-Host "Connecting to vCenter - $vcenter ...." -nonewline
Add-Content $logfile "Connecting to vCenter - $vcenter"
$success = Connect-VIServer $vcenter -username $username -Password $password
if ($success) { Write-Host "Connected!" -Foregroundcolor Green }
else
{
    Write-Host "Something is wrong, Aborting script" -Foregroundcolor Red
    Add-Content $logfile "Something is wrong, Aborting script"
    exit
}
Write-Host ""
Add-Content $logfile  ""
 
#Get a list of all powered on VMs - used for powering back on....
Get-VM -Location $cluster | where-object {$_.PowerState -eq "PoweredOn" } | Select Name | Export-CSV $filename
 
#change DRS Automation level to partially automated...
Write-Host "Changing cluster DRS Automation Level to Partially Automated" -Foregroundcolor green
Get-Cluster $cluster | Set-Cluster -DrsAutomation PartiallyAutomated -confirm:$false 
#change the HA Level
Write-Host ""
Write-Host "Disabling HA on the cluster..." -Foregroundcolor green
Write-Host ""
Add-Content $logfile "Disabling HA on the cluster..."
Add-Content $logfile ""
Get-Cluster $cluster | Set-Cluster -HAEnabled:$false -confirm:$false 
 
#get VMs again (we will do this again instead of parsing the file in case a VM was powered in the nanosecond that it took to get here.... :)
Write-Host ""
Write-Host "Retrieving a list of powered on guests...." -Foregroundcolor Green
Write-Host ""
Add-Content $logfile "Retrieving a list of powered on guests...."
Add-Content $logfile ""
$poweredonguests = Get-VM -Location $cluster | where-object {$_.PowerState -eq "PoweredOn" }
 
Add-Content $logfile "Checking to see if vCenter is virtualized"
#retrieve host info for vCenter
if ($vcenterVMName -ne "NA") 
{ 
    Add-Content $logfile "vCenter is indeed virtualized, getting ESXi host hosting vCenter Server"
    $vCenterHost = (Get-VM $vCenterVMName).Host.Name 
    Add-Content $logfile "$vCenterVMName currently running on $vCenterHost - will process this last"
}
else
{
    Add-Content $logfile "vCenter is not virtualized"
}
 
Add-Content $logfile "Proceeding with VM PowerOff"
Add-Content $logfile ""
#and now, let's start powering off some guests....
ForEach ( $guest in $poweredonguests ) 
{
    if ($guest.Name -ne $vCenterVMName)
    { 
        Write-Host "Processing $guest ...." -ForegroundColor Green
        Write-Host "Checking for VMware tools install" -Foregroundcolor Green
        $guestinfo = get-view -Id $guest.ID
        if ($guestinfo.config.Tools.ToolsVersion -eq 0)
        {
            Write-Host "No VMware tools detected in $guest , hard power this one" -ForegroundColor Yellow
            Add-Content $logfile "$guest.Name - no VMware tools, hard power off"
            Stop-VM $guest -confirm:$false
        }
        else
        {
           write-host "VMware tools detected.  I will attempt to gracefully shutdown $guest"
           Add-Content $logfile "$guest.Name - VMware tools installed, gracefull shutdown"
           $vmshutdown = $guest | shutdown-VMGuest -Confirm:$false
        }
    }   
}
 
#Lets wait a minute or so for shutdowns to complete
Write-Host ""
Write-Host "Giving VMs 2 minutes before resulting in hard poweroff"
Write-Host ""
Add-Content $logfile ""
Add-Content $logfile "Waiting a couple minutes then hard powering off all remaining VMs"
Add-Content $logfile ""
Sleep 120
 
 
#Now, let's go back through again to see if anything is still powered on and shut it down if it is
Write-Host "Beginning Phase 2 - anything left on....night night..." -ForegroundColor red
Write-Host ""
#get our list of guests still powered on...
$poweredonguests = Get-VM -Location $cluster | where-object {$_.PowerState -eq "PoweredOn" }
ForEach ( $guest in $poweredonguests ) 
{
    if ($guest.Name -ne $vCenterVMName)
    { 
        Write-Host "Processing $guest ...." -ForegroundColor Green
        #no checking for toosl, we just need to blast it down...
        write-host "Shutting down $guest - I don't care, it just needs to be off..." -ForegroundColor Yellow
        Add-Content $logfile "$guest.Name - Hard Power Off"
        Stop-VM $guest -confirm:$false
    }
}
 
#wait 30 seconds
Write-Host "Waiting 30 seconds and then proceding with host power off"
Write-Host ""
Add-Content $logfile ""
Add-Content $logfile "Processing power off of all hosts now"
Sleep 30
 
#and now its time to slam down the hosts - I've chosen to go by datacenter here but you could put the cluster
#  There are some standalone hosts in the datacenter that I would also like to shutdown, those vms are set to
# start and stop with the host, so i can just shut those hosts down and they will take care of the vm shutdown ;)
 
$esxhosts = Get-VMHost -Location $cluster
foreach ($esxhost in $esxhosts)
{
    if ($esxhost.Name -ne $vCenterHost)
    {
        #Shutem all down
        Write-Host "Shutting down $esxhost" -ForegroundColor Green
        Add-Content $logfile "Shutting down $esxhost"
        $esxhost | Foreach {Get-View $_.ID} | Foreach {$_.ShutdownHost_Task($TRUE)}
    }
}
 
#finally shut down the host which vCenter resides on if using that functionality
if ($vcenterVMName -ne "NA") 
{
    Add-Content $logfile ""
    Add-Content $logfile "Processing $vcenterHost - shutting down " 
    Get-VMhost $vCenterHost | Foreach {Get-View $_.ID} | Foreach {$_.ShutdownHost_Task($TRUE)} 
}
 
Add-Content $logfile ""
Add-Content $logfile "All done!"
#that's a wrap

So there you have it!  Hopefully this will help some of you out with your shutdown endeavors.  Feel free to make any changes you need in order to make it work for your environment and let me know how it goes – keep those comments, concerns, thoughts and emails coming in!

Move your templates to a new cluster – The PowerCLI Way

powerclilogoAlright, here's a script I quickly pieced together to accomplish the task of moving some of our templates over to a new cluster.  At the time I was currently in the process of deploying a new cluster on a new distributed virtual switch, thus this script only tackles your templates on a single host basis.  Meaning I was walking each host over to the new switch one by one and then migrating virtual machine networking and all vmkernel interfaces on the host, then performing a vmotion on the VMs and templates and finally destroying the original host…

Oh well, enough history (blabbing).  The script is very simple as you can see below.  Aside from your vCenter information it takes a couple of variables; source and destination host (Lines 7/8).  The only quirkiness I found was with the Set-Template cmdlet – I could not find a way to specify the location of the VM when converting it, thus you can see on line 18 I have to move it back to my source host after the conversion to ensure i will have the connectivity on my migrated vMotion network.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$vcenter ='IP of vCenter'
$vcuser = 'vCenter User'
$vcpass = 'vCenter Pass'
 
Connect-VIServer $vcenter -user $vcuser -pass $vcpass
 
$sourcehost = get-vmhost 'Source Hostname/IP'
$destinationhost = get-vmhost 'Target Hostname/IP'
 
#get all templates on source host
$templates = get-template -Location $sourcehost
 
foreach ($template in $templates)
{
    # convert template to VM
    Set-Template $template -ToVM -confirm:$False
    # ensure it is running on my source host
    Get-VM -Name $template | Move-VM -Destination $sourcehost
    # migrate vm to new cluster
    Get-VM -Name $template | Move-VM -Destination $destinationhost
    # convert back to a template
    Get-VM -Name $template | Set-VM -ToTemplate -Confirm:$false
}

So that's it!  I told you it was a quickie!  Again, I don't have a secret alias of PowerCLI Man so if you have any suggestions on how to improve up on any of this let me know…