Terraform: Is it ok to "read or create" a resource?
Tags: terraform, aws, bash, discuss
Published: 2020-11-21 12:00:47

I'm very new in Terraform and I suppose that I can bring unsuitable conceptions from programming languages to terraform configs, but I want to tell a little bit about my problem which I've solved in a controversial way, invite you to discuss that solution and, possible, explain that I was wrong.

Recently I needed to deploy a few services in AWS and I used terraform for creating infrastructure. I decided to create a separate terraform config for each application/service I want to deploy in AWS and while I was writing configs it became clear that different services could require the same AWS resource, like a role to access DB, for example. For this case, in order to have the ability to reuse parts of Terraform config as a module, I moved that resource creation into independent configs and started to use it as a module in my services configs.
But very soon I met a problem - when I have the same AWS resources creation statements in different configs, `terraform apply` will fail with the error `Resource already exists`. Ok, we need something like "read or create" for resources in terraform config. In that case, we can reuse modules without errors and have independent service configs, each of them would be able to deploy independently.  While I was searching for a solution for that case, I found that [githib issue](https://github.com/hashicorp/terraform/issues/16380), where the same situation discussed, but Terraform closed this issue with an explanation: 
>The sort of dynamic decision-making that is being requested here runs counter to Terraform's design goals since it makes the configuration a description of what might possibly be rather than what is. We are not going to change the behavior of data sources when the response is empty or 404, and will close this issue.

Ok, we can't make read or create a resource with terraform, but I have found a way which could be controversial and I don't have a definite answer for me is it worth to do terrafrom that way.

The solution is [terraform shell provider](https://github.com/scottwinkler/terraform-provider-shell) which allows to do something in shell and return json to terraform. Thus we can do something like this: 

```
data "shell_script" "check_existing" {
  lifecycle_commands {
    read = <<-EOF
      aws iam get-role --role-name ${var.role_name} > /dev/null 2>&1
      [ $? -eq 0 ] && echo "{\"isExist\": true}" || echo "{\"isExist\": false}"
    EOF
  }
}
```
The script above uses AWS CLI to check if a particular role exists, and return a boolean. This value will be able in other terraform config statements as: `data.shell_script.check_existing.output.isExist`.
And when I read or create some resource I can use a trick with resource count, for example (with configs separated into modules):

```
module "is_role_exist" {
  source = "../isExist"
  role_name = var.role_name
}

module "create_role" {
  source = "../create"
  role_name = var.role_name
  count = module.is_role_exist.is_role_exist ? 0 : 1
}

module "read_role" {
  source = "../read"
  role_name = var.role_name
  count = module.is_role_exist.is_role_exist ? 1 : 0
}

```

Now I have a few modules different applications depends on, with the following structure:

```
module
  - read
  - create
  - isExist
  - readOrCreate
```

It is the working solution for conditional creating resources but it contradicts terraform intention, as far as I understood, probably could be useful, but I will be glad to know other opinions 🙂 (There I ask you to share your opinion)