Tag Archives: powershell

vCloud Director Snapshots with PowerCLI

If you or your provider are already running vCloud Director 5.1, you now have access to take a single snapshot of your VMs!

If you want to play with snapshots in PowerCLI, you’re going to need the module in this blog. PowerCLI 5.1 currently only supports the vCloud API 1.5, which means there are no 5.1 API capabilities yet. (I stress “yet.” :D)

The module uses the current PowerCLI for it’s session and API access, and then bolts on some 5.1 API snapshot magic.

To use the module, simply download the .psm1 file and:


Import-Module vCloudSnapshots.psm1

After that, you’ll have access to 4 cmdlets:


Get-CISnapshot
New-CISnapshot
Remove-CISnapshot
Set-CISnapshot

To get snapshots:


# Get vApps with Snapshots
Get-CIVapp | Get-CISnapshot

# Get VMs with Snapshots
Get-CIVM | Get-CISnapshot

# Get Snapshot info for a particular VM
Get-CIVM "MyVM" | GetCISnapshot

It should be noted that all the cmdlets apply to a VM or vApp, so always make sure you’re doing a Get-CIVM or Get-CIVapp first.

For example…here’s creating, reverting, and removing:


# Create a snapshot for a vApp (or all VMs in the vApp, really.)
Get-CIVApp "MyVApp" | New-CISnapshot

# Revert to our snapshot
Get-CIVApp "MyVApp" | Set-CISnapshot -revertToCurrent

# Remove our snapshot
Get-CIVApp "MyVApp" | Remove-CISnapshot

For more help, just use Get-Help and the command!

vCloud Snapshots Powershell Module


Hacking the vCloud Director Browser Support Warning

 

 

I’m a Chrome user and it’s particularly annoying to get the “this browser is not supported” message any time I load the vCloud Director URL, or my session expires and I have to re-login. I try to spend most of my time in PowerCLI, but sometimes I need to make a minor tweak, or open the remote console of a VM.

I understand that Chrome is not a supported browser, which is only because of the remote console, as far as I can tell. Everything else works great in Chrome!

So what is an unsupported browser user to do? HACK!

I came up with this idea on accident, when I was playing with the Powershell v3 Invoke-RestMethod cmdlet.

Here’s what I ran:


Invoke-RestMethod "https://vcloud.example.com/cloud/org/jake" | Out-File C:\users\jake\desktop\vcloudindex.html -encoding UTF8

This dumps the HTML from the URL just like wget in Linux.

After that, I started changing out all of the relative HTML links to absolutes. In the following html, I changed the first one to show you an example.


css/main.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="support/<span class=">// <![CDATA[
javascript</span>/jquery/jquery-1.3.2.min.js" type="text/javascript">
// ]]></script>
<script type="text/javascript" src="support/<span class=">// <![CDATA[
javascript</span>/upload.js" type="text/javascript">
// ]]></script>
<script type="text/javascript" src="support/<span class=">// <![CDATA[
javascript</span>/swfobject.js" type="text/javascript">
// ]]></script>
<script type="text/javascript" src="support/<span class=">// <![CDATA[
javascript</span>/swfaddress.js" type="text/javascript">
// ]]></script>
 <script src="support/javascript/associative-array.js" type="text/javascript"></script>
 <script src="support/javascript/VMRCConsoleController.js" type="text/javascript"></script>
 <script src="support/javascript/remoteaccess.js" type="text/javascript"></script>

After that, I started running into cookie errors, and apparently Chrome does not load cookies for local pages by default. You can fix this by loading Chrome with –enable-file-cookies, but that seemed like a chore. I simply uploaded the file to my public dropbox folder and got the URL for the file.

At that point, I was accessing the file over the internet, but the flash was not loading. After a bit of investigation, I discovered this line:


{  wmode: "opaque", allowScriptAccess: "sameDomain" },

A quick google search revealed I could change “sameDomain” to “always.” I made the change and reloaded the page. Flash worked and I could log in!!!

So now I could focus my attention on that browser error. I did a find for the text of the error, and backtracked it to this function:


function onPageLoad() {
 if (isSupportedBrowser()) {
 initializeFlash();
 } else {
 $("#browserWarning").show();
 $("#flashWarning").hide();
 $("#cookieWarning").hide();
 }
 }

Easy fix. I simply removed the logic of the onPageLoad() function to this:


function onPageLoad() {
 initializeFlash();
 }

I reloaded the page, anxious to see if that fixed it….SUCCESS!

I browsed around a bit to see if there was anything that didn’t work, and it appears the only thing is right clicking objects. The work around for that is simply clicking the gear icon to bring up the actions for a particular object, but I am sure there is an HTML or javascript fix for that as well.

