Tag Archives: powershell

Using PowerShell and REST-API to create a VM in vCenter

VMware vSphere 6.5 comes with a RESTful API implementation and there’s some great documentation out there- starting with the API Explorer (http://my.vcenter.name/apiexplorer ). Here’s a quick piece on how to use this API to create a VM from the PowerShell command line. This is intentionally not using PowerCLI,  just the native PowerShell cmdlets- partly as a REST learning experience for me, and partly so the API code can be transferred to another language at a later date.

Step 1- Authenticate with the Server.

This step is well documented by Chris Wahl. I’ve borrowed some of his code here, and accompanied it with a section to get around the lack of trusted certificates on my homelab. 192.168.0.240 is the IP of my VCSA, so if you’re reusing this anywhere remember to replace that hard coded value where it appears.

#----------------------------------------------------------------------------------------------
#Step 1- Authenticate with the Server
#Ignore Server Certs- This is on my not-very-well-certified home lab.
if (-not ([System.Management.Automation.PSTypeName]'ServerCertificateValidationCallback').Type)
{
$certCallback=@"
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
public class ServerCertificateValidationCallback
{
public static void Ignore()
{
if(ServicePointManager.ServerCertificateValidationCallback ==null)
{
ServicePointManager.ServerCertificateValidationCallback +=
delegate
(
Object obj,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors errors
)
{
return true;
};
}
}
}
"
@
Add-Type $certCallback
}
[ServerCertificateValidationCallback]::Ignore();

#Get Some Credentials and Determine Authorisation Methods
$Credential = Get-Credential
$auth = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($Credential.UserName+':'+$Credential.GetNetworkCredential().Password))
$head = @{
'Authorization' = "Basic $auth"
}

#Authenticate against vCenter
$r = Invoke-WebRequest -Uri https://192.168.0.240/rest/com/vmware/cis/session -Method Post -Headers $head
$token = (ConvertFrom-Json $r.Content).value
$session = @{'vmware-api-session-id' = $token}

Now we have the Session ($session) we can test this by retrieving a list of VMs.

#----------------------------------------------------------------------------------------------
#Get a List of VMs
$r1 = Invoke-WebRequest -Uri https://192.168.0.240/rest/vcenter/vm -Method Get -Headers $session
$vms = (ConvertFrom-Json $r1.Content).value
$vms

Step 2- Construct the JSON specification

To create a new VM we need to provide a minimal spec for the machine, in JSON format. We need to tell it the intended Guest OS, what datastore is going to hold the VM, and where the VM will be placed in the resource/folder structure. To complete this we need to establish what options are available- just sticking in the display names of a datastore or folder from the Web Client will not work and will likely generate 404 responses to the API call.

To find these names we can use the API, API explorer gives us the following urls

Datastore:       /rest/vcenter/datastore
Folder:              /rest/vcenter/folder
Resource Pool: /rest/vcenter/resource-pool

So we can use PowerShell to retrieve a list of Datastores using this line of code

