Launching LAMP from a single script in AWS

Below is a bash script I wrote that can be executed from a bastion server within AWS. This script will deploy LAMP or a linux Ec2 instance that runs an apache web server/SQL database and PHP. Please modify as needed.

Here is an explanation for the script.

Line 1:
This file is a bash file, so the first line contains #!/bin/bash

Lines 7–11:
The instance size is set to t2.small, which should be large enough to run the database and web server.

Lines 16–29:
The script invokes the AWS CLI describe-regions command to get a list of all AWS regions. In each region, it queries for an existing VPC that has the name thattechguydom VPC. When it finds it, it captures the vpc ID and region where the That Tech Guy Dom LAMP instance should be deployed and breaks out of the while loop.

Lines 31–57:
The script invokes AWS CLI commands to look up the Subnet ID, Keypair name, and AMI ID values that will be needed to create an EC2 instance.

Notice on line 32 that the line ends with a backslash (\ ) character. This character can be used to wrap a single command on to another line in the script file. You will find that this technique is used many times in this script to make it easier to read.

Lines 59–124:
This part of the script cleans up the AWS account for situations where this script already ran on your AWS account, and it is now being run again. The script checks if an instance named thattechguydom already exists, and if a security group that includes thattechguydomSG in its name already exists. If either resource is found, the script prompts you to delete them.

Lines 126–154:
The script creates a new security group with ports 22 and 80 open.

Lines 156–168:
The script creates a new EC2 instance.

Notice how the values set in lines 8 and 10 and the values collected in lines 16–57 are used in this AWS CLI call.

Also, notice the reference to a user-data script.

While you are still in the bash script, look again at lines 157–168:
The entire call to create the instance is captured in a variable that is named instanceDetails. The contents of this variable are then echoed out to the terminal on line 177, and they are formatted for easier viewing by using a Python JavaScript Object Notation (JSON) tool.

Lines 179–188:
The instanceId is parsed out of the instanceDetails, and then a while loop checks every 10 seconds to see if a public IP address has been assigned to the instance. When the check succeeds, the public IP address is written to the terminal.





#!/bin/bash
DATE=`date '+%Y-%m-%d %H:%M:%S'`
echo
echo "Running create-instance.sh on "$DATE
echo

# Hard coded values
instanceType="t2.small"
echo "Instance Type: "$instanceType
profile="default"
echo "Profile: "$profile

echo
echo "Looking up account values..."

# get vpcId
vpc=""
while [[ "$vpc" == "" ]] ; do
  for i in $(aws ec2 describe-regions | grep RegionName | cut -d '"' -f4) ; do
    region=$i;
    vpc=$(aws ec2 describe-vpcs --region $i --filters "Name=tag:Name,Values='thattechguydom VPC'" --profile $profile | grep VpcId | cut -d '"' -f4 | sed -n 1p );
    if [[ "$vpc" != "" ]]; then
    	break;
    fi
  done
done
echo
echo "VPC: "$vpc
echo "Region: "$region

vpc=$(aws ec2 describe-vpcs \
--filters "Name=tag:Name,Values='thattechguydom VPC'" \
--region $region \
--profile $profile | grep VpcId | cut -d '"' -f4 | sed -n 1p)
echo "VPC: "$vpc

# get subnetId
subnetId=$(aws ec2 describe-subnets \
--filters "Name=tag:Name,Values='thattechguydom Public Subnet 1'" \
--region $region \
--profile $profile \
--query "Subnets[*]" | grep SubnetId | cut -d '"' -f4 | sed -n 1p)
echo "Subnet Id: "$subnetId

# Get keypair name
key=$(aws ec2 describe-key-pairs \
--profile $profile --region $region | grep KeyName | cut -d '"' -f4 )
echo "Key: "$key

# Get AMI ID
imageId=$(aws ec2 describe-images \
--owners amazon --query 'Images[*].[ImageId]' \
--filters 'Name=name,Values=amzn2-ami-hvm-2.0.20190115-x86_64-gp2' 'Name=state,Values=available' \
--output json \
--profile $profile \
--region $region | grep ami- | cut -d '"' -f2 | sed -n 1p)
echo "AMI ID: "$imageId

