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:

dwe@dwe:~/Downloads$ python --version
Python 2.7.6

The installation is quite easy:

dwe@dwe:~/Downloads$ curl "" -o ""
dwe@dwe:~/Downloads$ unzip
dwe@dwe:~/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
dwe@dwe:~/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

You can get help by issuing:

dwe@dwe:~/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:

dwe@dwe:~/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:

dwe@dwe:~/Downloads$ aws ec2 create-key-pair --key-name MyKeyPair --query 'KeyMaterial' --output text > MyKeyPair.pem
dwe@dwe:~/Downloads$ cat MyKeyPair.pem 

Be sure to modify the permission of the generated key:

dwe@dwe:~/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:

dwe@dwe:~/.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:

dwe@dwe:~/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:

dwe@dwe:~/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:

dwe@dwe:~/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:

[ec2-user@ip-172-31-43-22 ~]$ 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?

[ec2-user@ip-172-31-43-22 ~]$ 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):

dwe@dwe:~/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:

dwe@dwe:~/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:

dwe@dwe:~/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.
[ec2-user@ip-172-31-43-22 ~]$ ls /dev/sd*
/dev/sda1  /dev/sdb

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

[ec2-user@ip-172-31-43-22 ~]$ sudo su -
[root@ip-172-31-43-22 ~]# 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.

[root@ip-172-31-43-22 ~]# 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:

[root@ip-172-31-43-22 ~]# 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?

[root@ip-172-31-43-22 ~]# mkdir /opt/PostgreSQL
[root@ip-172-31-43-22 ~]# mount /dev/sdb1 /opt/PostgreSQL
[root@ip-172-31-43-22 ~]# 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:

dwe@dwe:~/.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 …