(Invoke-RestMethod -Uri https://192.168.0.240/rest/vcenter/datastore -Method Get -Headers $session ).value

which will produce a list of datastores, each looking something like this:

datastore  : datastore-11
name       : Datastore2
type       : VMFS
free_space : 100553195520
capacity   : 249913409536

From this example we want the value of the “datastore” field, e.g “datastore-11”.

Once we have this information we can combine it all to create a JSON spec file. My example looks like this:

{
"spec": {
"guest_OS": "RHEL_7_64",
"placement" : {
"datastore": "datastore-11",
"folder": "group-v224",
"resource_pool": "resgroup-182"
}
}
}

Step 3- Create the Virtual Machine.

Now we’ve done all this prep work, creating a Virtual Machine comes down to a single line of PowerShell pointing at the data.txt file containing the JSON code from Step 2.

Invoke-WebRequest -Uri https://192.168.0.240/rest/vcenter/vm -Method Post -Headers $session -ContentType "application/json" -Body (Get-Content data.txt)

Example Output:

StatusCode        : 200
StatusDescription : OK
Content           : {"value":"vm-462"}
RawContent        : HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json
Date: Mon, 27 Mar 2017 09:41:34 GMT

{"value":"vm-462"}
Forms             : {}
Headers           : {[Transfer-Encoding, chunked], [Content-Type, application/json], [Date, Mon, 27 Mar 2017 09:41:34 GMT]}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 18

The VM is created and we can check this from the vSphere Client:

2017-03-27 (10)

So, to summarise. Native PowerShell, with a little bit of JSON, can be used to communicate with the vSphere APIs and create new Virtual Machines. Depending on your use case there may be better ways of implementing automation processes through this API (PowerCLI is a good start) but if you want to drop to the raw RESTful API, possibly as a stepping stone to a larger project, PowerShell provides a handy method to get started down that path.

PowerShell Quick Tip- Letter Frequency

With a list of Surnames in a text file I wanted to see how many start with A, how many with B, and so on. This is my PowerShell solution:

(Get-Content .\surnames.txt).Substring(0,1).ToUpper() |
  Sort-Object | Group-Object |Select-Object Name, Count

Example Input (surnames.txt file):

Adams
Smith
Jones
Bloggs
...

Example Output:

Name Count
---- -----
A      162
B      372
C      365
D      193
E      187
F      198
G      154
H      321
...

Checking Encryption Status of Remote Windows Computers

Using the manage-bde command you can check the Bitlocker encryption status on both the local Windows computer but also remote devices on the local area network. For example, to check the encryption status of the C: drive on the computer “WS12345” the following command could be used

manage-bde -status -computername WS12345 C:

and the results might look something like this:

BitLocker Drive Encryption: Configuration Tool version 10.0.14393
Copyright (C) 2013 Microsoft Corporation. All rights reserved.

Computer Name: WS12345

Volume C: [OSDisk]
[OS Volume]

Size:                 237.99 GB
BitLocker Version:    2.0
Conversion Status:    Fully Encrypted
Percentage Encrypted: 100.0%
Encryption Method:    AES 256 with Diffuser
Protection Status:    Protection On
Lock Status:          Unlocked
Identification Field: None
Key Protectors:
    Numerical Password
    TPM

Expanding on this we could wrap some PowerShell around the command and read in a list of hostnames from a text file and report on the encryption status of each.

Firstly we need to format the output of manage-bde to only show us the value of the “Conversion Status” field- PowerShell’s string manupulation can come in handy here- we can locate the “Conversion Status” line, check that it is present (if the computer is not on the network, or access is denied the manage-bde command will not return a status), and then trim back the line so we only have the value of the field. For example:

#Check the Encryption Status of the C: drive, filter to the Conversion Status line
$EncryptionStatus=(manage-bde -status -computername "$hostname" C: | where {$_ -match 'Conversion Status'})
#Check a status was returned.
if ($EncryptionStatus)
{
  #Status was returned, tidy up the formatting
  $EncryptionStatus=$EncryptionStatus.Split(":")[1].trim()
}
else
{
  #Status was not returned. Explain why in the output
  $EncryptionStatus="Not Found On Network (or access denied)"
}

Once this is working, it’s just a case of reading in the text file using the get-content cmdlet and outputting a result. The full code (Get-EncryptionStatus.ps1) I used is available for downloading and/or improving on GitHub here- https://github.com/isjwuk/get-encryptionstatus

Finding Deprecated VMFS Volumes

Following an upgrade to vSphere 6, hosts popped up an error message reporting that some old VMFS volumes were found on the host. Whilst these still worked, it would be prudent to tidy them up and replace them with modern datastores. The message is:

“Deprecated VMFS volume(s) found on the host. Please consider upgrading volume(s) to the latest version”

image

Whilst this highlights that there is an issue, the GUI message doesn’t indicate which volumes (or how many) are affected. The following quick bit of PowerCLI produces a table of the datastores with a VMFS version of less than 5.

Get-Datastore |
 Where-Object { $_.ExtensionData.Info.VMFS.MajorVersion -lt 5} |
 Select-Object Name, FreeSpaceGB, CapacityGB, @{n='VMFS Version';e={$_.ExtensionData.Info.VMFS.MajorVersion}}

Example Output:

Name           FreeSpaceGB CapacityGB VMFS Version
----           ----------- ---------- ------------
VOL-VM01            574.77    2047.75            3
VOL-VM02           1924.05    2047.75            3
The Home Lab

Automated Deployment in the HomeLab- Part 1

I’m commencing a project with my HomeLab- I’m going to build a system whereby I can produce custom mini-lab environments by means of a script. There are off the shelf solutions to do this (see AutoLab as an example) but if I build this myself I get something tailored exactly to my needs (and available resources) and hopefully learn something along the way- which is what the HomeLab is all about really. This is the first post in what should develop into a series showing how I work through the process to create my automation system.

 

The Aims

a.k.a. what I want to achieve

  • ExampleMiniLabThe ability to run a script to deploy a predefined lab environment. For example running “Build-Project-Lab-One.ps1” makes 3 Windows Server VMs, connected on a private switch, with one running AD/DNS/DHCP roles, one acting as a Gateway, and one ready for whatever experiment I throw at it
  • The ability to quickly and easily modify a copy of that script to produce a lab with a different configuration. Then I can have a script that builds me a WDS platform, or another one that produces a SCOM test environment. I can use this library to quickly rebuild, or build a copy of, and of my environments within the HomeLab
  • This script should also create a second script for decommissioning  /destroying the lab environment when I’ve finished.
  • Whilst perhaps not meeting full “production” standards, the scripts should be at least in a state whereby I can post them online and not have to hide in a cave for the next decade whilst they get laughed at.

 

The Resources

a.k.a. what I have to play with

  • One Intel NUC host running vSphere ESXi 6 providing some compute, memory, storageIntel NUC
  • One VMUG Advantage Subscription complete with VMware EVAL Experience licensing- this provides VMware vCenter amongst other things.
  • One Microsoft DreamSpark Subscription and Microsoft Evaluation Licensing (see “Microsoft Licensing” on the Open Homelab Project for details on how to get these)
  • Me with my knowledge of Windows, vSphere, PowerShell, PowerCLI, and how to Google for stuff.
  • The community who not only kindly put content up on the internet for me to Google for but also are there for me to tweet, slack, and (shock, horror) talk to when I encounter problems or lose direction.

 

The Plan

a.k.a. How I’m hoping to achieve those aims with those resources.

To do all this I’m starting out by preparing a vSphere template of Windows Server 2012R2. I can deploy this- with customisations- using PowerCLI to form the building blocks of the lab environment. Once I have Windows VMs deployed I need to be able to configure them- this is where PowerShell remoting will come in handy- I can deploy roles and features and do some basic configuration. I’ll put together a PowerShell function to do all that. This function can then be re-used in the script to deploy multiple VMs with different configurations. For example:

CreateVM "Server1" $TemplateName $CustomizationSpec "Web-Server"
CreateVM "Server2" $TemplateName $CustomizationSpec "WDS"

I’ll use PowerCLI to deploy a private network within the Hypervisor and connect the VMs to it. This method will also be used to configure the connections to the gateway -one NIC pointing at the private switch and one at the internet-facing vSwitch already in place.

Some more in-depth PowerShell (possibly also arranged into reusable functions) to do the in-depth configuration of the roles. For example, when the script completes I want the Active Directory to be up and running, the Gateway providing an internet connection to the VMs, the VMs getting IP addresses from the lab DHCP and domain-joined. Basically I want to be able to run the script, make a brew, and come back and find a fully configured system ready to go.

 

Coming Soon- Part 2, full of scripting goodness.