web-dev-qa-db-fra.com

Configuration initiale du backend de terraform en utilisant terraform

Je viens juste de commencer à utiliser terraform et j'aimerais pouvoir utiliser AWS S3 comme serveur principal pour stocker l'état de mes projets. 

terraform {
    backend "s3" {
      bucket = "tfstate"
      key = "app-state"
      region = "us-east-1"
    }
}

Je pense qu'il est judicieux de configurer mon compartiment S3, mes groupes IAM et mes stratégies pour l'infrastructure de stockage principale avec terraform. 

Si j'installe mon état d'arrière-plan avant d'appliquer mon infrastructure terraform initiale, il se plaint raisonnablement que le compartiment d'arrière-plan n'est pas encore créé. Ma question est donc la suivante: comment configurer mon back-end terraform avec terraform tout en conservant mon état pour le back-end suivi par terraform? On dirait un problème de poupées imbriquées.

J'ai quelques idées sur la façon de créer un script autour de cela, par exemple, pour vérifier si le compartiment existe ou si un état a été défini, puis amorcer terraform et enfin copier le terraform tfstate jusqu'à s3 à partir du système de fichiers local après la première exécution. Mais avant de suivre cette voie laborieuse, je pensais m'assurer que je ne manquais pas quelque chose d'évident.

18
Jed Schneider

Pour configurer cela à l'aide de terraform remote state, j'ai généralement un dossier séparé appelé remote-state dans mon dossier dev et prod terraform. 

Le fichier main.tf suivant définira votre état distant pour ce que vous avez publié:

provider "aws" {
  region = "us-east-1"
}

resource "aws_s3_bucket" "terraform_state" {
  bucket = "tfstate"

  versioning {
    enabled = true
  }

  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_dynamodb_table" "terraform_state_lock" {
  name           = "app-state"
  read_capacity  = 1
  write_capacity = 1
  hash_key       = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}

Rendez-vous ensuite dans ce dossier en utilisant cd remote-state et exécutez terraform init && terraform apply; il ne devrait être exécuté qu'une seule fois. Vous pouvez ajouter quelque chose à bucket et au nom de table dynamodb pour séparer vos différents environnements.

25
Austin Davis

Comme vous l'avez découvert, terraform ne permet pas de construire les composants dont terraform a besoin. 

Bien que je comprenne l’inclination de terraform à «tout suivre», c’est très difficile et plus céphalique qu’il ne vaut la peine. 

Je gère généralement cette situation en créant un script shell d'amorçage simple. Cela crée des choses comme:

  1. Le compartiment s3 pour le stockage d'état 
  2. Ajoute le versioning à ce compartiment
  3. un utilisateur et un groupe IRA terraform avec certaines règles dont j'ai besoin pour les constructions terraform

Bien que vous ne deviez avoir à exécuter cette opération qu'une seule fois (techniquement), je constate que lorsque je développe un nouveau système, je tourne et arrache les choses à plusieurs reprises. Donc, avoir ces étapes dans un script rend cela beaucoup plus simple. 

Je construis généralement le script pour être idempotent. De cette façon, vous pouvez l'exécuter plusieurs fois sans craindre de créer des compartiments en double, des utilisateurs, etc.

8
djt

S'appuyant sur la contribution importante d'Austin Davis, voici une variante que j'utilise et qui inclut une exigence pour le cryptage des données:

provider "aws" {
  region = "us-east-1"
}

resource "aws_s3_bucket" "terraform_state" {
  bucket = "tfstate"

  versioning {
    enabled = true
  }

  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_dynamodb_table" "terraform_state_lock" {
  name           = "app-state"
  read_capacity  = 1
  write_capacity = 1
  hash_key       = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}

resource "aws_s3_bucket_policy" "terraform_state" {
  bucket = "${aws_s3_bucket.terraform_state.id}"
  policy =<<EOF
{
  "Version": "2012-10-17",
  "Id": "RequireEncryption",
   "Statement": [
    {
      "Sid": "RequireEncryptedTransport",
      "Effect": "Deny",
      "Action": ["s3:*"],
      "Resource": ["arn:aws:s3:::${aws_s3_bucket.terraform_state.bucket}/*"],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      },
      "Principal": "*"
    },
    {
      "Sid": "RequireEncryptedStorage",
      "Effect": "Deny",
      "Action": ["s3:PutObject"],
      "Resource": ["arn:aws:s3:::${aws_s3_bucket.terraform_state.bucket}/*"],
      "Condition": {
        "StringNotEquals": {
          "s3:x-amz-server-side-encryption": "AES256"
        }
      },
      "Principal": "*"
    }
  ]
}
EOF
}
5
Matt Lavin

J'ai créé un module terraform avec quelques commandes/instructions d'amorçage pour résoudre ceci:

https://github.com/samstav/terraform-aws-backend

Le README contient des instructions détaillées, mais le résumé est le suivant:

# conf.tf

module "backend" {
  source         = "github.com/samstav/terraform-aws-backend"
  backend_bucket = "terraform-state-bucket"
}

Ensuite, dans votre shell (assurez-vous de ne pas encore avoir écrit votre bloc terraform {}):

terraform get -update
terraform init -backend=false
terraform plan -out=backend.plan -target=module.backend
terraform apply backend.plan

Maintenant, écrivez votre bloc terraform {}:

# conf.tf

terraform {
  backend "s3" {
    bucket         = "terraform-state-bucket"
    key            = "states/terraform.tfstate"
    dynamodb_table = "terraform-lock"
  }
}

Et puis vous pouvez ré-initier:

terraform init -reconfigure
0
samstav