Slide 1

Slide 1 text

Copyright © 2019 HashiCorp Hands-On with Infrastructure-as-Code A Workshop on Terraform & Infrastructure !1

Slide 2

Slide 2 text

Presenter Rosemary Wang Developer Advocate at HashiCorp she/her @joatmon08 joatmon08 linkedin.com/in/rosemarywang/ !2

Slide 3

Slide 3 text

Teaching Assistants Allee Clark SRE at Oscar Health Abbas Syed Technical Account Manager at HashiCorp !3

Slide 4

Slide 4 text

Agenda Evolution of Infrastructure How did we get to Infrastructure-as-Code? Terraform Concepts How do we use it? Scaling Infrastructure-as-Code What do we do as a team? !4

Slide 5

Slide 5 text

Evolution of Infrastructure How did we get to Infrastructure-as-Code? !5

Slide 6

Slide 6 text

Infrastructure 
 is anything that supports an application ecosystem. !6

Slide 7

Slide 7 text

Maturity 7

Slide 8

Slide 8 text

Manual Everything 8

Slide 9

Slide 9 text

Basic Automation #!/bin/bash 9

Slide 10

Slide 10 text

Machine Virtualization 10

Slide 11

Slide 11 text

*aaS 11

Slide 12

Slide 12 text

Datacenter-as-Computer 12

Slide 13

Slide 13 text

Maturity 13

Slide 14

Slide 14 text

14

Slide 15

Slide 15 text

Capability Complexity 15

Slide 16

Slide 16 text

16

Slide 17

Slide 17 text

17

Slide 18

Slide 18 text

