The cool thing with Amazon AWS is that you can use command line tools on your workstation to bring up and manage your services. In this post I’ll look into how you can launch a Linux VM from the command line and how you can attach a storage volume to it.

Before you can use the command line tools you’ll need to create a user that has the proper permissions to connect. This must be done in the IAM console:


As I have no users available right now I’ll create one:


Be sure to download the credentials right now as you’ll not be able to get the “Secret Access Key” later. You’ll need this key when we configure the command line tools later.


Once the user is created all the details are listed:


To be sure I can do everything I want I attached the “Administrator Access” policy to the user just created:


Having the user available we can now proceed and install the command line tools. You’ll need to have python available for that:

[email protected]:~/Downloads$ python --version
Python 2.7.6

The installation is quite easy:

[email protected]:~/Downloads$ curl "" -o ""
[email protected]:~/Downloads$ unzip
[email protected]:~/Downloads$ sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
Running cmd: /usr/bin/python --python /usr/bin/python /usr/local/aws
Running cmd: /usr/local/aws/bin/pip install --no-index --find-links file:///home/dwe/Downloads/awscli-bundle/packages awscli-1.9.20.tar.gz
You can now run: /usr/local/bin/aws --version
[email protected]:~/Downloads$ /usr/local/bin/aws --version
aws-cli/1.9.20 Python/2.7.6 Linux/4.2.0-23-generic botocore/1.3.20
[email protected]:~/Downloads

You can get help by issuing:

[email protected]:~/Downloads$ /usr/local/bin/aws rds help

This brings up the manual similar to the Linux man pages:


Now it is the time to configure the command line tools. You’ll need the contents of the credentials file you downloaded above:

[email protected]:~/Downloads$ /usr/local/bin/aws configure
AWS Secret Access Key [None]: wK8r4r1kuIY1xdgIU2LlA3Uux3L5imGiZ09vNw0c
Default region name [None]: eu-west-1
Default output format [None]: json

A list of Regions and endpoints can be found here.

For being able to launch a VM the next step is to create the ssh keys:

[email protected]:~/Downloads$ aws ec2 create-key-pair --key-name MyKeyPair --query 'KeyMaterial' --output text > MyKeyPair.pem
[email protected]:~/Downloads$ cat MyKeyPair.pem 
[email protected]:~/Downloads$ 

Be sure to modify the permission of the generated key:

[email protected]:~/Downloads$ chmod 400 MyKeyPair.pem

What type of instance do we want to create? You can get the full list of available images by using the “describe-images” sub command:

[email protected]:~/.aws$ aws ec2 describe-images > /var/tmp/aws-images.json

As I wanted to know what “Amazon Linux” is based on I took this one:

"ImageId": "ami-025e5676"
"Description": "Amazon Linux AMI x86_64 EBS"

Knowing what we want to launch we can now bring up the instance:

