Edit: Since this post was written a while ago I’ve written a new example with Terraform 0.12, much cleaner code with modules and code used available in Github.
Since I’ve become a big fan of modules a case where you might need them is by creating multiple instances at the same time for your Terraform project.
One option is that you have the main Terraform project and call the modules from projects main.tf file multiple times like this:
Terraform project for creating instances
Again I have three files in my Terraform project named “Create_three_instances”. The files are:
- variables.tf
- main.tf
- outputs.tf
Let’s take a look of them.
In the variables.tf I have defined the necessary variables for this project. In this case when I’m creating instances I have some variables pointing to existing resources related to compartment, network and instance image/shape. These are required as in the main.tf I will need to get existing OCID’s for subnets, ADs etc. I have also defined a variable server_count which is the amount of servers I want to create in this example.
In the main.tf I get existing data by using the variables.
As you can see I first get the compartment information by passing tenancy_ocid and filter the result by compartment name. After that I use the compartment OCID to get Availability Domains and VCNs.
With compartment and VCN OCID I get the subnet information and limit the result with the subnet name. Lookups are very easy to use to get already existing data created by my network project earlier!
Next I call the actual module which creates the instances.
Calling this module I pass the server_count variable, some data through lookups which are required and also some static variables which were defined in the variables.tf.
One additional thing to note is the instance_create_vnic_details_private_ip. As you can see I actually pass the cidr_block of the subnet. Why? You will see when the actual module is being used.
Finally in the outputs.tf I will just print out the instance name, public and private IPs.
Using count when creating the resource
Now by looking the actual resource “oci_core_instance” “CreateInstance” you can see the usage of the count.
I pass the server_count variable to count in the start of the resource and this determines how many times the create resource will be looped through.
As you might want to or you have to separate the naming for some resources I’ve used ${count.index} in some places to have separation without creating a list of variables in the project’s variables.tf. In my opinion list might hard to keep updated and by using the numbering you can still have different naming.
If you take a look on the private_ip variable you see now the usage of the subnet cidr block I mentioned earlier. I use built-in function cidrhost to give specific private IP addresses for each host. You could let OCI to allocate those automatically but in some cases you want to define them.
Definition of cidrhost is as follows:
cidrhost(iprange, hostnum) – Takes an IP address range in CIDR notation and creates an IP address with the given host number. If given host number is negative, the count starts from the end of the range. For example, cidrhost(“10.0.0.0/8”, 2) returns 10.0.0.2 and cidrhost(“10.0.0.0/8”, -2) returns 10.255.255.254.
Reason why I have +3 in the end is that Oracle reserves two first IP addresses in the start of every subnet so the first instance will receive .3 IP address and continue to grow from there.
I also have outputs.tf in the module. There I need to use splat syntax (*) to send the list out.
Creating the resources
Now when I’m ready to create the resource I will run the usual terraform init, plan and apply. If you want to know more about those check my earlier blog post and video from here.
When I run terraform plan it will show that it will create three new resources:
Plan: 3 to add, 0 to change, 0 to destroy.
That matches the server_count variable. Once I’ve executed apply the outputs for the instances are:
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
instanceName = [
publicinstance-0,
publicinstance-1,
publicinstance-2
]
instancePrivateIP = [
10.0.2.3,
10.0.2.4,
10.0.2.5
]
instancePublicIP = [
130.61.XX.81,
130.61.XX.238,
130.61.XX.133
]
Video
Here is video also showing the exact same thing as above. You can see the code as well as running the Terraform project.
Summary
When you use Terraform modules the main goal would be to make easily repeatable code for your infrastructure. As Terraform isn’t always so flexible to different cases using count is one way to scale your resources up based on the need.
Lot of valuable information about Terraform I’ve got and used also here is from Yevgeniy Brikman’s book Terraform: Up and Running: Writing Infrastructure as Code. Definitely buy it if you are interested to learn more!
This is nice but still you need to find a way to share your code files.
Yeah absolutely! I just need to polish them at some point and upload to git.
Hi Simo,
Thanks for sharing the concept. Do we have this code available on github.
request you please share the url.
Keep writing such useful notes:)
Thanks.
Hi, Where I need to put entry for source = “../instance” and what would be the contents in it?
How would you extend your concept when each instance e.g. needs two additional data volumes attached?
Or you need to have the same firewall rules for each instance?
count only works in one resource definition, as far as I understand it.
Yeah there are definitely some hacks required if you go with count-approach. You would need to play with different set of variables (and the map with lists doesn’t work well yet). We discussed this also earlier with the team and since our projects usually involve instances without many similarities we mainly do all without count but utilize modules then from the project per instance/resource.