on
01-Apr-2019
04:00
- edited on
05-Jun-2023
21:48
by
JimmyPackets
This is a simple configuration example to show you the basics of integrating Ansible, Amazon Web Services CloudFormation, and F5’s AS3 declarative interface to create an ‘infrastructure-as-code’ BIG-IP implementation. It’s designed to give you the fundamental building blocks, and plenty of opportunity to extend and enhance things from here.
The architecture looks like this:
There is an Ansilbe host, simply a Linux server with Ansible and the AWS CLI tool installed , with a copy of the F5 Cloud Formation repository cloned onto it, and then a short and simple Ansible playbook which we can run. In an S3 bucket (because why not?) we have an AS3 declaration, again using a very simple configuration.
When we run the playbook, Ansible is going to use the F5 Cloud Formation Template (CFT) and data from the playbook to deploy and configure a BIG-IP, including AWS security group objects, etc. Part of the playbook data specifies a URL where the AS3 declaration is available and the post-install processes on the BIG-IP will uses this to pull down and apply the AS3 declaration.
The result is a running, BIG-IP passing traffic to some webservers, configured entirely by two small text files, with no scripting or deep BIG-IP knowledge required.
Although this configuration isn’t production-ready – it should give you the building blocks to create the infrastructure-as-code configuration.
If you’ve use AWS primarily through the web console then first create an AIM user for API access and get the secret and key
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html#id_users_create_cliwpsapi
Make sure the AWS user has permissions (these are probably overkill but will work fine for a demo).
It’s best to create a group to include these policies and then assign the user to the group (or use roles, but this article isn’t about AWS API best practices.)
Install and configure the AWS CLI:
$ sudo apt-get install awscli
Install Ansible and the Boto (AWS Python SDK) components:
$ sudo apt-get install ansible
$ sudo apt-get install python-pip
$ pip install boto3
$ pip install botocore
$ pip install boto
Use the API user’s access key ID and secret access key, along with the region your VPC is in, to configure the AWS cli:
$aws configure
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name [None]: us-west-1
Default output format [None]:
Check it works:
$ aws ec2 describe-instances
<some text describing your EC2 instances>
Clone the F5 AWS Cloudformation GitHub repository:
$ git clone https://github.com/F5Networks/f5-aws-cloudformation
Copy the template we are going to use to a ~/files directory – this isn’t really a necessary step for a production setup, and you could use environment variables etc. to get around long paths but it makes the playbook muc easier to read for this demo.
$mkidr ~/files
$cp /home/ubuntu/f5-aws-cloudformation/supported/standalone/1nic/existing-stack/byol/f5-existing-stack-byol-1nic-bigip.template ~/files/.
Set up a directory for your playbooks:
$mkdir -p ~/ansible/playbooks
Gather the IP address[es] of your target servers, and the address you intend to use for application traffic
Then use an example declaration from the AS3 documentation – the declaration below is about as simple as it gets:
{ "class": "AS3", "action": "deploy", "persist": true, "declaration": { "class": "ADC", "schemaVersion": "3.0.0", "id": "example-declaration-01", "label": "Sample 1", "remark": "Simple HTTP application with RR pool", "Sample_01": { "class": "Tenant", "A1": { "class": "Application", "template": "http", "serviceMain": { "class": "Service_HTTP", "virtualAddresses": [ "" ], "pool": "web_pool" }, "web_pool": { "class": "Pool", "monitors": [ "http" ], "members": [{ "servicePort": 80, "serverAddresses": [ "", "" ] }] } } } } }
Create this as a text file and then create an S3 bucket to put it in, make sure there is public read access to the file, and get the URL for the file
Check that it’s accessible from an internet connected machine
$ curl <your URL>
<your AS3 declaration>
Using the method of your choice, gather the following information
Create the playbook in ~/ansible/playbooks, in my example I called it “bigip_as3.json “
--- - name: F5 Ansible CFT AS3 hosts: localhost connection: local gather_facts: false tasks: - name: Run CFT cloudformation: stack_name: "f5-ansible-cloudformation" state: "present" region: "" disable_rollback: true template: "~/files/f5-existing-stack-byol-1nic-bigip.template" template_parameters: Vpc: "" subnet1Az1: "" subnet1Az1Address: "" imageName: "LTMOneBootLocation" instanceType: "m5.large" licenseKey1: "XXXXXXXXXXXXXXXXXXXXXXXX" sshKey: "" restrictedSrcAddress: "" restrictedSrcAddressApp: "" ntpServer: "0.pool.ntp.org" timezone: "UTC" declarationUrl: "" tags: Stack: "f5-ansible-cloudformation"
There are a number of parameters that the CFT referenced by the playbook needs:
Parameter Name |
Required |
Description |
---|---|---|
Vpc |
Yes |
Common VPC for the deployment. |
managementSubnetAz1 |
Yes |
Management subnet ID. |
subnet1Az1 |
Yes |
Public or External subnet ID. Same as management in a single NIC deployment. |
subnet1Az1Address |
No |
Optional. If you want to assign static IP address(es) in the subnet, type them here. Separate multiple IP addresses with a comma (the first is the Primary IP address, all others are Secondary). Otherwise leave DYNAMIC and a dynamic address is assigned based on the subnet you specified. |
imageName |
Yes |
F5 BIG-IP Performance Type – see below for more infor |
instanceType |
Yes |
Size for the F5 BIG-IP virtual instance (m5.large works fine for this) |
sshKey |
Yes |
Name of an existing EC2 KeyPair to enable SSH access to the instance. |
restrictedSrcAddress |
Yes |
The IP address range that can be used for management access to the EC2 instances. |
restrictedSrcAddressApp |
Yes |
The IP address range that can be used for management access to the EC2 instances. |
ntpServer |
Yes |
NTP server you want to use for this implementation (the default is 0.pool.ntp.org). |
timezone |
Yes |
Olson timezone string from /usr/share/zoneinfo (the default is UTC). |
owner |
No |
Owner Tag (the default is f5owner). |
costcenter |
No |
Cost Center Tag (the default is f5costcenter). |
declarationUrl |
No |
URL for the AS3 declaration JSON file to be deployed. Leave as none to deploy without a service configuration. |
The imageName parameter selects which BIG-IP image to boot from - you can find a list by exploring the CFT file and looking in the ‘mappings’ section. In our case as we are deploying a single NIC, LTM only configuration I selected “LTMOneBootLocation
”
. Note you need the actual name from the template not the AMI - e.g. LTMOneBootLocation not “ami-aa4ea2c9” , as the F5 CFT combines the imangeName with the selected AWS region to select the correct AMI.
Doing the same thing with a free trial is slightly easier a but has a small gotcha.
The template for the same implementation is in the payg directory:
~/f5-aws-cloudformation/supported/standalone/1nic/existing-stack/payg/f5-existing-stack-payg-1nic-bigip.templ
ate
Copy it to your
~/files directory
Unfortunately, there is a limit of 51200 bytes for a CloudFormation Template in Ansible – if you run the playbook you’ll get an ugly error along the lines of:
“Member must have length less than or equal to 51200”
So we need to go do a bit of pruning on our CFT – I suggest removing the region AMI mappings for the regions you aren’t interested in:
e.g. find the mapping section of your CFT
"BigipRegionMap": { "ap-northeast-1": { "AdvancedWaf1000Mbps": "ami-f52e5318", "AdvancedWaf200Mbps": "ami-472855aa", "AdvancedWaf25Mbps": "ami-88334e65", "Best1000Mbps": "ami-65285588", "Best200Mbps": "ami-80334e6d", "Best25Mbps": "ami-7128559c", "Best5000Mbps": "ami-92334e7f", "Better1000Mbps": "ami-072f52ea", "Better200Mbps": "ami-062f52eb", "Better25Mbps": "ami-e62c510b", "Better5000Mbps": "ami-8e295463", "Good1000Mbps": "ami-63334e8e", "Good200Mbps": "ami-062d50eb", "Good25Mbps": "ami-b02f525d", "Good5000Mbps": "ami-0a2d50e7", "PerAppVeAwaf200Mbps": "ami-88cebc65", "PerAppVeAwaf25Mbps": "ami-d03f423d", "PerAppVeLtm200Mbps": "ami-233d40ce", "PerAppVeLtm25Mbps": "ami-7f3f4292" }, "ap-northeast-2": { "AdvancedWaf1000Mbps": "ami-0f05872fb7d0e05d1", "AdvancedWaf200Mbps": "ami-015506735d78469e2",
Delete entire regions you don’t need – that will bring the file size down to a limit that Ansible finds acceptable.
To make PAYG licensing works, you need to subscribe to the software. For the 200Mb Good instance, the subscription page is here:
Click the ‘Subscribe button”
After that all you need to do is
“Good200Mbps”
This is the easy part! We use the ansible-playbook command:
ansible-playbook <your playbook>
(my PAYG playbook)$ ansible-playbook ./bigip_as3_payg.json
or
$ ansible-playbook ./bigip_as3.json
(my eval licensed playbook)
You can then follow the deployment by going to the AWS cloud formation console.
Once it’s up and running you can find the elastic IP address of your new big-IP instance from the cli or AWS console, and use your favorite SSH client and your SSH key to access the BIG-IP, change the admin password and view the configuration - remember AS3 declarations create their own partition.
Hopefully you’ll end up with something that looks like this
You can easily delete the stack using the AWS CLI:
$ aws cloudformation delete-stack --stack-name f5-ansible-cloudformation
There are number of next steps from here – leave me a comment if you’d like to see a particular one
I should also highlight Cody Greens's more production ready code at https://github.com/codygreen/Ansible. He uses a slightly different (but certainly no less valid) config, and has done a lot more with parameters to make things more modular.
Good article. Does AS3 allow to update L-3 objects? so that we are able to update routes or add or remove VLANs?
AS3 is focused on L4-L7 Services. It won't give you a declarative interface to handle this kind of component. We have another extension for this kind of use case called Declarative Onboarding (aka DO). It provides a declarative API to be able to fully onboard a newly provisioned device (login, vlan, self, routes, ntp, dns, clustering setup, ...)
it is available here: https://github.com/F5Networks/f5-declarative-onboarding
Documentation is here: https://clouddocs.f5.com/products/extensions/f5-declarative-onboarding/latest/ with an example: https://clouddocs.f5.com/products/extensions/f5-declarative-onboarding/latest/examples.html
As Nicolas said, you want DO for that - Cody Green's repo above has that integrated into his Ansible example, including the install of the DO component.