[email protected]:~/Downloads$ aws ec2 run-instances --image-id ami-025e5676 --count 1 --instance-type t1.micro --key-name MyKeyPair
    "OwnerId": "505389097308", 
    "ReservationId": "r-1abaa1a3", 
    "Groups": [], 
    "Instances": [
            "Monitoring": {
                "State": "disabled"
            "PublicDnsName": "", 
            "KernelId": "aki-71665e05", 
            "State": {
                "Code": 0, 
                "Name": "pending"
            "EbsOptimized": false, 
            "LaunchTime": "2016-01-16T04:57:30.000Z", 
            "PrivateIpAddress": "", 
            "ProductCodes": [], 
            "VpcId": "vpc-6c634009", 
            "StateTransitionReason": "", 
            "InstanceId": "i-07560b8a", 
            "ImageId": "ami-025e5676", 
            "PrivateDnsName": "", 
            "KeyName": "MyKeyPair", 
            "SecurityGroups": [
                    "GroupName": "default", 
                    "GroupId": "sg-ee31498a"
            "ClientToken": "", 
            "SubnetId": "subnet-dffbb186", 
            "InstanceType": "t1.micro", 
            "NetworkInterfaces": [
                    "Status": "in-use", 
                    "MacAddress": "0a:32:b2:89:70:67", 
                    "SourceDestCheck": true, 
                    "VpcId": "vpc-6c634009", 
                    "Description": "", 
                    "NetworkInterfaceId": "eni-0fa82155", 
                    "PrivateIpAddresses": [
                            "PrivateDnsName": "", 
                            "Primary": true, 
                            "PrivateIpAddress": ""
                    "PrivateDnsName": "", 
                    "Attachment": {
                        "Status": "attaching", 
                        "DeviceIndex": 0, 
                        "DeleteOnTermination": true, 
                        "AttachmentId": "eni-attach-21ce50c7", 
                        "AttachTime": "2016-01-16T04:57:30.000Z"
                    "Groups": [
                            "GroupName": "default", 
                            "GroupId": "sg-ee31498a"
                    "SubnetId": "subnet-dffbb186", 
                    "OwnerId": "505389097308", 
                    "PrivateIpAddress": ""
            "SourceDestCheck": true, 
            "Placement": {
                "Tenancy": "default", 
                "GroupName": "", 
                "AvailabilityZone": "eu-west-1a"
            "Hypervisor": "xen", 
            "BlockDeviceMappings": [], 
            "Architecture": "x86_64", 
            "StateReason": {
                "Message": "pending", 
                "Code": "pending"
            "RootDeviceName": "/dev/sda1", 
            "VirtualizationType": "paravirtual", 
            "RootDeviceType": "ebs", 
            "AmiLaunchIndex": 0

A lot of json ouput 🙂 You can pass json files to command line utilities as well. The output fully describes your instance.

Having a look at the AWS EC2 console we can see that the instance in deed was created and is being initialized currently:

While the instance is set up it is good idea to give the instance a name:

[email protected]:~/Downloads$ aws ec2 create-tags --resources i-07560b8a --tags Key=Name,Value=MyFirstInstance

Going back to the console the name is set and the instance is ready:

Time to connect:

[email protected]:~/Downloads$ ssh -i /home/dwe/Downloads/MyKeyPair.pem [email protected]

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
Amazon Linux version 2015.09 is available.

Lets check if this instance is redhat based and we can use yum:

[[email protected] ~]$ yum search postgresql94-server
Loaded plugins: priorities, update-motd, upgrade-helper
=================================================== N/S matched: postgresql94-server ====================================================
postgresql94-server.x86_64 : The programs needed to create and run a PostgreSQL server

  Name and summary matches only, use "search all" for everything.

Cool. What devices do I have available?

[[email protected] ~]$ df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/xvda1            2.0G  686M  1.3G  35% /
tmpfs                 298M     0  298M   0% /dev/shm

Only the root volume was created. Not much if we want to some real work on the instance. So lets create a new volume (10gb):

[email protected]:~/Downloads$ aws ec2 create-volume --size 10 --availability-zone eu-west-1a
    "AvailabilityZone": "eu-west-1a", 
    "Encrypted": false, 
    "VolumeType": "standard", 
    "VolumeId": "vol-fcb06c32", 
    "State": "creating", 
    "SnapshotId": "", 
    "CreateTime": "2016-01-16T05:17:33.615Z", 
    "Size": 10

Providing the name of the new volume it can be attached to the instance:

[email protected]:~/Downloads$ aws ec2 attach-volume --volume-id vol-fcb06c32 --instance-id i-07560b8a --device /dev/sdb
    "AttachTime": "2016-01-16T05:19:51.640Z", 
    "InstanceId": "i-07560b8a", 
    "VolumeId": "vol-fcb06c32", 
    "State": "attaching", 
    "Device": "/dev/sdb"

Seems this operation is online, lets check:

[email protected]:~/Downloads$ ssh -i /home/dwe/Downloads/MyKeyPair.pem [email protected]
Last login: Sat Jan 16 05:19:35 2016 from

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
Amazon Linux version 2015.09 is available.
[[email protected] ~]$ ls /dev/sd*
/dev/sda1  /dev/sdb

Really, really cool. Do I have root access to create a partition on my new device?

[[email protected] ~]$ sudo su -
[[email protected] ~]# fdisk /dev/sdb
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x85f44e7c.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
         switch off the mode (command 'c') and change display units to
         sectors (command 'u').

Command (m for help): p

Disk /dev/sdb: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x85f44e7c

   Device Boot      Start         End      Blocks   Id  System

Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
Partition number (1-4): 1
First cylinder (1-1305, default 1): 
Using default value 1
Last cylinder, +cylinders or +size{K,M,G} (1-1305, default 1305): 
Using default value 1305

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

[[email protected] ~]# ls -la /dev/sd*
lrwxrwxrwx 1 root root 5 Jan 16 04:58 /dev/sda1 -> xvda1
lrwxrwxrwx 1 root root 4 Jan 16 05:21 /dev/sdb -> xvdb
lrwxrwxrwx 1 root root 5 Jan 16 05:21 /dev/sdb1 -> xvdb1

Yes, I do. So creating a file system should be possible, too:

[[email protected] ~]# mkfs.ext4 /dev/sdb1
mke2fs 1.42.3 (14-May-2012)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
655360 inodes, 2620595 blocks
131029 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2684354560
80 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks: 
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done 

Can I mount it?

[[email protected] ~]# mkdir /opt/PostgreSQL
[[email protected] ~]# mount /dev/sdb1 /opt/PostgreSQL
[[email protected] ~]# df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/xvda1            2.0G  686M  1.3G  35% /
tmpfs                 298M     0  298M   0% /dev/shm
/dev/xvdb1            9.9G  151M  9.2G   2% /opt/PostgreSQL

Yes, I have full control. So I could do whatever I want with this VM. Imagine how easy it becomes to bring up test systems this way. Create a little script which wraps all the commands and you’re done in a few minutes.

As a lot of sensitive information was displayed here lets terminate the instance:

[email protected]:~/.aws$ aws ec2 terminate-instances --instance-ids i-07560b8a 
    "TerminatingInstances": [
            "InstanceId": "i-07560b8a", 
            "CurrentState": {
                "Code": 32, 
                "Name": "shutting-down"
            "PreviousState": {
                "Code": 16, 
                "Name": "running"

Easy as well. Have fun with command line tools …