Showing posts with label Scripting. Show all posts
Showing posts with label Scripting. Show all posts

2018-08-15

Saving a Few Shekels on your AWS bill

I have a jumpbox that I use to access resources in the cloud – and I use it at work, only during work hours and only on workdays.

There are usually 720 work hours in the month or 744 in months that have 31 days. Assuming that I want to run the instance for 12 hours a day and for 5 days a week. In order to calculate how many hours exactly – we will need an example.

The month of August, 2018

image

The work week in Israel is Sunday-Thursday (yeah – I know – we are special…).

August would have 22 work days. Total number of hours in August (31*24 = 744). 220 working hours in the month (22 working days multiplied by 10 hours per day).

The math is simple 220/744 – I only need the instance for 30% of the month – so why would I pay for all of it?

744 hours * $0.0464 (for a t2.medium instance in us-east-2) = $34.5216 and if I was to only pay for the hours that I was actually using the instance that would be 220 * $0.0464 = $10.208. A third of the cost. Simple math.

So there are multiple ways to do this – as a Lambda script, Cloud custodian – each of these work – very well and will work brilliantly at scale. For me it was a single machine and honestly I  could not be bothered to set up all the requirements to get everything working.

Simple solution – use cron. I don’t pay for resource usage by hour in my corporate network (If someone does – then you have my sympathies..) so I set up a simple cron job to do this.

To start up the instance:

0 8 * * 0,1,2,3,4 start-jumpbox

And to stop the instance at night

0 18 * * 0,1,2,3,4 stop-jumpbox

And what is the start/stop-jumpbox comand you might ask – really simple aws cli command

aws ec2 start-instances –region <__REGION__>  --instance-ids <__INSTANCE_ID__>

aws ec2 stop-instances –region <__REGION__>  --instance-ids <__INSTANCE_ID__>

Of course in the background the correct credentials and access keys are set up on my linux box – not going to go into how to that here – AWS has enough documentation on that.

The last thing that I needed to solve was the jumpbox has a public IP (obviously) and If I really wanted to save the money – I do not want to have pay for a static Elastic IP provisioned and sitting there idle for 70% of the month (because the instance is powered down

After doing the calculation – it was chump change for my use case (524hrs * $0.005=$2.62) so maybe I should have not worried about it – but the resulted script is still useful.

I wanted to use the allocated IP address that AWS provides to the instance at no cost. The problem with this is – every time you stop the instance the IP address is reclaimed by AWS and when you power it on – you will get a new one.

Me being the lazy bum I am – I did not want to have to lookup up the IP each and every time so I went down the path of updating a DNS record on each reboot.

Take the Public IP allocated to the instance and update a known FQDN that I would use on a regular basis.

The script can be found here (pretty self explanatory)



Now of course this is only a single instance – but if you are interested in saving money this is one of the considerations you think about looking to save. (of course this should be managed properly at scale a single cron job will not suffice…)

For example – if you have a 1000 development machines that are not being used after working hours (and I know that not everything can be shut down after hours there are background jobs that run 24/7),  and they are not a measly t2.medium but rather an m4.large 

1000 (instances) * 220 (work hours) * $0.1 (cost per hour) = $22,200

1000 (instances) * 744 (hours in the month) * $0.1 (cost per hour) =  $74,400

See how you just saved $50k a month on your AWS bill?

You are welcome :)

(If you want spend a some your well saved cash on my new book – The Cloud Walkabout – feel free).

If you have any questions about the script / solution or just want to leave a comment – please feel free to do so below

2018-07-09

Comparing CloudFormation, Terraform and Ansible - Part #2

The feedback I received from the first comparison was great – thank you all.

Obviously the example I used was not really something that you would use in the real world – because no-one actually creates a only a VPC – and does not create anything inside it, that is pretty futile.

So let’s go to the next example.

The scenario is to create a VPC, with a public presences and a private presence. This will be deployed across two availability zones. Public subnets should be able to route to the internet through an Internet Gateway, private subnets should be able to access the internet through a NAT Gateway.

This is slightly more complicated than just creating a simple VPC with a one-liner

So to summarize - the end state I expect to have is:

  • 1x VPC (192.168.90.0/24)
  • 4x Subnets
    • 2x Public
      • 192.168.90.0/26 (AZ1)
      • 192.168.90.64/26 (AZ2)
    • 2x Private
      • 192.168.90.128/26 (AZ1)
      • 192.168.90.192/26 (AZ2)
  • 1x Internet Gateway
  • 2x NAT Gateway (I really could do with one – but since the subnets and resources are supposed to be deployed in more than a single AZ – there will be two – and here I minimize the risk impact of loss of service if a single AZ fails)
  • 1x Public Route Table
  • 2x Private Route Table (1 for each AZ)

And all of these should have simple tags to identify them.

