halo sobat sekolahlinux, jika sebelumnya kita membahas tentang ansible sebagai konfigurasi management nah sekarang kita akan membahas terraform untuk keperluan membangun env di aws, oke langsung saja
pertama kita install dulu terraform pada linux os yang kita gunakan, disini saya menggunakan linux ubuntu 16.04
download package binary terraform terbaru di sini https://www.terraform.io/downloads.html
wget https://releases.hashicorp.com/terraform/0.11.7/terraform_0.11.7_linux_amd64.zip
jika sudah di download extract package nya
unzip https://releases.hashicorp.com/terraform/0.11.7/terraform_0.11.7_linux_amd64.zip
letakkan file binary terraform dan pindahkan hasil extract ke foder /usr/local/bin/
mv terraform /usr/local/bin/
oke sampai sini proses installasi sudah selesai sekarang kita akan masuk ke pembahasan bagaimana cara menggunakan terraform juga struktur layout filenya.
pertama kita akan melihat struktur file terraform yang saya gunakan yang ada dalam folder /home/ubuntu/terraform/
(d) = directory (f) = file . ├── README.MD (f) └── aws (d) ├── modules (d) │ └── ec2_instance (d) │ ├── main.tf (f) │ ├── outputs.tf (f) │ └── variables.tf (f) └── sekolahlinux-terraform (d) ├── main.tf (f) └── provisioning (d) ├── ansible_hosts (f) ├── ansible-deploy.sh (f) └── userdata.txt (f)
pada layout file diatas jelas sekali kalau saya membuat feature ec2 di aws menjadi sebuah module, kenapa harus dibuat module, agar jika kedepannya ada project untuk membuat template terraform lagi saya bisa menggunakan module tersebut, tanpa harus panjang-panjang ngetik lagi, nah berikut dibawah ini isi dari tiap file diatas
pertama mari kita lihat isi file dari module ec2_instance
pada file ./aws/modules/ec2_instance/main.tf
###### # EC2 instance ###### resource "aws_instance" "this" { count = "${var.instance_count}" ami = "${var.ami}" instance_type = "${var.instance_type}" user_data = "${file(var.user_data)}" subnet_id = "${var.subnet_id}" key_name = "${var.key_name}" monitoring = "${var.monitoring}" vpc_security_group_ids = ["${var.vpc_security_group_ids}"] iam_instance_profile = "${var.iam_instance_profile}" associate_public_ip_address = "${var.associate_public_ip_address}" private_ip = "${var.private_ip}" ipv6_address_count = "${var.ipv6_address_count}" ipv6_addresses = "${var.ipv6_addresses}" ebs_optimized = "${var.ebs_optimized}" volume_tags = "${var.volume_tags}" root_block_device = ["${var.root_block_device}"] ebs_block_device = "${var.ebs_block_device}" ephemeral_block_device = "${var.ephemeral_block_device}" source_dest_check = "${var.source_dest_check}" disable_api_termination = "${var.disable_api_termination}" instance_initiated_shutdown_behavior = "${var.instance_initiated_shutdown_behavior}" placement_group = "${var.placement_group}" tenancy = "${var.tenancy}" tags = "${merge(var.tags, map("Name", format("%s%d", var.tags_name, count.index + var.tags_title_number)), map("Hostname", format("%s%d", var.tags_hostname, count.index + var.tags_title_number)))}" }
lalu pada file ./aws/modules/ec2_instance/outputs.tf isinya seperti dibawah
output "id" { description = "List of IDs of instances" value = ["${aws_instance.this.*.id}"] } output "availability_zone" { description = "List of availability zones of instances" value = ["${aws_instance.this.*.availability_zone}"] } // GH issue: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance/issues/8 //output "placement_group" { // description = "List of placement groups of instances" // value = ["${element(concat(aws_instance.this.*.placement_group, list("")), 0)}"] //} output "key_name" { description = "List of key names of instances" value = ["${aws_instance.this.*.key_name}"] } output "public_dns" { description = "List of public DNS names assigned to the instances. For EC2-VPC, this is only available if you've enabled DNS hostnames for your VPC" value = ["${aws_instance.this.*.public_dns}"] } output "public_ip" { description = "List of public IP addresses assigned to the instances, if applicable" value = ["${aws_instance.this.*.public_ip}"] } output "network_interface_id" { description = "List of IDs of the network interface of instances" value = ["${aws_instance.this.*.network_interface_id}"] } output "primary_network_interface_id" { description = "List of IDs of the primary network interface of instances" value = ["${aws_instance.this.*.primary_network_interface_id}"] } output "private_dns" { description = "List of private DNS names assigned to the instances. Can only be used inside the Amazon EC2, and only available if you've enabled DNS hostnames for your VPC" value = ["${aws_instance.this.*.private_dns}"] } output "private_ip" { description = "List of private IP addresses assigned to the instances" value = ["${aws_instance.this.*.private_ip}"] } output "security_groups" { description = "List of associated security groups of instances" value = ["${aws_instance.this.*.security_groups}"] } output "vpc_security_group_ids" { description = "List of associated security groups of instances, if running in non-default VPC" value = ["${aws_instance.this.*.vpc_security_group_ids}"] } output "subnet_id" { description = "List of IDs of VPC subnets of instances" value = ["${aws_instance.this.*.subnet_id}"] } output "tags_name" { description = "List of tags of instances" value = ["${aws_instance.this.*.tags.Name}"] } output "tags_hostname" { description = "List of tags of instances" value = ["${aws_instance.this.*.tags.Hostname}"] }
lalu pada file ./aws/modules/ec2_instance/variables.tf isinya seperti dibawah
variable "tags_name" { description = "Name to be used on all resources as prefix" } variable "tags_title_number" { description = "Numbering for tag Name" } variable "tags_hostname" { description = "Hostname to be used on all resources as prefix" } variable "instance_count" { description = "Number of instances to launch" default = 1 } variable "ami" { description = "ID of AMI to use for the instance" } variable "placement_group" { description = "The Placement Group to start the instance in" default = "" } variable "tenancy" { description = "The tenancy of the instance (if the instance is running in a VPC). Available values: default, dedicated, host." default = "default" } variable "ebs_optimized" { description = "If true, the launched EC2 instance will be EBS-optimized" default = false } variable "disable_api_termination" { description = "If true, enables EC2 Instance Termination Protection" default = false } variable "instance_initiated_shutdown_behavior" { description = "Shutdown behavior for the instance" # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/terminating-instances.html#Using_ChangingInstanceInitiatedShutdownBehavior default = "" } variable "instance_type" { description = "The type of instance to start" } variable "key_name" { description = "The key name to use for the instance" default = "" } variable "monitoring" { description = "If true, the launched EC2 instance will have detailed monitoring enabled" default = false } variable "vpc_security_group_ids" { description = "A list of security group IDs to associate with" type = "list" } variable "subnet_id" { description = "The VPC Subnet ID to launch in" } variable "associate_public_ip_address" { description = "If true, the EC2 instance will have associated public IP address" default = false } variable "private_ip" { description = "Private IP address to associate with the instance in a VPC" default = "" } variable "source_dest_check" { description = "Controls if traffic is routed to the instance when the destination address does not match the instance. Used for NAT or VPNs." default = true } variable "user_data" { description = "The user data to provide when launching the instance" default = "" } variable "iam_instance_profile" { description = "The IAM Instance Profile to launch the instance with. Specified as the name of the Instance Profile." default = "" } variable "ipv6_address_count" { description = "A number of IPv6 addresses to associate with the primary network interface. Amazon EC2 chooses the IPv6 addresses from the range of your subnet." default = 0 } variable "ipv6_addresses" { description = "Specify one or more IPv6 addresses from the range of the subnet to associate with the primary network interface" default = [] } variable "tags" { description = "A mapping of tags to assign to the resource" default = {} } variable "volume_tags" { description = "A mapping of tags to assign to the devices created by the instance at launch time" default = {} } variable "root_block_device" { description = "Customize details about the root block device of the instance. See Block Devices below for details" default = {} } variable "ebs_block_device" { description = "Additional EBS block devices to attach to the instance" default = [] } variable "ephemeral_block_device" { description = "Customize Ephemeral (also known as Instance Store) volumes on the instance" default = [] } variable "network_interface" { description = "Customize network interfaces to be attached at instance boot time" default = [] }
selanjutnya mari kita lihat isi file dari template sekolahlinux-terraform
pada file ./aws/sekolahlinux-terraform/main.tf
################################################################################# ################################################################################# ################################################################################# provider "aws" { region = "ap-southeast-1" } ################################################################################# ################################################################################# ################################################################################# module "ec2_instance" { source = "../modules/ec2_instance/" instance_count = 2 tags_name = "webserver-sekolahlinux" tags_hostname = "webserver" tags_title_number = 1 ami = "ami-81cefcfd" instance_type = "c5.xlarge" key_name = "sekolahlinux" monitoring = true vpc_security_group_ids = ["sg-3c166256","sg-1c916326"] subnet_id = "subnet-2736a51e" associate_public_ip_address = false user_data = "./provisioning/userdata.txt" root_block_device = { volume_size = "100" } tags = { Environtment = "production" CreateBy = "terraform" } } ################################################################################# ################################################################################# ################################################################################# resource "null_resource" "this" { triggers { cluster_instance_ids = "${join("\n", module.ec2_instance.id)}" } provisioner "local-exec" { working_dir = "./provisioning" command = "echo '[webserver-ubuntu:vars] \nansible_ssh_private_key_file = /home/ubuntu/.ssh/id_rsa \n\n[webserver-ubuntu:children] \nwebserver-sekolahlinux \n\n[webserver-sekolahlinux]' > ansible_hosts" } provisioner "local-exec" { working_dir = "./provisioning" command = "echo '${join("\n", formatlist("%s ansible_host=%s ansible_port=22 ansible_user=ubuntu", module.ec2_instance.tags_hostname, module.ec2_instance.private_ip))}' >> ansible_hosts" } provisioner "local-exec" { working_dir = "./provisioning" command = "echo \"PLEASE WAIT 60s\" && sleep 60 && sh ansible-deploy.sh" } } ################################################################################# ################################################################################# ################################################################################# output "internal_ip" { description = "List of public IP addresses assigned to the instances, if applicable" value = "${module.ec2_instance.private_ip}" } output "tags" { description = "List of public IP addresses assigned to the instances, if applicable" value = "${module.ec2_instance.tags_name}" }
lalu pada file ./aws/sekolahlinux-terraform/provisioning/ansible-deploy.sh
#!/bin/bash cd /home/ubuntu/ansible-playbook/ ansible-playbook sekolahlinux-playbook.yml -i terraform/aws/sekolahlinux-terraform/provisioning/ansible_hosts --vault-pass=~/.vpass --extra-vars "user_deployment=ubuntu" --skip-tags "updateos"
lalu pada file ./aws/sekolahlinux-terraform/provisioning/userdata.txt
#!/bin/bash systemctl stop apt-daily.service systemctl kill --kill-who=all apt-daily.service # wait until `apt-get updated` has been killed while ! (systemctl list-units --all apt-daily.service | fgrep -q dead) do sleep 1; done
lalu pada file ./aws/sekolahlinux-terraform/provisioning/ansible_hosts
[webserver-ubuntu:vars] ansible_ssh_private_key_file = /home/ubuntu/.ssh/id_rsa [webserver-ubuntu:children] webserver-sekolahlinux [webserver-sekolahlinux] webserver1 ansible_host=172.16.2.23 ansible_port=22 ansible_user=ubuntu webserver2 ansible_host=172.16.3.23 ansible_port=22 ansible_user=ubuntu
penjelasan pada template sekolahlinux-terraform
module "ec2_instance" { source = "../modules/ec2_instance/" instance_count = 2 tags_name = "webserver-sekolahlinux" tags_hostname = "webserver" tags_title_number = 1 ami = "ami-8f137gg3" instance_type = "c5.xlarge" key_name = "sekolahlinux" monitoring = true vpc_security_group_ids = ["sg-3c166256","sg-1c916326"] subnet_id = "subnet-2736a51e" associate_public_ip_address = false user_data = "./provisioning/userdata.txt" root_block_device = { volume_size = "100" } tags = { Environtment = "production" CreateBy = "terraform" } }
didalam terraform terdapat 3 type variable yaitu strings, maps, lists, boolean detailnya kamu bisa baca di link type_variable_terraform , berikut dibawah ini paramater yang harus kamu rubah beserta penjelasannya pada template terraform untuk pembuatan instance di aws
instance_count
variable instance_count untuk menentukan berapa jumlah instance yang ingin kamu buat dalam sekali execute pada contoh script diatas saya memberikan value 2, yang menandakan saya ingin membuat sebanyak 2 instance
tags_name
variable tags_name untuk menentukan Tag Name pada instance aws yang akan kamu buat
tags_hostname
variable tags_hostname untuk menentukan Tag Hostname pada instance aws, juga digunakan untuk keperluan generate ansible host, yang mana value dari tag ini akan digunakan sebagai inventory_hostname pada host ansible
tags_title_number
variable tags_title_number untuk menentukan angka atau nomer dibelakang value dari tags_name, misal jika kita mengisi value tags ini dengan angka 1 dengan jumlah instance_count 2 makanya numbering akan dimulai dari angka satu seperti ini (webserver-sekolahlinux1, webserver-sekolahlinux2) namun jika tags_title_number dimulai dari angka 7 maka hasilnya akan seperti berikut (webserver-sekolahlinux7, webserver-sekolahlinux8)
ami
variable ami digunakan untuk menentukan ami mana yang akan kita gunakan, pada value ami diatas saya menggunakan ami ubuntu dengan id ami-81cefcfd dan nama ami nya ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-20180522
instance_type
variable instance_type digunakan untuk menentukan size resource untuk instance aws yang kita gunakan, untuk melihat list type instance kalian bisa ke link ini => instance_type aws
key_name
varibale key_name digunakan untuk menentukan ssh_pubkey existing mana yang akan kita inject kedalam instance tersebut
monitoring
variable monitoring memiliki type bolean, jadi disini kamu hanya bisa memberi value true atau false, tag ini sendiri berguna untuk mengaktifkan feature moniroting aws pada instance tersebut atau tidak
vpc_security_group_ids
variable vpc_security_group_ids digunakan untuk menentukan secgroup pada instance tersebut, value harus berisi secgroup_id, bagaima jika ada lebih dari 1 vpc secgroup, kalian bisa mengisi seperti berikut [“sg-3c166256”, “sg-1c916326”]
subnet_id
variable subnet_id digunakan untuk menentukan vpc & subnet mana yang akan kita gunakan untuk instance yang akan kita buat
associate_public_ip_address
variable associate_public_ip_address memiliki type bolean, dan variable ini digunakan untuk menentukan apakah instance kamu ingin diberikan dynamic public_ip atau tidak
user_data
variable user_data digunakan untuk menyisipkan script_bash yang akan dijalankan didalam os intance yang akan kita buat saat pertama kali instance tersebut hidup, misal kita ingin mematikan feature autoupdate saat sebuah instance dengan os ubuntu berjalan pertama kali
root_block_device
variable root_block_device digunakan untuk menentukan size root disk os dari instance yang akan kita buat, untuk variable ini typenya adalah list didalamnya ada sub default variable volume_size pada variable ini kamu tidak dapat sembarangan menambahkan sub variable, hanya sub default variable dari variable root_block_device saja yang bisa kamu tambahkan, contoh seperti dibawah
root_block_device = { volume_size = "100" }
tags
variable tags digunakan untuk menyisipkan sub tags variable tambahan diluar dari tags default di module (Name, Hostname) yang sudah dibuat, disini kamu bisa menambahkan sub tags, sesuai keinginan kamu, type variable tags adalah maps
tags = { Environtment = "production" CreateBy = "terraform" }
Provisioning terraform AWS
tujuan dari provisioning pada terraform adalah untuk melakukan automasi konfigurasi lebih lanjut terhadap instance yang sudah selesai dibuat dengan terraform
buka ./aws/sekolahlinux-terraform/main.tf dan rubah paramater dibawah sesuai dengan project yang akan dibuat
resource "null_resource" "sekolahlinux" { triggers { cluster_instance_ids = "${join("\n", module.ec2_instance.id)}" } provisioner "local-exec" { working_dir = "./provisioning" command = "echo '[webserver-ubuntu:vars] \nansible_ssh_private_key_file = /home/ubuntu/.ssh/id_rsa \n\n[webserver-ubuntu:children] \nwebserver-sekolahlinux \n\n[webserver-sekolahlinux]' > ansible_hosts" } provisioner "local-exec" { working_dir = "./provisioning" command = "echo '${join("\n", formatlist("%s ansible_host=%s ansible_port=22 ansible_user=ubuntu", module.ec2_instance.tags_hostname, module.ec2_instance.private_ip))}' >> ansible_hosts" } provisioner "local-exec" { working_dir = "./provisioning" command = "echo \"PLEASE WAIT 60s\" && sleep 60 && sh ansible-deploy.sh" } }
tujuan dari provisioning diatas adalah untuk generate file ./terraform/aws/sekolahlinux-terraform/provisioning/ansible_hosts berdasarkan dari ip_address baik public ataupun private yang dihasilkan dari pembuatan instance dengan terraform, lalu setelahnya akan menjalankan script ansible-deploy.sh untuk melakukan provisioning dengan aws
How to run terraform
add aws access_key & secret_access_key
selanjutnya export varibale dibawah pada host tempat kita akan menjalankan terraform template, untuk mendapatkan access_key dan secret_access_key kamu bisa mengikuti link berikut https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html lalu jika sudah dapat ikuti perintah dibawah, ganti XXXXX dengan access_key dan secret_access_key yang sudah kita dapatkan
export AWS_ACCESS_KEY_ID='XXXXXXXXXXXXXXXXXXXXXXXXXXXX' export AWS_SECRET_ACCESS_KEY='XXXXXXXXXXXXXXXXXXXXXXXXXXXX'
terraform init
jalankan perintah terraform init
untuk melakukan download plugin terraform aws dan juga melakukan mapping terhadap module
terraform plan
jalankan perintah terraform plan
untuk melihat/mereview template terraform sebelum dilakukan implementasi di aws
terraform apply
jalankan perintah terraform apply
untuk mengeksekusi template terraform dan mengimplementasikan ke aws
terraform destroy
jalankan perintah terraform destroy
untuk destroy seluruh instance aws yang kalian buat menggunakan template terraform aws
Untuk lebih detailnya kalian bisa mengunduh filenya di github sekolahlinux
untuk download module melalui terraform module bisa ke link dibawah
lalu untuk detail functions yang digunakan pada terraform kalian bisa lihat di sini
jika ada yang ingin kalian tanyakan silahkan comment dibawah atau comment di github repo sekolahlinux ya… terimakasih 🙂