#check for existing thattechguydom instance
existingEc2Instance=$(aws ec2 describe-instances \
--region $region \
--profile $profile \
--filters "Name=tag:Name,Values=thattechguydomserver" "Name=instance-state-name,Values=running" \
| grep InstanceId | cut -d '"' -f4)
if [[ "$existingEc2Instance" != "" ]]; then
  echo
  echo "WARNING: Found existing running EC2 instance with instance ID "$existingEc2Instance"."
  echo "This script will not succeed if it already exists. "
  echo "Would you like to delete it? [Y/N]"
  echo ">>"

  validResp=0
  while [ $validResp -eq 0 ];
  do
      read answer
      if [[ "$answer" == "Y" || "$answer" == "y" ]]; then
          echo
          echo "Deleting the existing instance..."
          aws ec2 terminate-instances --instance-ids $existingEc2Instance --region $region --profile $profile
          #wait for confirmation it was terminated
          aws ec2 wait instance-terminated --instance-ids $existingEc2Instance --region $region --profile $profile
          validResp="1"
      elif [[ "$answer" == "N" || "$answer" == "n" ]]; then
          echo "Ok, exiting."
          exit 1
      else
          echo "Please reply with Y or N."
      fi
  done

  sleep 10 #give it 10 seconds before trying to delete the SG this instance used.
fi

#check for existing thattechguydomSG security Group
existingMpSg=$(aws ec2 describe-security-groups \
--region $region \
--query "SecurityGroups[?contains(GroupName, 'thattechguydomSG')]" \
--profile $profile | grep GroupId | cut -d '"' -f4)

if [[ "$existingMpSg" != "" ]]; then
  echo
  echo "WARNING: Found existing security group with name "$existingMpSg"."
  echo "This script will not succeed if it already exists. "
  echo "Would you like to delete it? [Y/N]"
  echo ">>"

  validResp=0
  while [ $validResp -eq 0 ];
  do
      read answer
      if [[ "$answer" == "Y" || "$answer" == "y" ]]; then
          echo
          echo "Deleting the existing security group..."
          aws ec2 delete-security-group --group-id $existingMpSg --region $region --profile $profile
          validResp="1"
      elif [[ "$answer" == "N" || "$answer" == "n" ]]; then
          echo "Ok, exiting."
          exit 1
      else
          echo "Please reply with Y or N."
      fi
  done
  sleep 10 #give it 10 seconds before trying to recreate the SG
fi

# CREATE a security group and capture the name of it
echo
echo "Creating a new security group..."
securityGroup=$(aws ec2 create-security-group --group-name "thattechguydomSG" \
--description "thattechguydomSG" \
--region $region \
--group-name "thattechguydomSG" \
--vpc-id $vpc --profile $profile | grep GroupId | cut -d '"' -f4 )
echo "Security Group: "$securityGroup

# Open ports in the security group
echo
echo "Opening port 22 in the new security group"
aws ec2 authorize-security-group-ingress \
--group-id $securityGroup \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0 \
--region $region \
--profile $profile

echo "Opening port 80 in the new security group"
aws ec2 authorize-security-group-ingress \
--group-id $securityGroup \
--protocol tcp \
--port 8080 \
--cidr 0.0.0.0/0 \
--region $region \
--profile $profile

echo
echo "Creating an EC2 instance in "$region
instanceDetails=$(aws ec2 run-instances \
--image-id $imageId \
--count 1 \
--instance-type $instanceType \
--region us-east-1 \
--subnet-id $subnetId \
--security-group-ids $securityGroup \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=thattechguydomserver}]' \
--associate-public-ip-address \
--profile $profile \
--user-data file://create-lamp-instance-userdata.txt )

#if the create instance command failed, exit this script
if [[ "$?" -ne "0" ]]; then
  exit 1
fi

echo
echo "Instance Details...."
echo $instanceDetails | python -m json.tool

# Extract instanceId
instanceId=$(echo $instanceDetails | python -m json.tool | grep InstanceId | sed -n 1p | cut -d '"' -f4)
echo "instanceId="$instanceId
echo
echo "Waiting for a public IP for the new instance..."
pubIp=""
while [[ "$pubIp" == "" ]]; do
  sleep 10;
  pubIp=$(aws ec2 describe-instances --instance-id $instanceId --region $region --profile $profile | grep PublicIp | sed -n 1p | cut -d '"' -f4)
done

echo
echo "The public IP of your LAMP instance is: "$pubIp
echo
echo "Download the Key Pair from the Qwiklabs page."
echo
echo "Then connect using this command (with .pem or .ppk added to the end of the keypair name):"
echo "ssh -i path-to/"$key" ec2-user@"$pubIp
echo
echo "The website should also become available at"
echo "http://"$pubIp"/thattechguydom/"

echo
DATE=`date '+%Y-%m-%d %H:%M:%S'`
echo
echo "Done running create-instance.sh at "$DATE
echo

Leave a Reply