(The code for all of these scenarios is located here  https://github.com/maishsk/automation-standoff/tree/master/intermediate)

First lets have a look at CloudFormation


So this is a bit more complicated than the previous example. I still used the native resources in CloudFormation, and set defaults for the my parameters. You will see some built in functions that are available in CloudFormation – namely !Ref which is a reference function to lookup a value that has previously been created/defined in the template and !Sub that will substitute a value in the template with an environment variable.

So there are a few nifty things that are going here.

  1. You do not have remember resource names – CloudFormation keeps all the references in check and allows you to address them by name in other places in the template.
  2. CloudFormation manages the order in which the resources are created and takes of care of all of that for – and it will take care of the order what resources are created.

    For example – the route table for the private subnets will only be created after the NAT gateways have been created.
  3. More importantly – when you tear everything down – then CloudFormation takes care of the ordering for you, i.e. you cannot tear down a VPC – while the NAT gateways and Internet gateway are still there – so you need to delete those first and then you can go ahead and rip the everything else up.


Lets look at Ansible. There are built-in modules for this ec2_vpc_net, ec2_vpc_subnet, ec2_vpc_igw, ec2_vpc_nat_gateway, ec2_vpc_route_table.


As you can see this is bit more complicated than the previous example – because the subnets have to be assigned to the correct availability zones.

There are are a few extra variables that needed to be defined in order for this to work.


Last but not least – Terraform.

And a new set of variables



First Score - # lines of Code (Including all nested files)

Terraform – 164

CloudFormation - 172

Ansible – 204

(Interesting to see here how the order has changed)

Second Score - Easy of deployment / teardown.

I will not give a numerical score here - just to mention a basic difference between the three options.

Each of the tools use a  simple command line syntax to deploy

  1. CloudFormation

    aws cloudformation create-stack --stack-name testvpc --template-body file://vpc_cloudformation_template.yml

  2. Ansible

    ansible-playbook create-vpc.yml

  3. Terraform

    terraform apply -auto-approve

The teardown is a bit different

  1. CloudFormation stores the information as a stack - and all you need to do to remove the stack and all of its resources is to run a simple command of:

    aws cloudformation delete-stack --stack-name <STACKNAME>

  2. Ansible - you will need to create an additional playbook for tearing down the environment - it does not store the state locally. This is a significant drawback – you have to make sure that you have the order correct – otherwise the teardown will fail. this means you need to understand as well how exactly the resources are created.

    ansible-playbook remove-vpc.yml

  3. Terraform - stores the state of the deployment - so a simple run will destroy all the resources

    terraform destroy -auto-approve

You will see below that the duration of the runs are much longer than the previous example – the main reason being that the amount of time it takes to create a NAT gateway is long – really long (at least 1 minute per NAT GW) because AWS does a lot of grunt work in the background to provision this “magical” resource for you.

You can find the full output here of the runs below:

Results

Terraform
create: 2m33s
destroy: 1m24s

Ansible:
create: 3m56s
destroy: 2m12s

CloudFormation:
create: 3m26s
destroy: 2m14s

Some interesting observations. It seems that terraform was the fastest one of the three – at least in this case.

  1. The times are all over the place – and I cannot say one of the tools is faster than the other because the process is something that happens in the background and you have to wait for it complete. SO I am not sure how reliable the timings are.
  2. The code for the Ansible playbook is by far the largest – mainly because in order to tear everything down – it requires going through the deployed pieces and ripping them out – which requires a complete set of code.
  3. I decided to compare how much more code (you could compare increase in the amount of code to increased complexity) was added from the previous create step to this one

    Ansible: 14 –> 117 (~8x increase)
    CloudFormation: 24 –> 172 (~x7 Increase)
    Terraform: 7 –> 105 (~x15 increase)
  4. It is clear to me that allowing the provisioning tool to manage the dependencies on its own – is a lot simpler to handle – especially for large and complex environments.


This is by no means a recommendation to use one tool or the other - or to say that one tool is better than the other - just a simple side by side comparison between the three options that I have used in the past.

Thoughts and comments are always welcome, please feel free to leave them below.

2018-06-27

Comparing CloudFormation, Terraform and Ansible - Simple example


Whenever someone asks me what tools do you use to provision your infrastructure within AWS - the answer is it can be done with a variety of tools - but people usually use one of the following three
The next question that comes up of course - is which one is easier/better to use? The answer of course (as always..) is - "It Depends". There are really good reasons to use each and everyone of the tools. This could be ease of use, support, extensibility, flexibility, community support (or lack thereof).

I have worked with all three tools, and each of them have their ups and their downs. There are periods that I prefer Ansible, other days that Terraform and sometimes CloudFormation is the only way to get things done.

I wanted to compare all three in a set of scenarios - from the really simple to moderate - to complicated. Firstly - to see how this can be accomplished in each of the tools,evaluating complexity, time to completion and anything else that came up along the way.

Let's start by diving straight into the first example.

I want to create a VPC. A plain simple VPC, nothing else. No Network, no NAT gateways, routes, subnets, as simple as can be. Essentially this is a simple AWS API call  which would be:



(The code for all of these scenarios is located here - https://github.com/maishsk/automation-standoff/tree/master/simple)

First lets have a look at CloudFormation

Looks pretty simple. I used the native resources in CloudFormation, and set defaults for the name and the CIDR block.

Lets look at Ansible. There is a built-in module for this ec2_vpc_net.

The only difference here is that the variables are split into a separate file (as per Ansible best practices

Last but not least - Terraform.

Here the provider is split out into a separate file and the variables into another file (Terraform best practices)

First Score - # lines of Code (Including all nested files)


Ansible - 19
CloudFormation - 28
Terraform - 29

Second Score - Easy of deployment / teardown.


I will not give a numerical score here - just to mention a basic difference between the three options.

Each of the tools use a  simple command line syntax to deploy

  1. CloudFormation

    aws cloudformation create-stack --stack-name testvpc --template-body file://vpc_cloudformation_template.yml

  2. Ansible

    ansible-playbook create-vpc.yml

  3. Terraform

    terraform apply -auto-approve

The teardown is a bit different
  1. CloudFormation stores the information as a stack - and all you need to do to remove the stack and all of its resources is to run a simple command of:

    aws cloudformation delete-stack --stack-name <STACKNAME>

  2. Ansible - you will need to create an additional playbook for tearing down the environment - it does not store the state locally. This is a drawback

    ansible-playbook remove-vpc.yml

  3. Terraform - stores the state of the deployment - so a simple run will destroy all the resources

    terraform destroy -auto-approve
The last one I wanted to address was the time it took to deploy/tear down the resources for each tool - and I think that the differences here are quite interesting.

I ran a for.. loop through 3 iterations to bring up the VPC and tear it down and timed the duration for each run.

You can find the full output of the runs below:

Results


CloudFormation
create: 31.987s
destroy: 31.879s

Ansible

create: 8.144s
destroy: 2.554s


Terraform

create: 17.452s
destroy: 12.652s

So to summarize - it seems that Ansible is the fastest one of them all - there are a number of reasons why this is the case (and I will go into more detail into this in a future post)

This is by no means a recommendation to use one tool or the other - or to say that one tool is better than the other - just a simple side by side comparison between the three options that I have used in the past.

Next blog post will go into a slightly more complicated scenario.

Thoughts and comments are always welcome, please feel free to leave them below.

2015-08-26

PowerShell Profile Tricks for Better VMware Management

My new post on some PowerShell Profile tricks for VMware has been published on the
Petri IT knowledgebase.

As an IT pro, we rely on scripts to manage our VMware environment, which helps us be more efficient throughout our work day. In this article, I’d like to share some PowerShell profile tricks that are specific to VMware. These are tricks that I use on a daily basis, which I think you’ll find helpful .. ..

Read the full post

2013-10-01

Interesting Tidbit in #PowerCLI Release Notes

With last week's release - there was also an updated version on PowerCLI that was released as well.

VMware vSphere PowerCLI 5.5 Release 1 Release Notes

I noticed something quite strange (personally I have not yet hit this bug - but I am sure it must have happened to someone)

image

That sounds like fun… :)

So I put a question up on the the PowerCLI community.

What exactly does this mean?
Will it run the first 2?
Last 2?
Even lines?
Odd lines?
Just choose which some random lines?

I received the answer from Dimitar and thought that it would be worthwhile to share it with you all.

What this means is that it will either run the whole script or just the first line. We haven't been able to work out exactly what causes this, but we believe it has something to do with the way we escape special symbols and redirect the output in the script before sending it to the guest OS.

This is not a new issue for this release, but we decided to add it to the release notes. There are a couple of workarounds for this
1) make the script single line
2) save the script in a .bat file, upload it to the guest using Copy-VMGuestFile and execute the file using Invoke-VMScript.

Something you might want to remember…

2013-05-20

Change Outlook Meetings En Masse

I had my mailbox migrated to a new domain today. One of the side effects of this was that for silly reason, a large number of my meetings now had a prefix of Copy: added to the subject of the meeting.

image

Which annoyed the hell out of me.

Now I could go ahead and remove all the extra information one by one - but that is tedious annoying and against my automation principles.

So starting with this post Retrieve all-day appointments in Outlook with PowerShell and this one as well Create Outlook Appointments from PowerShell, I have managed to change all the outlook appointments that have the extra text in the subject line with the following lines of code