From a security standpoint, I could see someone constructing a man-in-the-middle attack of sorts to gain credentials, but they’d have to work at it. Always make sure the URL you are connected to is correct, and the SSL cert is for your domain. Bookmarking the link can also be a method to make sure you are not going to the wrong URL.

 

As a final note, This does NOT add VMware remote console support to Chrome. The VMRC plugin is currently only for Firefox and IE. The usefulness of this hack is somewhat limited, as it only removes the browser warnings.

Enjoy!

 

 


Emailing vCloud Organization Users

A question popped in the vCloud PowerCLI forum the other day on how to use the “notify users” function in the vCloud Director Web UI through the vCloud API. While the functionality is not in the API, PowerCLI and Powershell make short work of it with the vCloud snap-in and Powershell’s Send-MailMessage cmdlet.

The task in this case was to send an email to all Org users to remind them of the number of VMs they have in a particular organization. The following code could easily be adapted to show: Resource usage, IP addresses, OS usage, etc.

Probably the most interesting thing I find about this code is that it can be used in either a systems admin role, or an organization admin role.


$mailServer = "mail.example.com"
$users = Get-CIUser # use -Org "OrgName" if connected to system Org
$VApps = Get-CIVApp # use -Org "OrgName" if connected to system Org

foreach ($user in $users)
{
 $counter = 0
 $ownerVApps = $VApps | where {$_.owner -eq $user}
 $vapps | ForEach-Object {$counter += $_.extensiondata.children.vm.count}

$mailbody = "Greetings, You currently have $counter virtual machines in Organization: $($user.Org)"

Send-MailMessage -SmtpServer $mailServer -To $user.email -From "PowerCLI@example.com" -Body $mailbody

}

 


Powershell Outlook Time Reporting by Category

By request, I have revised the code from my original Outlook Time Reporting to report on Category and day of week with summary totals. A true time sheet really. In fact, I believe someone is using this to import their time into SAP…:D

This was a significant change made completely easy with Powershell.

The HTML and CSV output look like this:

Sample HTML report

 

Here’s the new code, but please leave me a comment if you use this! I’d love to hear about it!


#
# OutlookTimeReport.ps1
# Jake Robinson
# v1.2
#
#
#
#

param ( [DateTime] $rangeStart = (get-date -hour 0 -minute 0 -second 0).AddDays(-(get-date).DayOfWeek.value__),
 [DateTime] $rangeEnd = (get-date -hour 23 -minute 59 -second 59).AddDays(7-(get-date).DayOfWeek.value__),
 [String] $categories,
 [String] $calendar,
 [switch] $csv,
 [switch] $pickdates)

function pick-date()
{
 $objForm = New-Object Windows.Forms.Form

$objForm.Text = "Select a Date"
 $objForm.Size = New-Object Drawing.Size @(190,190)
 $objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True

$objForm.Add_KeyDown({
 if ($_.KeyCode -eq "Enter")
 {
 $dtmDate=$objCalendar.SelectionStart
 $objForm.Close()
 }
 })

$objForm.Add_KeyDown({
 if ($_.KeyCode -eq "Escape")
 {
 $objForm.Close()
 }
 })

$objCalendar = New-Object System.Windows.Forms.MonthCalendar
 $objCalendar.ShowTodayCircle = $False
 $objCalendar.MaxSelectionCount = 1
 $objForm.Controls.Add($objCalendar)

$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
 [void] $objForm.ShowDialog()

if ($dtmDate)
 {
 return $dtmDate
 }
}

