Ce post a pour objectif de montrer comment créer une Machine Virtuelle pas à pas sur Amazon Web Servicesavec du code uniquement grâce à Terraform. On pourrait bien sûr utiliser l’interface Graphique ou simplement l’interface ligne de commande aws-cli. Néanmoins avec ces interface l’automatisation devient presque impossible ou complexe. Il y aurait peut être d’autres moyens mais commençons par un. En effet, une étude rapide montre que terraform a des bonnes qualité et open source.
Ce billet est le premier d’une série de permettant monter une plateforme complète sur AWS. Pour l’instant commençons à jouer petite à petit avec terraform et aws.
Préparation de l’environnement de développement (votre usine à machines):
C’est fini les fer-à-souder, carte mère, disque dur, carte graphique, carte réseau, câblage électrique .etc…. Il suffit de disposer d’un ordinateur voire juste un raspberry, une connexion internet (quand même on ne peut plus vivre sans) et une carte bleu. Il ne faut pas oublier que le cloud est juste des machines gérées par quelqu’un d’autres.
- Créer un compte chez AWS. Il y a une offre gratuite qui permettrait de jouer à minima !. Néanmoins il faut saisir une carte bleu quand même.
- Avoir un « bon » IDE pour écrire DES lignes de code. Atom me semble intéressant. Par contre, il n’est pas packagé pour Rasperry PI 3 (arm) sous rasdebian. Sinon un bon vieux vi (c’est comme le vélo si vous l’avez appri un jour ça se n’oubli jamais) ou notepad font bien l’affaire.
- Télécharger et décompresser le package. Il ne faut pas oublier de mettre le binaire dans le PATH. Sur mon raspberrypi j’ai choisi de le mettre sous /usr/local/bin.
$wget https://releases.hashicorp.com/terraform/0.11.1/terraform_0.11.1_linux_arm.zip $unzip terraform_0.11.1_linux_arm.zip $sudo mv terraform /usr/local/bin/ $terraform -v Terraform v0.11.1
Créer et ranger vos clés d’administration AWS
- Connectez vous a votre console aws puis la gestion des utilisateurs https://console.aws.amazon.com/iam/home?#/users
- Créez un utilisateur de programmation ayant les droits de gérer le service EC2.
- Récupérez bien l’ACCESS KEY et la SECRET ACCESS KEY. Attention gardez les dans un endroit sûr.
- Pour que terraform puisse se connecter avec le compte de programmation à aws, il a besoin de trouver ce couple de clés dans les emplacements suivants :
- le code source de la config
- variables d’environnement système
- credential home
Je préfère que ça soit défini dans ~/.aws/credential
pi@raspberrypi:~/.aws $ cat credentials [default] aws_access_key_id = ABCDEFGHIJKLM aws_secret_access_key = **********************
Créer votre première VM AWS avec Terraform
1er test : Création avec informations minimale
- Définir le provider à utiliser, choisir la région de déploiement et initialiser votre environnement : https://www.terraform.io/docs/providers/aws/index.html
# Configure the AWS Provider provider "aws" { region = "eu-west-1" }
$ terraform init Initializing provider plugins... - Checking for available provider plugins on https://releases.hashicorp.com... - Downloading plugin for provider "aws" (1.6.0)... The following providers do not have any version constraints in configuration, so the latest version was installed. To prevent automatic upgrades to new major versions that may contain breaking changes, it is recommended to add version = "..." constraints to the corresponding provider blocks in configuration, with the constraint strings suggested below. * provider.aws: version = "~> 1.6" Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
- Chercher l’image du système que vous vous voulez instancier. Je vais partir sur un Ubuntu xenial : https://eu-west-1.console.aws.amazon.com/ec2/v2/home?region=eu-west-1#LaunchInstanceWizard: ami-8fd760f6. Sinon un petit helper pour choisir la dernière image.
data "aws_ami" "ubuntu" { most_recent = true filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-*"] } filter { name = "virtualization-type" values = ["hvm"] } owners = ["099720109477"] # Canonical } resource "aws_instance" "web" { ami = "${data.aws_ami.ubuntu.id}" instance_type = "t2.micro" tags { Name = "HelloWorld" } }
- Lancer la commande « plan » de terraform pour obtenir un plan d’exécution de la configuration et ainsi valider la syntaxe:
$ terraform plan Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage.</code> data.aws_ami.ubuntu: Refreshing state... ------------------------------------------------------------------------ An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: + aws_instance.web id: ami: "ami-63b0341a" associate_public_ip_address: availability_zone: ebs_block_device.#: ephemeral_block_device.#: instance_state: instance_type: "t2.micro" ipv6_address_count: ipv6_addresses.#: key_name: network_interface.#: network_interface_id: placement_group: primary_network_interface_id: private_dns: private_ip: public_dns: public_ip: root_block_device.#: security_groups.#: source_dest_check: "true" subnet_id: tags.%: "1" tags.Name: "HelloWorld" tenancy: volume_tags.%: vpc_security_group_ids.#: Plan: 1 to add, 0 to change, 0 to destroy. ------------------------------------------------------------------------ Note: You didn't specify an "-out" parameter to save this plan, so Terraform can't guarantee that exactly these actions will be performed if "terraform apply" is subsequently run.
- Appliquer votre configuration pour construire l’image sur AWS :
$ terraform apply data.aws_ami.ubuntu: Refreshing state... An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: + aws_instance.web id: ami: "ami-63b0341a" associate_public_ip_address: availability_zone: ebs_block_device.#: ephemeral_block_device.#: instance_state: instance_type: "t2.micro" ipv6_address_count: ipv6_addresses.#: key_name: network_interface.#: network_interface_id: placement_group: primary_network_interface_id: private_dns: private_ip: public_dns: public_ip: root_block_device.#: security_groups.#: source_dest_check: "true" subnet_id: tags.%: "1" tags.Name: "HelloWorld" tenancy: volume_tags.%: vpc_security_group_ids.#: Plan: 1 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes aws_instance.web: Creating... ami: "" => "ami-63b0341a" associate_public_ip_address: "" => "" availability_zone: "" => "" ebs_block_device.#: "" => "" ephemeral_block_device.#: "" => "" instance_state: "" => "" instance_type: "" => "t2.micro" ipv6_address_count: "" => "" ipv6_addresses.#: "" => "" key_name: "" => "" network_interface.#: "" => "" network_interface_id: "" => "" placement_group: "" => "" primary_network_interface_id: "" => "" private_dns: "" => "" private_ip: "" => "" public_dns: "" => "" public_ip: "" => "" root_block_device.#: "" => "" security_groups.#: "" => "" source_dest_check: "" => "true" subnet_id: "" => "" tags.%: "" => "1" tags.Name: "" => "HelloWorld" tenancy: "" => "" volume_tags.%: "" => "" vpc_security_group_ids.#: "" => "" aws_instance.web: Still creating... (10s elapsed) aws_instance.web: Still creating... (20s elapsed) aws_instance.web: Still creating... (30s elapsed) aws_instance.web: Creation complete after 33s (ID: i-00f226b1a021ee933) Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
- A ce stade on obtient une VM ubuntu qui tourne bien, mais pas trop utile. Aucun compte d’accès n’est défini.
- Pour détruire la plateforme :
$ terraform destroy data.aws_ami.ubuntu: Refreshing state... aws_instance.web: Refreshing state... (ID: i-00f226b1a021ee933) An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: - destroy Terraform will perform the following actions: - aws_instance.web Plan: 0 to add, 0 to change, 1 to destroy. Do you really want to destroy? Terraform will destroy all your managed infrastructure, as shown above. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes aws_instance.web: Destroying... (ID: i-00f226b1a021ee933) aws_instance.web: Still destroying... (ID: i-00f226b1a021ee933, 10s elapsed)
2eme Test: Créer la VM avec un compte d’accès SSH
- Définition du key pairs necessaire pour se connecter à la VM :https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html (https://www.terraform.io/docs/providers/aws/r/key_pair.html)
- Générer vos paire de clé (ssh-keygen)
- Ajouter la resource suivantes dans le script terraform
resource "aws_key_pair" "deployer" { key_name = "deployer-key" public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3F6tyPEFEzV0LX3X8BsXdMsQz1x2cEikKDEY0aIj41qgxMCP/iteneqXSIFZBp5vizPvaoIR3Um9xK7PGoW8giupGn+EPuxIA4cDM4vzOqOkiMPhz5XK0whEjkVzTo4+S0puvDZuwIsdiW9mxhJc7tgBNL0cYlWSYVkz4G/fslNfRPW5mYAM49f4fhtxPb5ok4Q2Lg9dPKVHO/Bgeu5woMc7RY0p1ej6D4CKFE6lymSDJpW0YHX/wqE9+cfEauh7xZcG0q9t2ta6F6fmX0agvpFyZo8aFbXeUBr7osSCJNgvavWbM/06niWrOvYX2xwWdhXmXSrbX8ZbabVohBK41 email@example.com" }
- Référencer la clé dans la définition de l’instance:
resource "aws_instance" "web" { ami = "${data.aws_ami.ubuntu.id}" instance_type = "t2.micro" key_name = "deployer-key" tags { Name = "HelloWorld" } }
$ terraform.exe plan Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. data.aws_ami.ubuntu: Refreshing state... ------------------------------------------------------------------------ An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: + aws_instance.web id: <computed> ami: "ami-63b0341a" associate_public_ip_address: <computed> availability_zone: <computed> ebs_block_device.#: <computed> ephemeral_block_device.#: <computed> instance_state: <computed> instance_type: "t2.micro" ipv6_address_count: <computed> ipv6_addresses.#: <computed> key_name: "deployer-key" network_interface.#: <computed> network_interface_id: <computed> placement_group: <computed> primary_network_interface_id: <computed> private_dns: <computed> private_ip: <computed> public_dns: <computed> public_ip: <computed> root_block_device.#: <computed> security_groups.#: <computed> source_dest_check: "true" subnet_id: <computed> tags.%: "1" tags.Name: "HelloWorld" tenancy: <computed> volume_tags.%: <computed> vpc_security_group_ids.#: <computed> + aws_key_pair.deployer id: <computed> fingerprint: <computed> key_name: "deployer-key" public_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCziY2Iv7bISA+YHULJsLZnqov+0bb7GnB5JY0U2Frwf0b/Exo39y5Djxc5MCuya11Y5VVzTruRHcHoUDeG4Ef9WmceV1qd8okttr6nayTsU4qI8rNiIJzIsKVMgWJCNRCM52lmvLNvaa+XdrGUrgNxUvuW52dnGiO/LGMismjGOPVy7lNiJMOjfDw9FN0rL5CkOTnh+5kBeDlv/8p3oOZ0lgHLr9AE4lf0B8tPBHYZZbCrQHh4j3HDiaxRZ5wkG0tZFw2xS6MYW48aUZ0kZxWkjqFBxXdUTfp6ccDgx1fX+/zRDI69ilDCKe8CuG+Eazi3h0/ooIW1BKyc9BvwdBI1 Wicem-KERKENI@WK-THINK" Plan: 2 to add, 0 to change, 0 to destroy. ------------------------------------------------------------------------ Note: You didn't specify an "-out" parameter to save this plan, so Terraform can't guarantee that exactly these actions will be performed if "terraform apply" is subsequently run. $ terraform.exe apply data.aws_ami.ubuntu: Refreshing state... An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: + aws_instance.web id: <computed> ami: "ami-63b0341a" associate_public_ip_address: <computed> availability_zone: <computed> ebs_block_device.#: <computed> ephemeral_block_device.#: <computed> instance_state: <computed> instance_type: "t2.micro" ipv6_address_count: <computed> ipv6_addresses.#: <computed> key_name: "deployer-key" network_interface.#: <computed> network_interface_id: <computed> placement_group: <computed> primary_network_interface_id: <computed> private_dns: <computed> private_ip: <computed> public_dns: <computed> public_ip: <computed> root_block_device.#: <computed> security_groups.#: <computed> source_dest_check: "true" subnet_id: <computed> tags.%: "1" tags.Name: "HelloWorld" tenancy: <computed> volume_tags.%: <computed> vpc_security_group_ids.#: <computed> + aws_key_pair.deployer id: <computed> fingerprint: <computed> key_name: "deployer-key" public_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCziY2Iv7bISA+YHULJsLZnq WJCNRCM52lmvLNvaa+XdrGUrgNxUvuW52dnGiO/LGMismjGOPVy7lNiJMOjfDw9FN0rL5CkOTnh+5kBeDlv/8p3oOZ0lgHLr9A 3h0/ooIW1BKyc9BvwdBI1 Wicem-KERKENI@WK-THINK" Plan: 2 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes aws_key_pair.deployer: Creating... fingerprint: "" => "<computed>" key_name: "" => "deployer-key" public_key: "" => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCziY2Iv7bISA+YHULJsLZnqov+0bb7GnB5JY0U aa+XdrGUrgNxUvuW52dnGiO/LGMismjGOPVy7lNiJMOjfDw9FN0rL5CkOTnh+5kBeDlv/8p3oOZ0lgHLr9AE4lf0B8tPBHYZZb vwdBI1 Wicem-KERKENI@WK-THINK" aws_instance.web: Creating... ami: "" => "ami-63b0341a" associate_public_ip_address: "" => "<computed>" availability_zone: "" => "<computed>" ebs_block_device.#: "" => "<computed>" ephemeral_block_device.#: "" => "<computed>" instance_state: "" => "<computed>" instance_type: "" => "t2.micro" ipv6_address_count: "" => "<computed>" ipv6_addresses.#: "" => "<computed>" key_name: "" => "deployer-key" network_interface.#: "" => "<computed>" network_interface_id: "" => "<computed>" placement_group: "" => "<computed>" primary_network_interface_id: "" => "<computed>" private_dns: "" => "<computed>" private_ip: "" => "<computed>" public_dns: "" => "<computed>" public_ip: "" => "<computed>" root_block_device.#: "" => "<computed>" security_groups.#: "" => "<computed>" source_dest_check: "" => "true" subnet_id: "" => "<computed>" tags.%: "" => "1" tags.Name: "" => "HelloWorld" tenancy: "" => "<computed>" volume_tags.%: "" => "<computed>" vpc_security_group_ids.#: "" => "<computed>" aws_key_pair.deployer: Creation complete after 1s (ID: deployer-key) aws_instance.web: Still creating... (10s elapsed) aws_instance.web: Still creating... (20s elapsed) aws_instance.web: Creation complete after 25s (ID: i-0468a29150f912c9c) Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
L’accès à la machine n’est toujours pas possible avec le client ssh. En effet, la sécurité par défaut ne le permet pas.
3ieme Test : Paramétrage du firewall
- Définir un VPC:
# variables ============================================================== variable "region" { default = "eu-west-1" } # VPC ============================================================== resource "aws_vpc" "vpc01" { cidr_block = "10.1.0.0/16" tags { Name = "vpc_global" } } # Subnets ============================================================== resource "aws_subnet" "vmtest-a" { vpc_id = "${aws_vpc.vpc01.id}" cidr_block = "10.1.0.0/23" availability_zone = "${var.region}a" map_public_ip_on_launch = true tags { Name = "subnet_vmtest_a" } }
- Définir la table de routage pour le VPC:
# routing ============================================================== resource "aws_internet_gateway" "gw-to-internet01" { vpc_id = "${aws_vpc.vpc01.id}" } resource "aws_route_table" "route-to-gw01" { vpc_id = "${aws_vpc.vpc01.id}" route { cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.gw-to-internet01.id}" } } resource "aws_route_table_association" "vmtest-a" { subnet_id = "${aws_subnet.vmtest-a.id}" route_table_id = "${aws_route_table.route-to-gw01.id}" }
- Définir la règle de sécurité pour autoriser l’accès ssh que depuis quelques ips spécifiques (pour des raisons de sécurités) et autorisé la vm a accéder n’importe où :
resource "aws_security_group" "sg_infra" { name = "sg_infra" description = "standard ssh & monitoring" vpc_id = "${aws_vpc.vpc01.id}" tags { Name = "sg_infra" } ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = [ "**************/32" ] } ingress { from_port = -1 to_port = -1 protocol = "icmp" cidr_blocks = [ "**************/32" ] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
- Ajouter le groupe de sécurité et le réseau à l’instance :
resource "aws_instance" "web" { ami = "${data.aws_ami.ubuntu.id}" instance_type = "t2.micro" key_name = "deployer-key" subnet_id = "${aws_subnet.vmtest-a.id}" security_groups = [ "${aws_security_group.sg_infra.id}" ] tags { Name = "HelloWorld" } }
- Finally, on essaye de se connecter. On récupère l’ip et on teste le ssh :
$ terraform state list aws_ami.ubuntu aws_instance.web aws_internet_gateway.gw-to-internet01 aws_key_pair.deployer aws_route_table.route-to-gw01 aws_route_table_association.vmtest-a aws_security_group.sg_infra aws_subnet.vmtest-a aws_vpc.vpc01 $ terraform state show aws_instance.web id = i-06994705d02e89d70 ami = ami-63b0341a associate_public_ip_address = true availability_zone = eu-west-1a disable_api_termination = false ebs_block_device.# = 0 ebs_optimized = false ephemeral_block_device.# = 0 iam_instance_profile = instance_state = running instance_type = t2.micro ipv6_addresses.# = 0 key_name = deployer-key monitoring = false network_interface.# = 0 network_interface_id = eni-6856cc6b placement_group = primary_network_interface_id = eni-6856cc6b private_dns = ip-10-1-1-100.eu-west-1.compute.internal private_ip = 10.1.1.100 public_dns = public_ip = 34.241.243.116 root_block_device.# = 1 root_block_device.0.delete_on_termination = true root_block_device.0.iops = 100 root_block_device.0.volume_size = 8 root_block_device.0.volume_type = gp2 security_groups.# = 0 source_dest_check = true subnet_id = subnet-accf86f7 tags.% = 1 tags.Name = HelloWorld tenancy = default volume_tags.% = 0 vpc_security_group_ids.# = 1 vpc_security_group_ids.1603707239 = sg-4c8aa937 $ ping 34.241.243.116 Pinging 34.241.243.116 with 32 bytes of data: Reply from 34.241.243.116: bytes=32 time=51ms TTL=44 Reply from 34.241.243.116: bytes=32 time=50ms TTL=44 Ping statistics for 34.241.243.116: Packets: Sent = 2, Received = 2, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 50ms, Maximum = 51ms, Average = 50ms Control-C $ ssh ubuntu@34.241.243.116 The authenticity of host '34.241.243.116 (34.241.243.116)' can't be established. ECDSA key fingerprint is SHA256:el4TGdeAmljklvZG8OZfM/ajUJlj2uZi98+4kSF4jao. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '34.241.243.116' (ECDSA) to the list of known hosts. Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-1043-aws x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage Get cloud support with Ubuntu Advantage Cloud Guest: http://www.ubuntu.com/business/services/cloud 0 packages can be updated. 0 updates are security updates. The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. To run a command as administrator (user "root"), use "sudo <command>". See "man sudo_root" for details. ubuntu@ip-10-1-1-100:~$
L’intégralité du script est sur https://github.com/wkerkeni/terraform-samples
N’hésitez pas à commenter cet article et/ou me contacter 😉Références et liens utiles
Please llet me know if you’re looking for a article
writer for your site. You have somke really good posts and
I think I would be a good asset. If you ever want to take ome of the load off, I’d really like to
write some articles for your blog inn exchange for a
link back to mine. Please blazt me an e-mail if interested.
Cheers!