Первоначальная настройка терраритового бэкэнда с использованием терраформы

Я только начинаю работать с terraform и хотел бы использовать AWS S3 в качестве бэкэнда для хранения состояния моих проектов.

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

Я чувствую, что имеет смысл настроить мою корзину S3, группы IAM и политики для инфраструктуры внутреннего хранения с помощью terraform.

Если я настраиваю свое состояние бэкэнда до того, как применяю свою начальную инфраструктуру terraform, он обоснованно жалуется, что бэкэнд-контейнер еще не создан. Итак, у меня возникает вопрос, как мне настроить мой внутренний сервер terraform с помощью terraform, сохраняя при этом свое состояние для внутреннего сервера, отслеживаемого terraform. Похоже, проблема с матрешками.

У меня есть некоторые мысли о том, как написать сценарий вокруг этого, например, проверить, существует ли корзина или какое-то состояние, затем загрузить terraform и, наконец, скопировать terraform tfstate до s3 из локальной файловой системы после первого запуска. Но прежде чем идти по этому трудоемкому пути, я подумал, что должен убедиться, что не упустил что-то очевидное

Ответ 1

Чтобы настроить это с помощью удаленного состояния terraform, у меня обычно есть отдельная папка с именем remote-state в моей папке dev и prod terraform.

Следующий файл main.tf настроит ваше удаленное состояние для того, что вы опубликовали:

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"
  }
}

Затем перейдите в эту папку, используя cd remote-state, и запустите terraform init && terraform apply - это нужно выполнить только один раз. Вы можете добавить что-то в имя таблицы bucket и DynamodBB, чтобы разделить вашу среду.

Ответ 2

Как вы обнаружили, вы не можете использовать terraform для того, чтобы в первую очередь создавать потребности терраформирования компонентов.

Хотя я понимаю склонность к тому, чтобы terraform "отслеживал все", это очень сложно, и больше головной боли, чем того стоит.

Обычно я обрабатываю эту ситуацию, создавая простую оболочку бутстрапа script. Он создает такие вещи, как:

  • Ведро s3 для хранения состояния
  • Добавляет управление версиями в указанное ведро
  • пользователь и группа Ira terraform с определенными политиками, которые мне понадобятся для построений терраформ.

В то время как вам нужно всего лишь запустить этот один раз (технически), я нахожу, что, когда я разрабатываю новую систему, я разворачиваю и разрываю вещи несколько раз. Таким образом, выполнение этих шагов в одном script делает это намного проще.

Я обычно строю script как идемпотент. Таким образом, вы можете запускать его несколько раз, не опасаясь, что вы создаете дубликаты ведра, пользователей и т.д.

Ответ 3

Опираясь на большой вклад Остина Дэвиса, я использую вариант, включающий требование шифрования данных:

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
}

Ответ 4

Обычно я начинаю без удаленного бэкэнда для создания начальной инфраструктуры, как вы сказали, S3, роли IAM и другие важные вещи. После этого я просто добавляю конфигурацию сервера и запускаю terraform init для перехода на S3.

Это не лучший случай, но в большинстве случаев я не перестраиваю всю свою среду каждый день, поэтому этот полуавтоматический подход достаточно хорош. Я также разделяю следующие "слои" инфраструктуры (VPC, Subnets, IGW, NAT и т.д.) На различные состояния.

Ответ 5

Я создал модуль terraform с несколькими командами/инструкциями начальной загрузки, чтобы решить эту проблему:

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

В README есть подробные инструкции, но суть такова:

# conf.tf

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

Затем в вашей оболочке (убедитесь, что вы еще не написали свой блок terraform {}):

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

Теперь напишите свой блок terraform {}:

# conf.tf

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

И тогда вы можете повторно инициировать:

terraform init -reconfigure

Ответ 6

Я решил эту проблему путем создания удаленного состояния проекта в первом цикле применения плана инициализации и инициализации удаленного состояния во втором цикле применения плана инициализации.


# first init plan apply cycle 
# Configure the AWS Provider
# https://www.terraform.io/docs/providers/aws/index.html
provider "aws" {
  version = "~> 2.0"
  region  = "us-east-1"
}

resource "aws_s3_bucket" "terraform_remote_state" {
  bucket = "terraform-remote-state"
  acl    = "private"

  tags = {
    Name        = "terraform-remote-state"
    Environment = "Dev"
  }
}

# add this sniped and execute the 
# the second init plan apply cycle
# https://www.terraform.io/docs/backends/types/s3.html

terraform {
  backend "s3" {
    bucket = "terraform-remote-state"
    key    = "path/to/my/key"
    region = "us-east-1"
  }
}

Ответ 7

Предполагая, что вы запускаете terraform локально, а не на каком-либо виртуальном сервере, и что вы хотите сохранить состояние terraform в сегменте S3, который не существует. Вот как я бы подошел к этому,

Создать скрипт Terraform, который обеспечивает S3 Bucket

Создайте скрипт terraform, который обеспечивает вашу инфраструктуру

В конце вашего скрипта terraform для предоставления корзины, которая будет использоваться вторым скриптом terraform для хранения файлов состояния, включите код для предоставления нулевого ресурса.

В блоке кода для нулевого ресурса с помощью команды запуска local-exec provisioner перейдите в каталог, в котором существует ваш второй скрипт terraform, за которым следует обычная инициализация terraform для инициализации бэкэнда, затем план terraform, затем применение terraform