if ($pickdates)
{
 [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
 [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

 [DateTime] $pickedStart = pick-date
 [DateTime] $pickedEnd = pick-date

 If ($pickedStart -and $pickedEnd)
 {
 $rangeStart = $pickedStart
 $rangeEnd = $pickedEnd
 }
}

Add-Type -AssemblyName Microsoft.Office.Interop.Outlook

$class = @”
using Microsoft.Office.Interop.Outlook;public class MyOL
{
 public MAPIFolder GetCalendar(string userName)
 {
 Application oOutlook = new Application();
 NameSpace oNs = oOutlook.GetNamespace("MAPI");
 Recipient oRep = oNs.CreateRecipient(userName);
 MAPIFolder calendar = oNs.GetSharedDefaultFolder(oRep, OlDefaultFolders.olFolderCalendar);
 return calendar;
 }
}
“@

Add-Type $class -ReferencedAssemblies Microsoft.Office.Interop.Outlook

$outlook = new-object MyOL
$restriction = "[End] >= '{0}' AND [Start] <= '{1}'" -f $rangeStart.ToString("g"), $rangeEnd.ToString("g")
$seArray = @()
$categoryMatch = [string]$categories -replace ' ','|'
$SECalendar = $outlook.GetCalendar($calendar)
$appointments = $SECalendar.items
$appointments.Sort("[Start]")
$appointments.IncludeRecurrences = $true
$SEappts = $appointments.Restrict($restriction) | where {$_.categories -match $categoryMatch}

# Group the appointments by category
$grouped = $SEappts | Group-Object categories

# This is our table array
$table = @()

# Days of the week. Are these language sensitive?
$dow = @("Monday","Tuesday","Wednesday","Thursday","Friday")
# Change hash values to local language if required.
$dowHash = @{"Monday"="Monday"; "Tuesday"="Tuesday"; "Wednesday"="Wednesday"; "Thursday"="Thursday"; "Friday"="Friday"}

# For each category...
foreach ($group in $grouped)
{
 # Start a new row...
 $row = New-Object PSObject

 # Add the category name to the start of our table...
 Add-Member -MemberType NoteProperty -Name "Category" -value $group.name -InputObject $row

# For each work day
 foreach ($day in $dow)
 {
 # Sum up the hours for that category.
 $hours = ($group.group | where {$_.start.dayofweek -eq $day} | Measure-Object -Sum -Property duration).sum /60

# Then add it to our table row.
 Add-Member -MemberType NoteProperty -Name $dowHash.($day) -Value $hours -InputObject $row
 }
 $table += $row
}


# Summary at the end
$summary = New-Object PSObject
Add-Member -MemberType NoteProperty -Name "Category" -value "SUMMARY" -InputObject $summary
foreach ($day in $dow)
 {
 # Sum up the hours for that category.
 $hours = ($SEappts | where {$_.start.dayofweek -eq $day} | Measure-Object -Sum -Property duration).sum /60

# Then add it.
 Add-Member -MemberType NoteProperty -Name $dowHash.($day) -Value $hours -InputObject $summary
 }
$table += $summary

# Get TOTAL hours for final HTML report
$totalHours = ($SEappts | Measure-Object -Sum -Property duration).sum /60


if ($csv)
{
 $table | Export-Csv -NoTypeInformation -Path "$HOME\desktop\OutlookTimeReport.csv"
 Invoke-Item "$HOME\desktop\OutlookTimeReport.csv"
}
else
{
 $totalpost = "<table><tr><td><b>TOTAL HOURS:</b></td><td><b>$($totalHours)</b></td></tr><table>"
 $seHTML = $table | ConvertTo-Html -PostContent $totalpost | Out-File "$HOME\desktop\OutlookTimeReport.html"
 Invoke-Item "$HOME\desktop\OutlookTimeReport.html"
}


Time Reporting with Outlook and Powershell

Do you work at a company that requires you to report your time?

Are you curious how much time you are spending on different projects or duties?

Hate having a separate tool just for time tracking and reporting? (me too!)

My Outlook calendar makes for easy time tracking. It’s on my iPhone, my desktop, my laptop, my home PC…but there are no reporting capabilities that I am aware of, and I am not going to buy an app or plugin that I have to manage.

What if I wanted to find out how much time the rest of my team is spending on something like “host maintenance?” They have their calendar shared with me, and I can see their categories, but I would still have to manually add up their time, force them to do double data entry to another app, or something else that requires double-data-entry.

I wrote this Powershell solution to figure out how much time a group of people were spending on “Sales Engineering,” and made a couple modifications for this post to allow different users and categories.

Features:

Category

use -category to add up time on. Only single category supported.

Multiple calendars

use -calendars to enter a single name like “Jake Robinson” or multiple in an array: @(“Jake Robinson”, “Alan Renouf”)

Date Range

By default the current week M-F will be reported. If you need something a little different, you can use -pickDates to bring up a graphical calendar to each start and end date (click the date then press enter). You can also use -rangeStart and -rangeEnd with something like this “5/24/2012”

HTML or CSV output

Outputs to HTML report by default, or use -csv to output to comma separated value file.

Screenshots!

My Calendar

Categories

Example with pickDates

HTML report

Example using rangeStart and rangeEnd to CSV

CSV output

…and here’s the script!


#
# OutlookTimeReport.ps1
# Jake Robinson
# v1.2
#
#
#
#

param ( [DateTime] $rangeStart = (get-date -hour 0 -minute 0 -second 0).AddDays(-(get-date).DayOfWeek.value__),
 [DateTime] $rangeEnd = (get-date -hour 23 -minute 59 -second 59).AddDays(7-(get-date).DayOfWeek.value__),
 [String] $category,
 [String] $calendars,
 [switch] $csv,
 [switch] $pickdates)

function pick-date()
{
 $objForm = New-Object Windows.Forms.Form

$objForm.Text = "Select a Date"
 $objForm.Size = New-Object Drawing.Size @(190,190)
 $objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True

$objForm.Add_KeyDown({
 if ($_.KeyCode -eq "Enter")
 {
 $dtmDate=$objCalendar.SelectionStart
 $objForm.Close()
 }
 })

$objForm.Add_KeyDown({
 if ($_.KeyCode -eq "Escape")
 {
 $objForm.Close()
 }
 })

$objCalendar = New-Object System.Windows.Forms.MonthCalendar
 $objCalendar.ShowTodayCircle = $False
 $objCalendar.MaxSelectionCount = 1
 $objForm.Controls.Add($objCalendar)

$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
 [void] $objForm.ShowDialog()

if ($dtmDate)
 {
 return $dtmDate
 }
}

if ($pickdates)
{
 [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
 [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

 [DateTime] $pickedStart = pick-date
 [DateTime] $pickedEnd = pick-date

 If ($pickedStart -and $pickedEnd)
 {
 $rangeStart = $pickedStart
 $rangeEnd = $pickedEnd
 }
}

Add-Type -AssemblyName Microsoft.Office.Interop.Outlook

$class = @”
using Microsoft.Office.Interop.Outlook;public class MyOL
{
 public MAPIFolder GetCalendar(string userName)
 {
 Application oOutlook = new Application();
 NameSpace oNs = oOutlook.GetNamespace("MAPI");
 Recipient oRep = oNs.CreateRecipient(userName);
 MAPIFolder calendar = oNs.GetSharedDefaultFolder(oRep, OlDefaultFolders.olFolderCalendar);
 return calendar;
 }
}
“@

Add-Type $class -ReferencedAssemblies Microsoft.Office.Interop.Outlook

$outlook = new-object MyOL
$restriction = "[End] >= '{0}' AND [Start] <= '{1}'" -f $rangeStart.ToString("g"), $rangeEnd.ToString("g")
$seArray = @()
foreach($se in $calendars)
{
 $seObject = New-Object PSObject
 $SECalendar = $outlook.GetCalendar($se)
 $appointments = $SECalendar.items
 $appointments.Sort("[Start]")
 $appointments.IncludeRecurrences = $true
 $SEappts = $appointments.Restrict($restriction) | where {$_.categories -match $category}
 $SEhours = ($SEappts | Measure-Object -Sum -Property duration).sum / 60
 Add-Member -MemberType noteproperty -Value $se -Name "Team Member" -InputObject $seObject
 Add-Member -MemberType noteproperty -Value $SEhours -Name "Hours" -InputObject $seObject
 $seArray += $seObject
}
$totalHours = ($seArray | measure-object -Sum -Property hours).sum
$totalsObject = New-Object PSObject
Add-Member -MemberType noteproperty -Value "TOTAL HOURS" -Name "Team Member" -InputObject $totalsObject
Add-Member -MemberType noteproperty -Value $totalHours -Name "Hours" -InputObject $totalsObject
$seArray += $totalsObject

if ($csv)
{
 $seArray | Export-Csv -NoTypeInformation -Path "$HOME\desktop\OutlookTimeReport.csv"
 Invoke-Item "$HOME\desktop\OutlookTimeReport.csv"
}
else
{
$seHTML = $seArray | ConvertTo-Html -Body "</pre>
<h3>$($category): $($rangeStart.ToString("MM.dd.yyyy")) - $($rangeEnd.ToString("MM.dd.yyyy"))</h3>
<pre>"| Out-File "$HOME\desktop\OutlookTimeReport.html"
 Invoke-Item "$HOME\desktop\OutlookTimeReport.html"
}

 

 
For more Powershell and Outlook fun, see Alan’s post!


How to be a PowerCLI Ninja

Dean (aka @Danger0u5) mentioned on twitter that he’d like to be a Powershell/PowerCLI ninja someday. (I think he’s already on his way. :)) It got me thinking about how I got started with Powershell and what my core success factors were with learning it. These tips can apply to learning almost anything, really!

1. Make it a priority.

My mentor and Bluelock CTO Pat O’Day (maybe unbeknownst to him :)), caught me once saying “I wish I had the time…” Very understanding to my dilemma, he said “You will never have enough time. It’s about priority.” That simple fact might be obvious to a lot of people, but it absolutely changed my views about time vs. priority. “I don’t have time” doesn’t exist for me anymore. It’s all about where it lands in the list. “Hey Jake, can you {Insert task here}?” “I can do that, but lets figure out the priority.”

The point here? You have to decide the priority of learning this new skill. Ask yourself “How important to me is learning Powershell/PowerCLI?” I can tell you from experience it is well worth it, but you need to decide for yourself. After that, it will be fairly simple to fit it into your list.

2. Focus on a specific task.

I learned Powershell because I had an idea. A need. I wanted learn to Powershell to accomplish something very specific. So, I mapped out in my head the various bits of information that I would need, and then learned how to get that information with Powershell. Then it was just math and formatting. There are a lot of great books, blogs and online training regarding Powershell/PowerCLI, but if you don’t have a specific need, you aren’t going to retain the information.

I’ve been struggling learning Ruby (and Rails). I really want to learn Ruby (and Rails). The problem is, I don’t have a specific need. I’ve been through tryruby.org and Rails for Zombies, which are both GREAT free tools to learn Ruby (and Rails), but the moment I leave the page, it’s gone. There are a few reasons I want to learn Ruby, but again, nothing that is specific enough, or high enough priority.

For those struggling to learn Powershell/PowerCLI, don’t be discouraged. Focus on a very specific need. For those struggling to find a specific need, talk to people around your office. Groans and sighs are good places to start. Listen for phrases like “It’s going to take me 12 hours to put this data in.” Another good place to look for inspiration is the forums. Look for tumbleweed type questions (those that have somehow slipped the view of Luc Dekens. ;)) I’ll lurk the forums once in a while looking for things I find interesting. Even if they are already answered, I can look at how the problem was solved, and see if I would have answered it the same way.

3. Don’t be afraid to ask for help, but know how to ask good questions.

It surprises me how often I answer my own questions just by typing it up, and reading it as if I were answering for someone else. When I ask a question, I really try to understand what information someone would need to answer it. I want to equip the answerer with enough information to get me the right answer the first time. There can be too little information, but worse there can be too much information. Understanding the real issue is the key to getting a correct answer.

Back in my telecom days, there was a tag always attached to a piece of equipment that, in large bold font said “DO NOT ASSUME ANYTHING.” I always had a chuckle about it, but it’s a life rule Engineers should live by. Don’t assume thing A doesn’t work because of thing B. Be ready to defend it with facts. When it comes to asking questions, don’t assume that DNS is working properly, or that network cable is attached and it’s not a crossover, or everything is powered on, or………….. you get the point. 😉

4. Share what you know.

The question my mentor asked that I responded with “I just haven’t had the time” was about my involvement with the local tech community. My other excuse was “I don’t really get anything out of it.” That, of course, was also a poor response. 😉 He went on to tell the story of the birth of Bluelock. Actually, the story of how the founders of Bluelock first met. Not surprisingly, it was a small technology group.

He recommended that I make it a priority (see tip 1), but also start sharing what I know. I countered with the fact that there are EXPERTS out there who know way more than I do, and what could I possibly have to offer the community. He pointed out that everyone is a sum of their own unique experiences. No two people live exactly the same life. Whether you consider yourself an expert or not, you bring a uniqueness no one else has. Don’t be afraid to share it. You could turn out to be an expert in someone else’s eyes.

So if you’ve been on the fence about learning PowerCLI (Powershell), Ruby, or anything else, think about these principles and how they apply to you!


Getting Started with the Unofficial vCloud Powershell Functions

By Popular Demand! A quick how-to on my vCloud Powershell functions!


My Powershell Christmas Wishlist

Dear Santa Snover,

I met you briefly following a PowerCLI session at VMworld and gave you a part of my wishlist, but didn’t want to take up your time at that point. Here is my FULL Christmas wishlist!

  1. JSON support, like XML support. To Quote Jeff: “Watch this space.”
  2. A fix for the XML <item> bug! HERE!
  3. ISE in a web browser. Heck, I would even take just the console. Something like THIS. This would kinda give cross-platform support for those who use OSX or Linux.
  4. Intellisense in ISE for cmdlets and .NET namespaces. I love ISE because it is so lightweight. All the other IDEs I have tried are too slow. All I want to do is code.
  5. REST API support. This one is kinda iffy, as I can easily use [System.Net.Webclient], but built in XML/JSON validation would be sweet, or maybe even build a PSobject from an XML/JSON namespace.
  6. Multi-threading support that doesn’t require me to load 3rd party snapins for each job. Or, more specifically, doesn’t hog memory like it’s going out of style.
That’s it. Just six requests. I can’t guarantee whether I am on your naughty or nice list (I did mention Linux…), but these sure would be swell additions.

 
 

A cool Powershell polo would be nice too.

 
 

Cheers,
Jake Robinson