475 def update 476 return update_api if api_request? 477 478 if authorized_action(@account, @current_user, :manage_account_settings) 479 respond_to do |format| 480 481 custom_help_links = params[:account].delete :custom_help_links 482 if custom_help_links 483 @account.settings[:custom_help_links] = custom_help_links.select{|k, h| h['state'] != 'delete 484 hash = index_with_hash[1] 485 hash.delete('state') 486 hash.assert_valid_keys ["text", "subtext", "url", "available_to"] 487 hash 488 end 489 end 490 491 params[:account][:turnitin_host] = validated_turnitin_host(params[:account][:turnitin_host]) 492 enable_user_notes = params[:account].delete :enable_user_notes 493 allow_sis_import = params[:account].delete :allow_sis_import 494 params[:account].delete :default_user_storage_quota_mb unless @account.root_account? && !@accou 495 unless @account.grants_right? @current_user, :manage_storage_quotas 496 [:storage_quota, :default_storage_quota, :default_storage_quota_mb, 497 :default_user_storage_quota, :default_user_storage_quota_mb, 498 :default_group_storage_quota, :default_group_storage_quota_mb].each { |key| params[:account] 499 end 500 if params[:account][:services] 501 params[:account][:services].slice(*Account.services_exposed_to_ui_hash(nil, @current_user, @a 502 @account.set_service_availability(key, value == '1') 503 end 504 params[:account].delete :services 505 end 506 if @account.grants_right?(@current_user, :manage_site_settings) 507 # If the setting is present (update is called from 2 different settings forms, one for notifi 508 if params[:account][:settings] && params[:account][:settings][:outgoing_email_default_name_op 509 # If set to default, remove the custom name so it doesn't get saved 510 params[:account][:settings][:outgoing_email_default_name] = '' if params[:account][:setting 511 end 512 513 google_docs_domain = params[:account][:settings].try(:delete, :google_docs_domain) 514 if @account.feature_enabled?(:google_docs_domain_restriction) && 515 @account.root_account? && 516 [email protected]_admin? 517 @account.settings[:google_docs_domain] = google_docs_domain.present? ? google_docs_domain : 518 end 519 520 @account.enable_user_notes = enable_user_notes if enable_user_notes 521 @account.allow_sis_import = allow_sis_import if allow_sis_import && @account.root_account? 522 if @account.site_admin? && params[:account][:settings] 523 # these shouldn't get set for the site admin account 524 params[:account][:settings].delete(:enable_alerts) 525 params[:account][:settings].delete(:enable_eportfolios) 526 end 527 else 528 # must have :manage_site_settings to update these 529 [ :admins_can_change_passwords, 530 :admins_can_view_notifications, 531 :enable_alerts, 532 :enable_eportfolios, 533 :enable_profiles, 534 :show_scheduler, 535 :global_includes, 536 :gmail_domain 537 ].each do |key| 538 params[:account][:settings].try(:delete, key) 18

Slide 19

Slide 19 text

475 def update 476 return update_api if api_request? 477 478 if authorized_action(@account, @current_user, :manage_account_settings) 479 respond_to do |format| 480 481 custom_help_links = params[:account].delete :custom_help_links 482 if custom_help_links 483 @account.settings[:custom_help_links] = custom_help_links.select{|k, h| h['state'] != 'delete 484 hash = index_with_hash[1] 485 hash.delete('state') 486 hash.assert_valid_keys ["text", "subtext", "url", "available_to"] 487 hash 488 end 489 end 490 491 params[:account][:turnitin_host] = validated_turnitin_host(params[:account][:turnitin_host]) 492 enable_user_notes = params[:account].delete :enable_user_notes 493 allow_sis_import = params[:account].delete :allow_sis_import 494 params[:account].delete :default_user_storage_quota_mb unless @account.root_account? && !@accou 495 unless @account.grants_right? @current_user, :manage_storage_quotas 496 [:storage_quota, :default_storage_quota, :default_storage_quota_mb, 497 :default_user_storage_quota, :default_user_storage_quota_mb, 498 :default_group_storage_quota, :default_group_storage_quota_mb].each { |key| params[:account] 499 end 500 if params[:account][:services] 501 params[:account][:services].slice(*Account.services_exposed_to_ui_hash(nil, @current_user, @a 502 @account.set_service_availability(key, value == '1') 503 end 504 params[:account].delete :services 505 end 506 if @account.grants_right?(@current_user, :manage_site_settings) 507 # If the setting is present (update is called from 2 different settings forms, one for notifi 508 if params[:account][:settings] && params[:account][:settings][:outgoing_email_default_name_op 509 # If set to default, remove the custom name so it doesn't get saved 510 params[:account][:settings][:outgoing_email_default_name] = '' if params[:account][:setting 511 end 512 513 google_docs_domain = params[:account][:settings].try(:delete, :google_docs_domain) 514 if @account.feature_enabled?(:google_docs_domain_restriction) && 515 @account.root_account? && 516 [email protected]_admin? 517 @account.settings[:google_docs_domain] = google_docs_domain.present? ? google_docs_domain : 518 end 519 520 @account.enable_user_notes = enable_user_notes if enable_user_notes 521 @account.allow_sis_import = allow_sis_import if allow_sis_import && @account.root_account? 522 if @account.site_admin? && params[:account][:settings] 523 # these shouldn't get set for the site admin account 524 params[:account][:settings].delete(:enable_alerts) 525 params[:account][:settings].delete(:enable_eportfolios) 526 end 527 else 528 # must have :manage_site_settings to update these 529 [ :admins_can_change_passwords, 530 :admins_can_view_notifications, 531 :enable_alerts, 532 :enable_eportfolios, 533 :enable_profiles, 534 :show_scheduler, 535 :global_includes, 536 :gmail_domain 537 ].each do |key| 538 params[:account][:settings].try(:delete, key) 19 Infrastructure-as-Code

Slide 20

Slide 20 text

Infrastructure-as-Code Goals ▪ Unify the view of resources ▪ Support the modern data center (IaaS, PaaS, SaaS) ▪ Expose a way for individuals and teams to safely and predictably change infrastructure ▪ Provide a workflow that is technology agnostic ▪ Manage anything with an API !20

Slide 21

Slide 21 text

AWS CloudFormation AWS, YAML/JSON !21 Azure Resource Manager Azure, JSON Google Cloud Deployment Manager Google Cloud, YAML/JSON Ansible, Puppet, Chef Configuration Management Tools, YAML/JSON Terraform Most Infrastructure & More, HashiCorp Configuration Language Pulumi Most Public Clouds, 
 Javascript/Typescript/Python/Golang

Slide 22

Slide 22 text

“I don’t know how to code. But I do want to automate how I configure my infrastructure.” - An Infrastructure Engineer Who Started Using Google Cloud !22

Slide 23

Slide 23 text

“It was hard enough for me to learn infrastructure terms but writing code for it too? Difficult.” - A Developer Who Started Using Google Cloud !23

Slide 24

Slide 24 text

Terraform HashiCorp Configuration Language CODE EDITOR resource "google_compute_instance" "default" { name = "test" machine_type = "n1-standard-1" zone = "us-central1-a" tags = ["foo", "bar"] boot_disk { initialize_params { image = "debian-cloud/debian-9" } } // Local SSD disk scratch_disk { } network_interface { network = "default" access_config { // Ephemeral IP } } metadata = { foo = "bar" } metadata_startup_script = "echo hi > /test.txt" service_account { scopes = ["userinfo-email", "compute-ro", "storage-ro"] } } !24

Slide 25

Slide 25 text

Extensibility Configure Beyond Public Cloud Infrastructure CODE EDITOR # Create a new Datadog monitor resource "datadog_monitor" "foo" { name = "Name for monitor foo" type = "metric alert" message = "Monitor triggered. Notify: @hipchat-channel" escalation_message = "Escalation message @pagerduty" query = "avg(last_1h):avg:aws.ec2.cpu{environment:foo,host:foo} by {host} > 4" thresholds = { ok = 0 warning = 2 warning_recovery = 1 critical = 4 critical_recovery = 3 } notify_no_data = false renotify_interval = 60 notify_audit = false timeout_h = 60 include_tags = true silenced = { "*" = 0 } tags = ["foo:bar", "baz"] } !25

Slide 26

Slide 26 text

Terraform Concepts How do we use it? !26

Slide 27

Slide 27 text

Get Started ▪ Open Chrome. ▪ Go to hashi.co/ wwcode-nyc ▪ Click Start. !27 hashi.co/wwcode-nyc

Slide 28

Slide 28 text

!28

Slide 29

Slide 29 text

TERMINAL > terraform help Usage: terraform [-version] [-help] [args] The available commands for execution are listed below. The most common, useful commands are shown first, followed by less common or more advanced commands. If you're just getting started with Terraform, stick with the common commands. For the other commands, please read the help and docs before usage. Common commands: apply Builds or changes infrastructure console Interactive console for Terraform interpolations destroy Destroy Terraform-managed infrastructure env Workspace management fmt Rewrites config files to canonical format get Download and install modules for the configuration graph Create a visual graph of Terraform resources import Import existing infrastructure into Terraform init Initialize a Terraform working directory output Read an output from a state file plan Generate and show an execution plan providers Prints a tree of the providers used in the configuration refresh Update local state file against real resources show Inspect Terraform state or plan taint Manually mark a resource for recreation untaint Manually unmark a resource as tainted validate Validates the Terraform files version Prints the Terraform version workspace Workspace management !29

Slide 30

Slide 30 text

Concept: Providers “Plug-in” to the Terraform core codebase ▪ Interface between Terraform core and any API ▪ Create, read, update, and delete resources Develop your own! !30

Slide 31

Slide 31 text

!31 terraform.io/docs/providers/

Slide 32

Slide 32 text

!32

Slide 33

Slide 33 text

Command: terraform init Run: ▪ To initialize a new Terraform working directory ▪ After changing provider configuration
 You do not need to run terraform init before each command.
 Similar to git init! !33

Slide 34

Slide 34 text

!34

Slide 35

Slide 35 text

!35

Slide 36

Slide 36 text

Command: terraform plan Shows: ▪ What will happen ▪ Why it will happen Don’t need to guess changes or their effects. Plans can be saved. !36

Slide 37

Slide 37 text

TERMINAL ! + resource will be created
 - resource will be destroyed
 ~ resource will be updated in-place
 -/+ resources will be destroyed and re-created !37

Slide 38

Slide 38 text

!38

Slide 39

Slide 39 text

!39

Slide 40

Slide 40 text

!40

Slide 41

Slide 41 text

!41 terraform.io/docs/providers/kubernetes/

Slide 42

Slide 42 text

What should be in our terraform plan? TERMINAL > cd /root/terraform > terraform plan -out=nginx.tfplan !42

Slide 43

Slide 43 text

TERMINAL Terraform will perform the following actions: # kubernetes_deployment.application will be created + resource "kubernetes_deployment" "application" { + id = (known after apply) # . . . } # kubernetes_service.application will be created + resource "kubernetes_service" "application" { + id = (known after apply) + load_balancer_ingress = (known after apply) + metadata { + generation = (known after apply) + labels = { + "app" = "nginx" } + name = "nginx" + namespace = "default" + resource_version = (known after apply) + self_link = (known after apply) + uid = (known after apply) } # . . . } Plan: 2 to add, 0 to change, 0 to destroy. ------------------------------------------------------------------------ This plan was saved to: nginx.tfplan To perform exactly these actions, run the following command to apply: terraform apply "nginx.tfplan" !43

Slide 44

Slide 44 text

Concept: Resource Graph Helps determine: ▪ What gets created first ▪ How modifications affect other resources Based on graph theory !44

Slide 45

Slide 45 text

!45 terraform.io/docs/internals/graph.html

Slide 46

Slide 46 text

TERMINAL > terraform graph digraph { compound = "true" newrank = "true" subgraph "root" { "[root] data.google_compute_zones.available" [label = "data.google_compute_zones.available", sh ape = "box"] "[root] google_compute_network.vpc" [label = "google_compute_network.vpc", shape = "box"] "[root] google_compute_subnetwork.subnet" [label = "google_compute_subnetwork.subnet", shape = "box"] "[root] google_container_cluster.kubernetes" [label = "google_container_cluster.kubernetes", sh ape = "box"] "[root] local.labels" [label = "local.labels", shape = "note"] "[root] provider.google" [label = "provider.google", shape = "diamond"] "[root] var.cluster_name" [label = "var.cluster_name", shape = "note"] "[root] var.credentials" [label = "var.credentials", shape = "note"] "[root] var.kubernetes_version" [label = "var.kubernetes_version", shape = "note"] "[root] var.machine_type" [label = "var.machine_type", shape = "note"] !46

Slide 47

Slide 47 text

!47

Slide 48

Slide 48 text

Command: terraform apply Executes: ▪ Changes in order based on resource graph ▪ Parallel changes, if possible ▪ Error handling and recovery ▪ Updates in-place if possible or re-creates
 Produces a state file. !48

Slide 49

Slide 49 text

TERMINAL > cd /root/terraform > terraform apply 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: # . . . 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 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 kubernetes_service.application: Creating... kubernetes_service.application: Creation complete after 0s [id=default/nginx] kubernetes_deployment.application: Creating... kubernetes_deployment.application: Still creating... [10s elapsed] kubernetes_deployment.application: Creation complete after 16s [id=default/nginx] Apply complete! Resources: 2 added, 0 changed, 0 destroyed. !49

Slide 50

Slide 50 text

!50

Slide 51

Slide 51 text

TERMINAL > cat terraform.tfstate { "version": 4, "terraform_version": "0.12.5", "serial": 7, "lineage": "1d460dfb-d28d-04d7-bebb-867ab2389221", "outputs": {}, "resources": [ { "mode": "managed", "type": "kubernetes_deployment", "name": "application", "provider": "provider.kubernetes", "instances": [ { "schema_version": 0, "attributes": { "id": "default/nginx", "metadata": [ { "annotations": null, "generate_name": "", "generation": 1, "labels": { "app": "nginx" }, "name": "nginx", "namespace": "default", "resource_version": "793", "self_link": "/apis/apps/v1/namespaces/default/deployments/nginx", "uid": "72961252-acb6-11e9-b03e-42010a840047" } ], !51

Slide 52

Slide 52 text

Concept: State Before Infrastructure-as-Code: ▪ What’s the configuration? ▪ Manual changes = 
 State must be maintained appropriately. ▪ Like configuration backup. ▪ Separate state file per “environment”. !52

Slide 53

Slide 53 text

!53 terraform.io/docs/backends/types/local.html

Slide 54

Slide 54 text

Change State File Location From /root/terraform/ to / tmp/ CODE EDITOR # versions.tf terraform { required_version = "~> 0.12" backend "local" {} } provider "kubernetes" { host = "https://kubernetes:6443" version = "~> 1.7" } !54

Slide 55

Slide 55 text

Tip: Use separate files for different backend parameters! CODE EDITOR # backend.conf path = "/tmp/terraform.tfstate" !55

Slide 56

Slide 56 text

TERMINAL > terraform init -backend-config=backend.conf Initializing the backend... Initializing provider plugins... 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. !56

Slide 57

Slide 57 text

TERMINAL > terraform apply An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create # . . . 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 kubernetes_service.application: Creating... kubernetes_deployment.application: Creating... kubernetes_service.application: Creation complete after 5s [id=default/nginx] kubernetes_deployment.application: Creation complete after 8s [id=default/nginx] Apply complete! Resources: 2 added, 0 changed, 0 destroyed. The state of your infrastructure has been saved to the path below. This state is required to modify and destroy your infrastructure, so keep it safe. To inspect the complete state use the `terraform show` command. State path: /tmp/terraform.tfstate !57

Slide 58

Slide 58 text

TERMINAL > cat /tmp/terraform.tfstate { "version": 4, "terraform_version": "0.12.5", "serial": 3, "lineage": "a8d16ab1-f30d-d21c-97e5-5e3bee628081", "outputs": {}, "resources": [ { "mode": "managed", "type": "kubernetes_deployment", "name": "application", "provider": "provider.kubernetes", "instances": [ !58

Slide 59

Slide 59 text

Concept: Variables Parametrize when possible. ▪ Can have defaults ▪ Can be overridden by CLI or variables file For passwords or secrets, do not leave them in any files! Pass them via command line or environment variables. !59

Slide 60

Slide 60 text

Variable Types Valid types: string, number, bool Complex types: list, map If omitted, the type is inferred from the type of default. If no default, type is string. !60

Slide 61

Slide 61 text

CODE EDITOR # /root/terraform/variables.tf variable "name" { type = string default = “nginx” } variable "namespace" { default = "default" } variable "replicas" { default = 1 } !61

Slide 62

Slide 62 text

!62 terraform.io/docs/configuration/variables.html

Slide 63

Slide 63 text

TERMINAL > ls -al /root/terraform total 40 drwxr-xr-x 3 root root 4096 Jul 23 14:41 . drwx------ 10 root root 4096 Jul 23 14:41 .. drwxr-xr-x 3 root root 4096 Jul 23 14:40 .terraform -rw-r--r-- 1 root root 32 Jul 23 14:40 backend.conf -rw-r--r-- 1 root root 723 Jul 23 14:21 deployment.tf -rw-r--r-- 1 root root 3148 Jul 23 14:28 nginx.tfplan -rw-r--r-- 1 root root 305 Jul 23 14:21 service.tf -rw-r--r-- 1 root root 24 Jul 23 14:41 terraform.tfvars -rw-r--r-- 1 root root 335 Jul 23 14:21 variables.tf -rw-r--r-- 1 root root 152 Jul 23 14:41 versions.tf > cat terraform.tfvars replicas= name= !63

Slide 64

Slide 64 text

Update the Variables !64

Slide 65

Slide 65 text

!65

Slide 66

Slide 66 text

TERMINAL > terraform apply # . . . Plan: 2 to add, 0 to change, 2 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 kubernetes_service.application: Destroying... [id=default/nginx] kubernetes_service.application: Destruction complete after 0s kubernetes_service.application: Creating... kubernetes_service.application: Creation complete after 1s [id=default/myapp] kubernetes_deployment.application: Destroying... [id=default/nginx] kubernetes_deployment.application: Destruction complete after 0s kubernetes_deployment.application: Creating… kubernetes_deployment.application: Creation complete after 4s [id=default/myapp] Apply complete! Resources: 2 added, 0 changed, 2 destroyed. The state of your infrastructure has been saved to the path below. This state is required to modify and destroy your infrastructure, so keep it safe. To inspect the complete state use the `terraform show` command. State path: /tmp/terraform.tfstate !66

Slide 67

Slide 67 text

Scaling Infrastructure-as- Code What do we do as a team? !67

Slide 68

Slide 68 text

Modules For others to use. !68 terraform.io/docs/modules/

Slide 69

Slide 69 text

CODE EDITOR data "google_service_account" "vault_server" { account_id = "vault-server" } module "cluster" { # version control, local directory, or Terraform module registry source = "./kubernetes-cluster" project = var.project region = var.region subnet_cidr = var.subnet_cidr cluster_name = var.cluster_name kubernetes_version = "1.13.7-gke.8" service_account = data.google_service_account.vault_server.email num_clusters = 16 } !69

Slide 70

Slide 70 text

!70 registry.terraform.io/

Slide 71

Slide 71 text

State per “Environment” Version Control Continuous Integration Testing !71

Slide 72

Slide 72 text

Software Development Patterns? !72

Slide 73

Slide 73 text

Workflows Software Development 1. Fix code. 2. (Maybe) test. 3.git commit 4. Work a little more. 5.git push 6. Test everything. Infrastructure-as-Code 1. Fix configuration. 2. Test syntax. 3. git commit 4. Work a little more. 5. git push 6. Check differences. 7. Apply new configuration. 8. Test everything. !73

Slide 74

Slide 74 text

General Terraform Repository Structure !74

Slide 75

Slide 75 text

Collaboration Demo !75 github.com/joatmon08/terraform0.12-cloud/tree/remote-state

Slide 76

Slide 76 text

Many more nuances…! (Click for links below.) 1. Migrating Terraform Resources 2. Upgrading Terraform 3. Workspaces, Terraform Cloud’s way of separating state 4. Secrets in infrastructure configuration !76

Slide 77

Slide 77 text

learn.hashicorp.com/terraform/ terraform.io/downloads.html terraform.io/docs/ !77

Slide 78

Slide 78 text

Rosemary Wang Developer Advocate at HashiCorp she/her @joatmon08 joatmon08 linkedin.com/in/rosemarywang/ !78