Successfully Deploy Your Application in the AWS Public Cloud: Part 1 of 4

In this series of articles, we're going to walk you through a fairly typical lift-and-shift deployment of BIG-IP in AWS, so that:

  • If you’re just starting, you can get an idea of what lies ahead.
  • If you’re already working in the cloud, you can get familiar with a variety of F5 solutions that will help your application and organization be successful.

The scenario we've chosen is pretty typical: we have an application in our data center and we want to move it to the public cloud. As part of this move, we want to give development teams access to an agile environment, and we want to ensure that NetOps/SecOps maintains the stability and control they expect.

Here is a simple diagram for a starting point. 

We’re a business that sells our products on the web. Specifically, we sell a bunch of random picnic supplies and candies. Our hot seller this summer is hotdog-flavored lemonade, something you might think is appalling but that really encompasses everything great about a picnic.

But back to the scenario:

We have a data center, where we have two physical BIG-IPs that function as a web application firewall (WAF), and they load balance traffic securely to three application servers. These application servers get their product information from a product database. 

Our warehouse uses a separate internal application to manage inventory, and that inventory is stored in an inventory database.

In this series of articles, we’ll show you how to move the application to Amazon Web Services (AWS), and discuss the trade-offs that come at different stages in the process. 

So let’s get started.

The challenge

Move to the cloud; keep environments in sync

The solution

Use a CloudFormation Template (CFT) to create a repeatable cloud deployment

We’ve been told to move to the cloud, and after a thorough investigation of the options, have decided to move our picnic-supply-selling app to Amazon Web Services.

Our organization has several different environments that we maintain.

  • Dev (one environment per developer)
  • Test
  • UAT
  • Performance
  • Production

These environments can tend to be out of sync with one another. This frustrates everyone.

And when we deploy the app to production, we often see unexpected results. If possible, we don’t want to bring this problem along to the cloud. We want to deploy our application to all of these environments and have the result be the same every time. Even if each developer has different code, all developers should be working in an infrastructure environment that matches all other environments, most importantly, production.

Enter the AWS CloudFormation template. We can create a template and use it to consistently spin up the same environment. If we require a change, we can make the modification and save a new version of the CFT, letting everyone on the team know about the change. And it’s version-controlled, so we can always roll back if we mess up.

So we use a CFT to create our application servers and deploy the latest code on them. In our scenario, we create an AWS Elastic Load Balancer so we can continue load balancing to the application servers. 

Our product data has a dependency on inventory data that comes from the warehouse, and we use BIG-IP for authentication (among other things). We use our on-premise BIG-IPs to create an IPSEC VPN tunnel to AWS. This way, our application can maintain a connection to the inventory system.

When we get the CFT working the way we want, we can swing the DNS to point to these new AWS instances. 

Details about the CloudFormation template

We put a CFT on github that you can deploy to demonstrate the AWS part of this setup. It may help you visualize this deployment, and in part 2 of this series, we'll be expanding on this initial setup.

If you'd like, you can deploy by clicking the following button. Ensure that when you're in the AWS console, you select the region where you want to deploy. And if you're really new to this, just remember that active instances cost money.

The CFT creates four Windows servers behind an AWS Elastic Load Balancer (ELB). Three of the servers are running a web app and one is used for the database. Beware, the website is a bit goofy and we were feeling punchy when we created it.

Here is a brief explanation of what specific sections of the CFT do.

The Parameters section includes fields you must populate when deploying the CFT. In this case, you’ll have to specify a name for your servers, and the AMI (Amazon Machine Image) ID to build the servers from.

In the template, you can see what parameters look like. For example, the field where you enter the AMI ID:

"WindowsAMI": {
      "Description": "Windows Version and Region AMI",
      "Type": "String",

To find the ID of the AMI you want to use, look in the marketplace, find the product you want, click the Manual Launch tab, and note the AMI ID for the region where you’re going to deploy. We are using Microsoft Windows Server 2016 Base and Microsoft Windows Server 2016 with MSSQL 2016.

Note: These IDs can change; check the AWS Marketplace for the latest AMI IDs.

The resources section of the CFT performs the legwork. 

The CFT creates a Virtual Private Cloud (VPC) with three subnets so that the application is redundant across availability zones. It creates a Windows Server instance in each availability zone, and it creates an AWS Elastic Load Balancer (ELB) in front of the application servers.

Code that creates the load balancer:

"StackELB01": {
      "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
      "Properties": {
        "Subnets" : [
          { "Ref": "StackSubnet1" },
          { "Ref": "StackSubnet2" },
          { "Ref": "StackSubnet3" }
        "Instances": [
          { "Ref": "WindowsInstance1" },
          { "Ref": "WindowsInstance2" },
          { "Ref": "WindowsInstance3" }
        "Listeners": [
            "LoadBalancerPort": "80",
            "InstancePort": "80",
            "Protocol": "HTTP"
        "HealthCheck": {
          "Target": "HTTP:80/",
          "HealthyThreshold": "3",
          "UnhealthyThreshold": "5",
          "Interval": "30",
          "Timeout": "5"
          { "Ref": "ELBSecurityGroup" }

Then the CFT uses Cloud-Init to configure the Windows machines. It installs IIS on each machine, sets the hostname, and creates an index.html file that contains the server name (so that when you load balance to each machine, you will be able to determine which app server is serving the traffic). It also adds your user to the machine’s local Administrators group. 

Note: This is just part of the code. Look at the CFT itself for details.

"install_IIS": {
   "files": {
      "C:\\Users\\Administrator\\Downloads\\firstrun.ps1": {
         "content": {
            "Fn::Join": [
                     "param ( \n",
                     " [string]$password,\n",
                     " [string]$username,\n",
                     " [string]$servername\n",
                     "Add-Type -AssemblyName System.IO.Compression.FileSystem\n",
                     "## Create user and add to Administrators group\n",
                     "$pass = ConvertTo-SecureString $password -AsPlainText -Force\n",
                     "New-LocalUser -Name $username -Password $pass -PasswordNeverExpires\n",
                     "Add-LocalGroupMember -Group \"Administrators\" -Member $username\n",

The CFT then calls PowerShell to run the script.  

"commands": {
   "b-configure": {
       "command": {
           "Fn::Join": [
                " ",
                   "powershell.exe -ExecutionPolicy unrestricted C:\\Users\\Administrator\\Downloads\\firstrun.ps1",
                   { "Ref": "adminPassword" },
                   { "Ref": "adminUsername"},
                   { "Ref": "WindowsName1"},

Finally, this section includes signaling. You can use the Cloud-Init cfn-signal helper script to pause the stack until resource creation is complete. 

For more information, see

Sample of signaling:

"WindowsInstance1WaitHandle": {
   "Type": "AWS::CloudFormation::WaitConditionHandle"
"WindowsInstance1WaitCondition": {
   "Type": "AWS::CloudFormation::WaitCondition",
   "DependsOn": "WindowsInstance1",
   "Properties": {
      "Handle": {
         "Ref": "WindowsInstance1WaitHandle"
      "Timeout": "1200"

The output includes the URL of the AWS ELB, which you use to connect to your applications.

"Outputs": {
    "ServerURL": {
      "Description": "The AWS Generated URL.",
      "Value": {
        "Fn::Join": [
              "Fn::GetAtt": [

This output is displayed in the AWS console, on the Outputs tab. 

You can use the link to quickly connect to the ELB.

When we're done deploying the app and we’ve fully tested it in AWS, we can swing the DNS from the internal address to the AWS load balancer, and we’re up and running.

Come back next week as we implement BIG-IP VE for security in AWS.

Updated Jun 06, 2023
Version 2.0

Was this article helpful?

No CommentsBe the first to comment