$olApp = New-Object -COM Outlook.Application
$namespace = $olApp.GetNamespace("MAPI")
$fldCalendar = $namespace.GetDefaultFolder(9)
$items = $fldCalendar.Items
$copies = $items  | ? {$_.Subject -like "Copy:*" }
$copies | % {
	$newsubject = ($_.Subject).Trim("Copy: ")
	$_.Subject = $newsubject
	$_.Save()
}

Before

Before

After

After

Gotta Love PowerShell!!

2013-02-28

How To Deal With a Complex Project

You have been tasked with a task, it could be a long term project, a one-off thing. These usually involve identification of number of tasks and stages that need to be executed in order to complete the whole task.

One such an example that I would like to discuss today is the completion of a complex scripting task.

One script I have been nurturing is a deployment script for Oracle RAC on VMware. It actually has grown to be over 700 lines of code (perhaps one day I will be able to make it public).

I was asked not so long a go, “How do you manage to write such a long script? How or where do you even start?”. The answer to that question is the reason for this post.

I really love working in PowerShell and PowerCLI – that is no secret. I do dabble in other scripting languages as well but Powershell is still my favorite.

Scripting can be used in different ways.

  • One time quick and dirty tasks. You pop out a line of code which gets the specific information you want, and perhaps you will save it (you should) – just in case you need to do it again. These are also referred to as one-liners.
  • Functions – here you think a little more, I want to create a piece of code that can be used on a regular basis – make it re-usable and write it to be robust, mean and lean.
  • A process or a workflow. Now I know some of you will say that Powershell/PowerCLI is not really the ultimate tool to use for running complex tasks with multiple scenarios and use cases – and I must say I agree. On the other hand – vCenter Orchestrator has a steep learning curve. Using a tool that you already know and are familiar with will make it easier. Easier to write, optimize, troubleshoot, and document.

No matter which tool you use the methodology will be the same. This is how I do it – and perhaps this can help you too.

  1. Envision the process from a high level perspective
  2. Layout the steps you need to perform in order to get there
  3. For each step – detail what exactly needs to happen
  4. Write the code you need to get it done.

It is best to explain this by walking through an example (without actually writing the code)

Each VM that is created is usually done by an admin/user. You would like to have some way of knowing who created the VM, when and for whom (owner/department).

So how would you go about doing this?

Step 1 – Envision the process

This is how I see this – from a very high level.

Envision the process

Someone creates a VM, the details are filled in, and a report is sent out. Look at it from the perspective of upper management – they do not care about the details, how it works, they look at the process.

That was the easy part.

Step 2 - Layout the steps

When a VM is created it is logged, and these logs can be parsed or checked for specific events. From that information you will extract the information needed (Date, Created) and attach that info to the VM, the easiest way would be to add it to a custom field for the VM. This process will be performed for each of the new VM's that were found.

All of this information should should be collected into a readable format that can be sent as a report, either a file or an HTML page.

Layout the Steps

Step 3 – Describe each step in detail

VM Created

A VM is  born

A VM is born. How do you know that this happens? Well pretty simple. Go through the events that were created in vCenter. But you have to remember of course that there are several ways to create a VM and these could be:

  1. Deploy from OVF
  2. Deploy from Template
  3. Create From Scratch
  4. Clone from Existing VM

Not all of these have the same events registered in the Events and Task so you will have to look for all of them to catch all VM's created.

You really do not want to scan all the events (from time immemorial) so you should decide on a timeframe - an this will be the period you will check against.

So if I were to layout my steps they would be as follows.

## Define Period to search - 24 hours
## Get All events for VM created
    ## Cloned VM's
    ## Deployed from OVF
    ## Created from Scratch
    ## Cloned From Existing

But in order to even begin to get events I would need a few things (seeing that I will be running this as a scheduled script):

  1. PowerCLI
  2. Connection to vCenter

So my steps will change a bit to look like the following:

### Validate connection to vCenter ###

## Check for PowerCLI
## Check for Connection
    ## If not connected then connect
        ## Need vCenter Name and credentials

### Get All VM's Created ###

## Define Period to search - 24 hours
## Get All events for VM created and store in a variable
    ## Cloned VM's
    ## Deployed from OVF
    ## Created from Scratch
    ## Cloned From Existing

OK So now I have all the VM's created within the past 24 hours. Here would be my next steps.

Update Fields

From the event Update Fieldsdetails of each event for each VM - get who the user was that created it and when.
Populate that into a Custom field/Tag for each VM.

So if I were to layout my steps they would be as follows.

### Update details for each VM

## Go through each event
## Update Created By
    ## Extract Username that created the VM
        ## Get proper Name from Active Directory
            ## Need access to Active Directory - AD Powershell Module
        ## Update Custom Field / Tag
        ## Save to report
## Update Date Created
    ## Extract Date when VM was created
        ## Convert date to something that can be indexed
        ## Update Custom Field / Tag
        ## Save to report
## Update Owner
    ## Check if Owner was already updated
        ## If not - send email to user that created it to update the field
            ## Need to get email address of creator
            ## SMTP server variable
            ## From address variable
            ## Subject variable
            ## Body to be sent in email
        ## Save notification to report

Create Report

Now to send out the report.

## Take information from report
## Convert to something readable
    ## Export to Excel file
    ## Convert the information to HTML so it can be re-used
## Send Email
    ## SMTP server variable
    ## From address variable
    ## To address variable
    ## Subject variable
    ## Body to be sent in email

Step 4 – Write the code in detail

Now you know what the steps are, it is now time to write the code for each and every step, and this is where most of the work will be. It could be that during the writing of the code you will see that you need to perform additional steps in order to accomplish what you would like, and if so - continue writing out the stages and fill in the appropriate code. Such an example would be - this should be run as a scheduled task which means you will need to provide a method to pass the credentials to the script. Another example would be - you already sent an email to the Creator saying that they should update the Owner, and if they did not - do you leave it? Send them another email? Escalate the issue? For each of these scenarios, there are steps that need to identified, and the appropriate code written.

Another thing I like about this methodology is that it already partially provides some basic documentation for your script - something that is very important - so the code can be re-used in other scenarios as well.

I will not be providing the code for the steps, I will leave that task for you.

Summary

Envision at a high level what you want to accomplish, identify what are the needed stages for this to succeed, and break each of these stages into small steps, and detailed again further into more steps until you have each and every step as part of your plan.

I used a PowerCLI script here as an example - but this methodology can be applied to any project.

  • VCDX Certification
  • Upgrade from 5.0 to 5.1
  • Implementation of vCloud in your organization
  • Upgrade Active Directory to 2012

This is the methodology that I use - I hope that it will be of some use to you.

2013-01-24

Configuring SSH Equivalence for Oracle RAC

