AWS Developer Tools Blog
Stripe Windows Ephemeral Disks at Launch
Today we have another guest post by AWS Solutions Architect David Veith.
Amazon EC2 currently offers more than 20 current-generation instance types for your Windows operating system workloads. The root volume for Windows instances will always be a volume provided by the Amazon EBS service. Additional EBS drives can easily be added as desired.
Depending on the EC2 instance type selected, there will also be from zero to 24 instance-store volumes automatically available to the instance. Instance-store volumes provide temporary block-level storage to the instance. The data in an instance store persists only during the lifetime of its associated instance. Because of the temporary nature of instance-store volumes, they are often referred to as ””’ephemeral”’—not lasting, enduring, or permanent”.
Many workloads can benefit from this type of temporary block-level storage, and it’s important to mention that ephemeral volumes also come with no extra cost.
This blog post describes how the ephemeral volumes of any Windows EC2 instance can be detected at launch, and then automatically striped into one large OS volume. This is a common use case for many AWS customers.
Detecting Ephemeral vs. EBS Volumes
In order to build a striped volume consisting only of instance-store volumes, we first need a mechanism to distinguish the volume types (EBS or ephemeral) associated with the instance. The EC2 metadata service provides a mechanism to determine this.
The following PowerShell statement retrieves all the block devices of a running Windows EC2 instance it is executed upon:
$alldrives = (Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/block-device-mapping/).Content
Here’s an example of the data returned from the metadata service for an M3.Xlarge instance (launched from the AWS Management Console) with one root EBS volume and two instance-store volumes:
Using the same instance type (M3.Xlarge), but this time launching the instance from an AWS CloudFormation script (or AWS command-line tools), the same code produces this output:
Why the difference?
When an instance is launched from the AWS Management Console, the console performs some additional steps to have the instance metadata reflect only the ephemeral drives that are actually present. In order for our code to handle both cases, we can query WMI to see if the OS actually sees the volume.
$disknumber = (Get-WmiObject -Class Win32_DiskDrive | where-object {$_.SCSITargetId -eq $scsiid}).Index
if ($disknumber -ne $null)
How EC2 Windows Maps Drives
Hopefully, you noticed in the code directly above that we queried WMI with the SCSI ID of each volume. Where did we get the SCSI ID?
To answer that question, we need to explain how EC2 Windows instances map block devices’ SCSI IDs in the operating system. The following table shows this:
For example, we can see that ‘xvdcb’ will always map to SCSI ID ’79’. We could build a lookup table that contains all the potential mount points and their corresponding SCSI IDs, but a more elegant approach is to use a simple algorithm based on ASCII arithmetic.
We know that all device mappings for Windows instances begin with the ‘xvd’ prefix. If we remove this prefix, we can use the remaining portion (‘cb’ in this example) to derive the correct SCSI ID.
'c' = ASCII 99
'b' = ASCII 98
(('c' - 97) * 26) + ('b' - 97) = 79
In the final PowerShell script below, this psuedo-code is implemented as the GetSCSI
function.
The Complete Powershell Script
#################################################
# Detect the Ephemeral drives and stripe them
#################################################
# Be sure to choose a drive letter that will not already be assigned
$DriveLetterToAssign = "K:"
#################################################
# Given a device (e.g. xvda), strip off
# "xvd" and convert the remainder to the
# appropriate SCSI ID
#################################################
function GetSCSI {
Param([string]$device)
# remove xvd prefix
$deviceSuffix = $device.substring(3)
if ($deviceSuffix.length -eq 1) {
$scsi = (([int][char] $deviceSuffix[0]) - 97)
}
else {
$scsi = (([int][char] $deviceSuffix[0]) - 96) * 26
+ (([int][char] $deviceSuffix[1]) - 97)
}
return $scsi
}
#################################################
# Main
#################################################
# From metadata read the device list and grab only
# the ephemeral volumes
$alldrives = (Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/block-device-mapping/).Content
$ephemerals = $alldrives.Split(10) | where-object {$_ -like 'ephemeral*'}
# Build a list of scsi ID's for the ephemeral volumes
$scsiarray = @()
foreach ($ephemeral in $ephemerals) {
$device = (Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/block-device-mapping/$ephemeral).Content
$scsi = GetSCSI $device
$scsiarray = $scsiarray + $scsi
}
# Convert the scsi ID's to OS drive numbers and set them up with diskpart
$diskarray = @()
foreach ($scsiid in $scsiarray) {
$disknumber = (Get-WmiObject -Class Win32_DiskDrive | where-object {$_.SCSITargetId -eq $scsiid}).Index
if ($disknumber -ne $null)
{
$diskarray += $disknumber
$dpcommand = "select disk $disknumber
select partition 1
delete partition
convert dynamic
exit"
$dpcommand | diskpart
}
}
# Build the stripe from the diskarray
$diskseries = $diskarray -join ','
if ($diskarray.count -gt 0)
{
if ($diskarray.count -eq 1) {
$type = "simple"
}
else {
$type = "stripe"
}
$dpcommand = "create volume $type disk=$diskseries
format fs=ntfs quick
assign letter=$DriveLetterToAssign
exit"
$dpcommand | diskpart
}
</powershell>
Extra Credit
In Windows Server 2012 R2, Microsoft introduced new PowerShell storage-management cmdlets that replace the need to use the diskpart utility in many cases. If you know your servers will be running only Windows Server 2012 R2, or later, you might want to use these newer Microsoft cmdlets. You can find more information on these cmdlets at http://technet.microsoft.com/en-us/library/hh848705.aspx.