SSH Equivalence is one of the pre-requisites needed for an Oracle RAC installation. Scripting Fu
There are a number of posts on how to do this like here or here, and Oracle even have been so kind as to provide a script that will do this for you (even though it is not 100% automated.
The process is relatively simple (when you break it down piece by piece)
  1. Create the .ssh directory under the users /home folder for VM1 and VM2
  2. Create an RSA key on VM1 and VM2
  3. Copy the contents of ~/.ssh/id_rsa.pub from VM1 and VM2 into ~/.ssh/authorized_keys on both VM1 and VM2
  4. You should then be able to connect to each host (and also the localhost as well) without a password prompt.
  5. Repeat the process on both VM’s with the oracle user
But this process requires a decent amount of manual interaction from the user at the following stages:
  1. Copying the files between VM1 <-> VM2
  2. First connection prompts to add the hosts key to the ~/.ssh/known_hosts file
Manual interaction is the mother of all headaches when you want to automate something. As I have posted before here and here I am in the middle of automating a Oracle RAC deployment on VMware. This is an additional part of the solution.
I had to come up with a method to do this without any user interaction, and here is how I went about the process. I broke down the whole process – stage by stage.
  1. Re-create the ssh_host_rsa_key – the reason for this being – that since these VM’s are deployed from the same template – the ssh_host_rsa_key is identical – and this caused problems for my script (this actually could be useful in some cases – but not here).
  2. Create the ~/.ssh/id_rsa.pub key for the root user on each host – without prompts.
  3. In order to prevent the popup when connecting to another VM for the first time I needed to get the keys from ssh_host_rsa_key.pub into the .ssh/known_hosts before I connected to the VM for the first time.
  4. Add the public key from each VM into the ~/.ssh/authorized_keys file.
  5. Get this information from VM1 to VM2 and and vice-versa – and all of this without prompts – which meant I could not go through the guest operating system.
  6. Repeat the process for the oracle user.
So my initial challenge was how to do the copying of the files without going through the guest OS, but that actually turned out to be pretty simple. PowerCLI has the Copy-VMGuestFile cmdlet that will allow me to transfer files to and from the guest – so that solved my worries.
There were several issues along the way that I needed to address.
  1. I needed to construct the known_hosts file based on several pieces of information, the hostname, IP address and the ssh_host_rsa_key, I am sure there is a easier way of doing this in bash – but this way works for me.
  2. Creating the rsa keys for the oracle user – since I did not want to connect to the VM twice with two different credentials – here I solved the problem by duplicating the files from the root user to the oracle user and manipulated the contents a bit to suit my needs.
  3. Copying the files back to the guest after manipulation – resulted in a change in their format from UNIX to DOS and I could not find a way to control that from the PowerCLI side – therefore some vi manipulation was needed to convert them back.
So without further ado – here is the script – annotations are at the bottom
<#
 .SYNOPSIS
  Configure SSH equivalence between two Oracle RAC nodes

 .DESCRIPTION
  The script will execute on both guests, configure the RSA keys,
  known_hosts and authorized_keys files on each host for both the 
  root and oracle user to enable SSH equivalence for Oracle RAC

 .PARAMETER  VM1
  Name of the first VM
 .PARAMETER  VM2
  Name of the first VM
 .PARAMETER  VM1_IP
  The IP address of the first VM
 .PARAMETER  VM2_IP
  The IP address of the second VM
 .PARAMETER  HostCredentials
  The credentials for the ESXi host
 .PARAMETER  GuestCredentials
  The credentials for the guest VM 
 .PARAMETER Cleanup
  Will cleanup the temporary files created. On by default
 
 .EXAMPLE
  PS C:\> Set-SSHKeys -VM1 hosta -VM2 hostb -VM1_IP 10.10.10.1 -VM2_IP 10.10.10.2
  This example shows how to call the Configure-SSHKeys against hosta with the IP address
  of 10.10.10.1 and hostb with the IP address of 10.10.10.2.
 .EXAMPLE
  PS C:\> Set-SSHKeys -VM1 hosta -VM2 hostb -VM1_IP 10.10.10.1 -VM2_IP 10.10.10.2 -HostCredentials `
  (Get-Credential) -GuestCredentials (Get-Credential) -Cleanup:$false
  This example shows how to call the Configure-SSHKeys against hosta with the IP address
  of 10.10.10.1 and hostb with the IP address of 10.10.10.2. while prompting for credentials
  for both the host and the guest and not cleaning up the files after completion.

 .NOTES
  Author: Maish Saidel-Keesing
  Date: 20 January, 2012
  For more in depth info on the script please see:
  http://technodrone.blogspsot.com/2013/01/set-sshkeys.html

#>
function Set-SSHKeys {
 [CmdletBinding()]
 param(
  [Parameter(Position=0, Mandatory=$true)]
  [System.String]$VM1,
  [Parameter(Position=1, Mandatory=$true)]
  [System.String]$VM2,
  [Parameter(Position=2)]
  [System.String]$VM1_IP,
  [Parameter(Position=3)]
  [System.String]$VM2_IP,
  $HostCredentials,
  $GuestCredentials,
  $Cleanup=$true
 )
 # Check for parameters
 if (!$HostCredentials) {
  $HostCredentials = $Host.ui.PromptForCredential("ESXi Host Credentials","Enter the credentials for the ESXi Host","root","")
 }
 if (!$GuestCredentials) {
  $GuestCredentials = $Host.ui.PromptForCredential("Guest VM Credentials","Enter the credentials for the guest VM","root","")
 }
 if (!$VM1_IP) {
 $VM1_IP = (Get-VMGuestNetworkInterface -Name eth0 -vm $VM1 -HostCredential $HostCredentials -GuestCredential $GuestCredentials).IP
 }
 if (!$VM2_IP) {
 $VM2_IP = (Get-VMGuestNetworkInterface -Name eth0 -vm $VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials).IP
 }
## script to be executed on VM1
$myscript1 = @"
mv /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.old
mv /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.old
ssh-keygen -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key
ssh-keygen -t dsa -N "" -f /etc/ssh/ssh_host_dsa_key
mkdir ~/.ssh
ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
echo -n `$(hostname -s) >> .ssh/known_hosts
echo -n "," >> .ssh/known_hosts
echo -n $VM1_IP >> .ssh/known_hosts
echo -n " " >> .ssh/known_hosts
cat /etc/ssh/ssh_host_rsa_key.pub >> .ssh/known_hosts
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
mkdir /home/oracle/.ssh
cp .ssh/* /home/oracle/.ssh/
chown -R oracle:dba /home/oracle/.ssh
"@
## script to be executed on VM2
$myscript2 = @"
mv /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.old
mv /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.old
ssh-keygen -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key
ssh-keygen -t dsa -N "" -f /etc/ssh/ssh_host_dsa_key
mkdir ~/.ssh
ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
echo -n `$(hostname -s) >> .ssh/known_hosts
echo -n "," >> .ssh/known_hosts
echo -n $VM2_IP >> .ssh/known_hosts
echo -n " " >> .ssh/known_hosts
cat /etc/ssh/ssh_host_rsa_key.pub >> .ssh/known_hosts
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
mkdir /home/oracle/.ssh
cp .ssh/* /home/oracle/.ssh/
chown -R oracle:dba /home/oracle/.ssh
"@
 # run the scripts on VM1 and VM2
 Invoke-VMScript -vm $VM1 -ScriptText $myscript1 -ScriptType bash -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 Invoke-VMScript -vm $VM2 -ScriptText $myscript2 -ScriptType bash -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 ## authorized_keys for root
 # get files from guests
 Copy-VMGuestFile -GuestToLocal -Source /root/.ssh/authorized_keys -Destination ./authorized_keys_VM1_root -VM $VM1 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 Copy-VMGuestFile -GuestToLocal -Source /root/.ssh/authorized_keys -Destination ./authorized_keys_VM2_root -VM $VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 Copy-VMGuestFile -GuestToLocal -Source /home/oracle/.ssh/authorized_keys -Destination ./authorized_keys_VM1_ora -VM $VM1 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 Copy-VMGuestFile -GuestToLocal -Source /home/oracle/.ssh/authorized_keys -Destination ./authorized_keys_VM2_ora -VM $VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 # Change root to oracle to fix running the script with root credentials
 Get-Item .\authorized_keys_*ora | % {
 (get-content $_).Replace("root@","oracle@") | Set-Content $_ -Force
 }
 # concatenate contents of files
 (Get-Content ./authorized_keys_VM1_root) + "`r`n" + (Get-Content ./authorized_keys_VM2_root) + "`r`n" + (Get-Content ./authorized_keys_VM1_ora) + "`r`n" + (Get-Content ./authorized_keys_VM2_ora) | Out-File -FilePath ./authorized_keys -Encoding ascii
 # copy files back 
 Copy-VMGuestFile -LocalToGuest -Source ./authorized_keys -Destination /root/.ssh/ -VM $VM1 -HostCredential $HostCredentials -GuestCredential $GuestCredentials -Force
 Copy-VMGuestFile -LocalToGuest -Source ./authorized_keys -Destination /root/.ssh/ -VM $VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials -Force
 $vicmd = "/bin/vi +':w ++ff=unix' +':q' .ssh/authorized_keys"
 $return1 = Invoke-VMScript -ScriptText $vicmd -vm $VM1,$VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 ## known_hosts for root
 # get files from guests
 Copy-VMGuestFile -GuestToLocal -Source /root/.ssh/known_hosts -Destination ./known_hosts_VM1 -VM $VM1 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 Copy-VMGuestFile -GuestToLocal -Source /root/.ssh/known_hosts -Destination ./known_hosts_VM2 -VM $VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 # concatenate contents of files
 (Get-Content ./known_hosts_VM1) + "`r`n" + (Get-Content ./known_hosts_VM2) | Out-File -FilePath ./known_hosts -Encoding ascii
 # copy files back 
 Copy-VMGuestFile -LocalToGuest -Source ./known_hosts -Destination /root/.ssh/ -VM $VM1 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 Copy-VMGuestFile -LocalToGuest -Source ./known_hosts -Destination /root/.ssh/ -VM $VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 $vicmd = "/bin/vi +':w ++ff=unix' +':q' .ssh/known_hosts"
 $return1 = Invoke-VMScript -ScriptText $vicmd -vm $VM1,$VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials 
 ## authorized_keys for oracle
 # copy files back 
 Copy-VMGuestFile -LocalToGuest -Source ./authorized_keys -Destination /home/oracle/.ssh/ -VM $VM1 -HostCredential $HostCredentials -GuestCredential $GuestCredentials -Force
 Copy-VMGuestFile -LocalToGuest -Source ./authorized_keys -Destination /home/oracle/.ssh/ -VM $VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials -Force
 $vicmd = "/bin/vi +':w ++ff=unix' +':q' /home/oracle/.ssh/authorized_keys"
 $return1 = Invoke-VMScript -ScriptText $vicmd -vm $VM1,$VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials 
 ## known_hosts for Oracle
 # copy files back 
 Copy-VMGuestFile -LocalToGuest -Source ./known_hosts -Destination /home/oracle/.ssh/ -VM $VM1 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 Copy-VMGuestFile -LocalToGuest -Source ./known_hosts -Destination /home/oracle/.ssh/ -VM $VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials
 $vicmd = "/bin/vi +':w ++ff=unix' +':q' /home/oracle/.ssh/known_hosts"
 $return1 = Invoke-VMScript -ScriptText $vicmd -vm $VM1,$VM2 -HostCredential $HostCredentials -GuestCredential $GuestCredentials 
 # remove temporary files
 if ($Cleanup) {
  Get-Item .\authorized_keys*, .\known_hosts* | Remove-Item -Confirm:$false 
 }
}
Lines 45-57 - The script requires some parameters (two are mandatory). The name of the VM’s that will be configured, their IP addresses, and if you would like to not remove the files created during the process, you should change the $Cleanup variable to $false (by default $true). Also in order to run scripts on the guests you will need to provide credentials for the hosts and the guests (I am assuming that all hosts have one password and also the guests have one password as well).

Lines 59-70 - If the credentials were not provided as variables – then you will be prompted. If the IP’s were not provided, they will be retrieved through the API.

Lines 72-106 - The script that should be run on the guests. There is one for each VM – due to the fact that the IP is (of course) different on each of them.
A bit more details about the script that is run on the guest. 
Lines 73-76 - The guest SSH keys are re-created as I explained above
Lines 77-78 – Create the .ssh directory and create the keys. –N is to set a blank password on the key and –f is for the path. 
Lines 79-83 - The known_hosts file is basically a concatenation of 3 things for each entry:
<hostname>, <IP_Address> <Contents of rsa_key.pub> (The commas and spaces are important!) 
Line 84 - Add the contents of id_rsa.pub to the authorized_keys file. 
Lines 85-87 – Copy the files into the oracle user’s directory and make sure sure the file ownership is correct.
Lines 108-109 – Run the scripts on each VM.

Lines 112-115 – Copy the files to the local computer for text manipulation.

Lines 117-118 – The authorized_keys are per user, and the ones we created for the oracle user were copies of those from the root user, so the username has to be changed.

Line 121 – Combine all 4 authorized_keys files into one, with carriage returns after each one.

Lines 123-126 – Copy the files back to the guests. And as I said above, the files needed some additional vi manipulation because during the copy back – they file type was incorrect.

Lines 129-137 – The same process for the known_hosts file. Take note – only one copy from each guest was needed, that is because it is VM specific and not user specific.The same vi manipulation as well.

Lines 140-149 – The process is repeated to place the files in the oracle user’s home directory.

Lines 151-153 – Cleanup the files – done by default.

2013-01-17

Another PowerShell vExpert.me URL Shortner

Building on Jonathan Medd’s excellent idea of Using PowerShell to access the vExpert.me URL Shortener, I decided to improve it a bit more.
Here is the completed script.
<#
 .SYNOPSIS
  Will create a new vExpert.me URL

 .DESCRIPTION
  Using the Invoke-Rest Cmdlet to invoke a creation of a new vExpert.me URL

 .PARAMETER  URL
  URL that should be shortened.
 
 .PARAMETER Custom
  The custom URL that should be used.

 .EXAMPLE
  PS C:\> New-vExpertURL -URL 'http://www.google.com'
  This example shows how to call the New-vExpertURL function with with the URL parameter and generate a random URL.

 .EXAMPLE 
  PS C:\> New-vExpertURL -URL 'http://www.google.com' -Custom this_is_my_link
  This will create a custome URL of http://vexpert.me/this_is_my_link pointing to http://www.google.com
 .INPUTS
  System.String

 .OUTPUTS
  System.String

 .NOTES
  For more information about advanced functions, call Get-Help with any
  of the topics in the links listed below.

#>
function New-vExpertURL {
 [CmdletBinding()]
 param(
  [Parameter(Position=0, Mandatory=$true)]
  [System.String]$URL,
  [Parameter(Position=1)]
  [System.String]$Custom
 )
 begin {
 if (!$($Custom) ) {
  $baseurl_2 = "&action=shorturl&format=json&url="
  } else {
  $baseurl_2 = "&action=shorturl&keyword=" + $Custom + "&format=json&url="
 }
 $baseurl_1 = "http://vexpert.me/yourls-api.php?signature="
 $secret = "xxxxxxxxx"
 
 }
 process {
 $invokeurl = $baseurl_1 + $secret + $baseurl_2 + $URL
 $vExpertme = Invoke-RestMethod -Uri $invokeurl
 $vExpertme.shorturl | clip
 Write-Host "The shortenend URL is now in your clipboard" -ForegroundColor Green
 }
 end {
 }
}
It is quite self explanatory. You will need to enter your personal secret code in Line 47.
So I added 4 things
  1. This is now a function – and it accepts parameters.
  2. One of the parameters is CustomURL which will allow you to enter your custom text if you please.
  3. The output will provide the URL and a success message.
  4. The URL will now be in your clipboard so you can use it.

2013-01-03

PowerCLI Does not officially support Powershell v3

Just a heads up. According to the Release Notes PowerCLI does is not supported

Release Notes

Does this mean that it will not work – No of course not!  From what I have tested it works almost flawlessly – but there are some quirks…

For example - Set-NetworkAdapter returning 'Operation is not valid due to the current state of the object' and Error with Move-VM: Operation is not valid due to the current state of the object.

And as Luc put it, “Well, you can't file a bug for something that is not supported, now can you Smile

Update – February 12th, 2013

VMware have now released an updated version – see the announcement here - PowerCLI 5.1 Release 2 Now Available

This will actually render this blog post obsolete – but I am happy VMware have addressed this issue.

2013-01-02

Invoke-VMScript Failed - and how I was Baffled.

Luc Dekens wrote a great post a while back Will Invoke-VMScript work? about the prerequisites needed in order to get Invoke-VMscript to work. Stop for a minute and go and read his post.

Glad to have back.

As part of an Oracle RAC provisioning script that I am working on – one of the first things I wanted to do was to configure the network settings for my two nodes – with parameters taken from a config file.

Of course if the VM does not have an IP address yet then you cannot configure it through the network, so here is where Invoke-VMscript comes into play. Huh?

A few things first off the bat. My configuration was working also with the 32-bit engine but also with the 64-bit engine as well. The rest of the prerequisites were all there.

So here is what was happening. In the script I had stored the HostCredentials and the Guestcredentials each in a variable. When it came time power on the VM’s,The script would wait for the VMware tools to start running in the guest before executing the command and then run my script inside the guest OS – but the command would fail with this message.

Invoke-VMScript : 02/01/2013 15:17:07    Invoke-VMScript        Error occured while executing script on guest OS in VM
'testdbCA1b'. Could not locate "Powershell" script interpreter in any of the expected locations. Probably you do not have enough permissions to execute command within guest.
At line:5 char:1
+ Invoke-VMScript -ScriptText $bb -vm $dbvm2 -HostCredential $hostcreds -GuestCred ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (testdbCA1b:VirtualMachineImpl) [Invoke-VMScript], VimException
    + FullyQualifiedErrorId : Client20_VmGuestServiceImpl_RunScriptCore_ExeLookupFailed,VMware.VimAutomation.ViCore.Cmdlets.Commands.InvokeVmScript

Now this was really weird – because this was completely not true. To make it even more baffling – when trying to run the commands manually – not as part of the script – it would work – without a problem. So I was starting to think that perhaps there was a problem that the credentials were not being passed properly down to the command – which was not likely – but still I had no other clue.

I then wondered – Invoke-VMscript – interacts with the VMware Tools in the guest through VIX – so maybe there was a problem there.

So I checked my versions (it was 1.12) and looked at the release notes and saw something there that ultimately led me onto the right path.

VIX

OK I was not getting any of these errors, but my command would not work. So I wanted to see what the logs of the VMware Tools in the guest were saying – after all it was interacting with the guest through VMware Tools. But where was the log?

KB1007873 - Enabling debug logging for VMware Tools within a guest operating system showed me the way to enable VMware Tools logging in a Linux VM.

You need to create a file (if it does not exist) /etc/vmware-tools/tools.conf and add to that file:

log = true
log.file = /tmp/vmtools.log

I then performed the following to test my theory.

  1. Restart the VM
  2. Wait for the tools to report Running and up-to-date
  3. Invoke-VMscript

This I did with a simple PowerShell script.

Restart-VM -VM $dbvm2 -Confirm :$false
Sleep 10
while (((get-vm $dbvm2 ).ExtensionData.Guest.ToolsRunningStatus ) -ne "guestToolsRunning" ) {
Write-Host "....." -ForegroundColor Yellow
Sleep 5
}

Invoke-VMScript -ScriptText $bb -vm $dbvm2 -HostCredential $hostcreds -GuestCredential $guestcreds
while ( $? -eq $false ) {
Get-Date -Format HH :mm :ss
sleep 2
Invoke-VMScript -ScriptText $bb -vm $dbvm2 -HostCredential $hostcreds -GuestCredential $guestcreds
}

So I noticed a few things.

  1. VMware Tools comes up and reports itself as running – way before the OS is actually available – and that you have a console prompt.
    Even before SSH starts
  2. The first 3-5 tries of Invoke-VMscript would fail – with the same error message I had before. And suddenly it would work as if nothing was wrong.

I went to look in the VMware Tools log that I had just configured and there I found something which I find very strange – but did solve my mystery but I still do not have the answer as to why it is happening.

For the failed attempts I had this in the log.

[Jan 02 13:17:06.925] [   debug] [vix] VixTools_StartProgram: args: progamPath: 'cmd.exe', arguments: '/C powershell -NonInteractive -EncodedCommand cABvAHcAZQByAHMAaABlAGwAbAAuAGUAeABlACAALQBPAHUAdABwAHUAdABGAG8AcgBtAGEAdAAgAHQAZQB4AHQAIAAtAE4AbwBuAEkAbgB0AGUAcgBhAGMAdABpAHYAZQAgAC0AQwBvAG0AbQBhAG4AZAAgACcAJgAgAHsAbABzACAALQBsAGEAfQAnACAAPgAgACIALwB0AG0AcAAvAHAAbwB3AGUAcgBjAGwAaQB2AG0AdwBhAHIAZQAwACIAOwAgAGUAeABpAHQAIAAkAGwAYQBzAHQAZQB4AGkAdABjAG8AZABlAA=='', workingDir: '

[Jan 02 13:17:11.988] [   debug] [vix] VixTools_StartProgram: args: progamPath: 'cmd.exe', arguments: '/C powershell -NonInteractive -EncodedCommand cABvAHcAZQByAHMAaABlAGwAbAAuAGUAeABlACAALQBPAHUAdABwAHUAdABGAG8AcgBtAGEAdAAgAHQAZQB4AHQAIAAtAE4AbwBuAEkAbgB0AGUAcgBhAGMAdABpAHYAZQAgAC0AQwBvAG0AbQBhAG4AZAAgACcAJgAgAHsAbABzACAALQBsAGEAfQAnACAAPgAgACIALwB0AG0AcAAvAHAAbwB3AGUAcgBjAGwAaQB2AG0AdwBhAHIAZQAwACIAOwAgAGUAeABpAHQAIAAkAGwAYQBzAHQAZQB4AGkAdABjAG8AZABlAA=='', workingDir: '

But for the successful attempt the log showed (which was what I expected)

[Jan 02 13:17:23.211] [   debug] [vix] VixTools_StartProgram: args: progamPath: '/bin/bash', arguments: '-c "bash > /tmp/powerclivmware0 2>&1 -c \"ls -la\""'', workingDir: '
[Jan 02 13:17:23.211] [   debug] [vmsvc] Executing async command: '"/bin/bash" -c "bash > /tmp/powerclivmware0 2>&1 -c \"ls -la\""' in working dir '/root'
[Jan 02 13:17:23.214] [   debug] [vix] VixToolsStartProgramImpl started '"/bin/bash" -c "bash > /tmp/powerclivmware0 2>&1 -c \"ls -la\""', pid 3792

ID-10081549Now here is the weird part. If you look at the first two failures – you will see that VIX trying to execute a Windows command on a Linux operating system – which…. probably .. won’t…. really…. work…. !!!

Only about 20 seconds later – did it execute the correct bash command – in my case ‘ls –la’ and it worked of course.

So here I found my way around my problem – but have not gotten to the bottom of the mystery yet. I put in an extra sleep statement into the script that would wait a bit longer until the OS was completely up and only then run the Invoke-VMscript command – and all was working fine…

So a few things I learned today:

  • How to enable logging for VMware Tools
  • VIX does weird things.
  • A workaround is as good of a solution as any other.
  • I would add one more thing to Luc’s prerequisites – wait until the VM has completely started before attempting to use Invoke-VMscript.

2012-12-27

Creating an EagerZeroedThick disk with PowerCLI

Hey…. -  that is not possible – I hear you say – well in principle you are right. Up until today…

By mistake of course – I found that there was a change made to the 5.1 release of PowerCLI – but this change has not been documented anywhere – which I think is a shame. This post is the only public reference I know of.

Up until the 5.1 release you could not create an EagerZeroedThick hard disk with PowerCLI. Let’s look at the 5.0 documentation for PowerCLI.

New-Harddisk

As you can see above the options are Thin or Thick and if you go to look at the
VirtualDiskStorageFormat – Enum you will see the that there is no EagerZeroedThick option.

Enum

The PowerCLI changelog has a new Cmdlet Move-Harddisk – which as you can see allows you to migrate a VMDK from one location to another – and if you also noticed..

5.1 New-Harddisk

Yep, EagerZeroedThick is one of the options as you can see above. The VirtualDiskStorageFormat – Enum was not updated though.

So I used this today as part of a bigger automation process to prepare some VM’s for Oracle RAC (which I will post about in the not too distant future)

get-vm $vm1 | New-HardDisk -DiskType flat -CapacityGB 2 -StorageFormat EagerZeroedThick -Datastore $dbds |New-ScsiController -Type ParaVirtual -BusSharingMode NoSharing

Which is so much easier than… (taken from Luc Deken’s post)

$vmName = <vm-name>
$vCenter = <vCenter-name>
$esxAccount = <ESX-account>
$esxPasswd = <ESX-password>

function Set-EagerZeroThick{
	param($vcName, $vmName, $hdName)

# Find ESX host for VM
	$vcHost = Connect-VIServer -Server $vcName -Credential (Get-Credential -Credential "vCenter account")
	$vmImpl = Get-VM $vmName
	if($vmImpl.PowerState -ne "PoweredOff"){
		Write-Host "Guest must be powered off to use this script !" -ForegroundColor red
		return $false
	}

	$vm = $vmImpl | Get-View
	$esxName = (Get-View $vm.Runtime.Host).Name
# Find datastore path
	$dev = $vm.Config.Hardware.Device | where {$_.DeviceInfo.Label -eq $hdName}
	if($dev.Backing.thinProvisioned){
		return $false
	}
	$hdPath = $dev.Backing.FileName

# For Virtual Disk Manager we need to connect to the ESX server
	$esxHost = Connect-VIServer -Server $esxName -User $esxAccount -Password $esxPasswd

# Convert HD
	$vDiskMgr = Get-View -Id (Get-View ServiceInstance -Server $esxHost).Content.VirtualDiskManager
	$dc = Get-Datacenter -Server $esxHost | Get-View
	$taskMoRef = $vDiskMgr.EagerZeroVirtualDisk_Task($hdPath, $dc.MoRef)
	$task = Get-View $taskMoRef
	while("running","queued" -contains $task.Info.State){
		$task.UpdateViewData("Info")
	}

	Disconnect-VIServer -Server $esxHost -Confirm:$false

# Connect to the vCenter
	Connect-VIServer -Server $vcName -Credential (Get-Credential -Credential "vCenter account")
	if($task.Info.State -eq "success"){
		return $true
	}
	else{
		return $false
	}
}

Set-EagerZeroThick $vCenter $vmName "Hard disk 1"

Ah the little things…..

I wonder what other gems are hidden deep in the bowels of PowerCLI that were not updated in the changelog/release notes?? …. What do you think ??

2012-10-16

Using Powershell to install PowerCLI

I am currently preparing a deployment package architecture for a full vSphere environment and one of the requests was to include PowerCLI in the installation script.

I was surprised that I could not find anything already mentioned on how to do this on Google.

So here is the syntax:

# Install PowerCLI
Set-executionPolicy RemoteSigned -Confirm:$false -force
Write-Host "Installing PowerCLI"
$myargs = $myargs = '/q /s /w /L1033 /v" /qn '
$exe = "C:\installs\VMware-PowerCLI-5.1.0-793510.exe"
Start-process $exe $myargs –Wait
Add-PSSnapin -Name VMware.VimAutomation.Core

Line 2:
Set the execution policy to RemoteSigned

Line 4-5: Prepare the installation syntax

Line 7: Add the PowerCLI Snapin

Easy as that!

2012-09-10

A Powershell Script to notify you on the vSphere 5.1 Release

vSphere 5.1 will most probably be released by the end of September 10th (that is 2 minutes from now in my timezone).

Of course nothing is 100% certain but I will explain on what this assumption is based.

  1. From this Press release 
    Press Release 1 
  2. From the VMware Unveils Industry’s Most Comprehensive Cloud Infrastructure and Management Solution press release
    Press Release 2
  3. September 11, 9-11. This is a sensitive date for especially for Americans. I doubt that anyone, let alone VMware, would go all out with the release multiple new versions of their software on this day. I assume it will be before that.
  4. vCAT 3.0 release end of day September 10, 2012

    vCAT
  5. vSphere 5.0 is available for download from here – but 5.1 is not – yet…

    Not yet

So instead of pushing F5 the whole day I wrote a very simple Powershell script to do it for me.

F5
F5

The basic function was taken from this post Using PowerShell to Query Web Site Information

Then the rest was easy.

Get the page. Parse the HTML and see if it still contains the Unable to Complete Your Request text.

If it does – that means the 5.1 site has not gone live. Sleep for 5 minutes, and check again.

If the text is no longer there – then probably the 5.1 bits are available. In that case – do something (like send me an email)

function Get-WebPage {
<#  
.SYNOPSIS  
   Downloads web page from site.
.DESCRIPTION
   Downloads web page from site and displays source code or displays total bytes of webpage downloaded
.PARAMETER Url
    URL of the website to test access to.
.PARAMETER UseDefaultCredentials
    Use the currently authenticated user's credentials  
.PARAMETER Proxy
    Used to connect via a proxy
.PARAMETER Credential
    Provide alternate credentials 
.PARAMETER ShowSize
    Displays the size of the downloaded page in bytes                 
.NOTES  
    Name: Get-WebPage
    Author: Boe Prox
    DateCreated: 08Feb2011        
.EXAMPLE  
    Get-WebPage -url "http://www.bing.com"
    
Description
------------
Returns information about Bing.Com to include StatusCode and type of web server being used to host the site.

#> 
[cmdletbinding(
	DefaultParameterSetName = 'url',
	ConfirmImpact = 'low'
)]
    Param(
        [Parameter(
            Mandatory = $True,
            Position = 0,
            ParameterSetName = '',
            ValueFromPipeline = $True)]
            [string][ValidatePattern("^(http|https)\://*")]$Url,
        [Parameter(
            Position = 1,
            Mandatory = $False,
            ParameterSetName = 'defaultcred')]
            [switch]$UseDefaultCredentials,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = '')]
            [string]$Proxy,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = 'altcred')]
            [switch]$Credential,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = '')]
            [switch]$ShowSize                        
                        
        )
Begin {     
    $psBoundParameters.GetEnumerator() | % { 
        Write-Verbose "Parameter: $_" 
        }
   
    #Create the initial WebClient object
    Write-Verbose "Creating web client object"
    $wc = New-Object Net.WebClient 
    
    #Use Proxy address if specified
    If ($PSBoundParameters.ContainsKey('Proxy')) {
        #Create Proxy Address for Web Request
        Write-Verbose "Creating proxy address and adding into Web Request"
        $wc.Proxy = New-Object -TypeName Net.WebProxy($proxy,$True)
        }       
    
    #Determine if using Default Credentials
    If ($PSBoundParameters.ContainsKey('UseDefaultCredentials')) {
        #Set to True, otherwise remains False
        Write-Verbose "Using Default Credentials"
        $wc.UseDefaultCredentials = $True
        }
    #Determine if using Alternate Credentials
    If ($PSBoundParameters.ContainsKey('Credentials')) {
        #Prompt for alternate credentals
        Write-Verbose "Prompt for alternate credentials"
        $wc.Credential = (Get-Credential).GetNetworkCredential()
        }         
        
    }
Process {    
    Try {
        If ($ShowSize) {
            #Get the size of the webpage
            Write-Verbose "Downloading web page and determining size"
            "{0:N0}" -f ($wr.DownloadString($url) | Out-String).length -as [INT]
            }
        Else {
            #Get the contents of the webpage
            Write-Verbose "Downloading web page and displaying source code" 
            $wc.DownloadString($url)       
            }
        
        }
    Catch {
        Write-Warning "$($Error[0])"
        }
    }   
}  

$SMTP = "smtp.maishsk.local"

do {

	$a = Get-WebPage https://my.vmware.com/web/vmware/info/slug/datacenter_cloud_infrastructure/vmware_vsphere/5_1
	$test = $a -match "Unable to Complete Your Request"
	sleep 300
}
while ($test)

Send-MailMessage -From "Maish<maishsk@maishsk.local>" -To "Maish<maishsk@maishsk.local>" -Subject "vSphere 5.1 page has changed" -SmtpServer $SMTP
The script is not perfect, clean or optimized – but actually this shows you how to check for a change on a web page – which can be used in a large number of scenarios.

I will probably be asleep when vSphere 5.1 is released – but it will be interesting to see how accurate my script was – and at what time I received the notification from the script.

2012-05-13

Creating and Storing PowerShell Credentials

I actually do not understand why I have not put this in a blog post before, but it is about time - because I used it again today.

Sometime you need to store a credential for a number of purposes, be it a scheduled script - or just not having to enter credentials each and every time you would like to connect to a Host or your vCenter.

PowerCLI has it's own credential store with the New-VICredentialStoreItem and Get-VICredentialStoreItem cmdlets.

Personally - I do not like using this Cmdlet and have another method that I prefer using -System.Management.Automation.PSCredential.

First get the credential and store it in a file.

(Get-Credential).Password | ConvertFrom-SecureString | Out-File -FilePath C:\users\msaidelk\Documents\scripts\maish.cred

 

This will give you a string

Get-Content C:\users\msaidelk\Documents\scripts\maish.cred 01000000d08c9ddf0115d1118c7a00c04fc297eb01000000b215c3e1ee044b4286513bf7017abc8f0000000002000000000003660000c000000010000000d6533af1a8d6da153c0bf43713400cb30000000004800000a0000000100000008217a9ea26e47eba311ce8272156a7a2180000004c7047e3ca25bc540078e9dad60b0aff8ba48f37709534a614000000e582d7d6522c111d7101849 a27f8a9c034eb4ab6

To construct the credential again do this:

$vicred = New-Object System.Management.Automation.PsCredential "root", (Get-Content "C:\Users\msaidelk\Documents\scripts\maish.cred" | ConvertTo-SecureString) 

The object accepts two parameters: UserName, Password. They both have to be there and of course the username has to match the the credential that was originally entered for this to work

One other important point that I should point out is that when you import the credential – it has to be done with the same user that stored it in the file in the first place otherwise it will fail - like I tried below with a different user:

PS C:\Users\testa> $vicred = New-Object System.Management.Automation.PsCredential "root", (Get-Content "C:\temp\maish.cred" | ConvertTo-SecureString) ConvertTo-SecureString : Key not valid for use in specified state. At line:1 char:108 + ... \maish.cred" | ConvertTo-SecureString) + ~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidArgument: (:) [ConvertTo-SecureString], C ryptographicException + FullyQualifiedErrorId : ImportSecureString_InvalidArgument_Cryptographic Error,Microsoft.PowerShell.Commands.ConvertToSecureStringCommand

You could do use this method Storing Passwords to Disk in PowerShell with Machine-key Encryption but that is overkill for my use case.

To summarize the process:

  1. Store the password in a file.
  2. Build the credential by providing the correct username and the content of the file and store it in a variable.
  3. Use the variable when connecting to your vCenter / Hosts.

In my Powershell $PROFILE I have the following three lines:

$vcenter = "msaidelk-lab1" $vicred = New-Object System.Management.Automation.PsCredential "MAISHSK\Maish", (Get-Content "C:\Users\msaidelk\Documents\scripts\maish.cred" | ConvertTo-SecureString) Connect-VIServer $vcenter -Credential $vicred

Every time I open a PowerCLI prompt (I usually do that to perform something on my environment), I now have a connection to my vCenter ready for me.