29

I'm using Terraform to automate provision of Cognito Identity Pools in AWS. The AWS provider doesn't support Cognito yet so I've been using null_resource and local-exec to call the AWS CLI.

I have the following resource:

resource "null_resource" "create-identitypool" {
    provisioner "local-exec" {
        command = "aws cognito-identity create-identity-pool --identity-pool-name terraform_identitypool --no-allow-unauthenticated-identities --developer-provider-name login.terraform.myapp"
    }
}

which gives the following output:

null_resource.create-identitypool (local-exec): {
null_resource.create-identitypool (local-exec):     "IdentityPoolId": "eu-west-1:22549ad3-1611-......",
null_resource.create-identitypool (local-exec):     "AllowUnauthenticatedIdentities": false,
null_resource.create-identitypool (local-exec):     "DeveloperProviderName": "login.terraform.myapp",
null_resource.create-identitypool (local-exec):     "IdentityPoolName": "terraform_identitypool"
null_resource.create-identitypool (local-exec): }
null_resource.create-identitypool: Creation complete

The next step is to add some roles, which I've already created, to the identity pool:

resource "null_resource" "attach-policies-identitypool" {
    provisioner "local-exec" {
        command = "aws cognito-identity set-identity-pool-roles --identity-pool-id ${null_resource.create-identitypool.IdentityPoolId} --roles authenticated=authroleXXX,unauthenticated=unauthroleXXX"
    }
}

The issue is that I'm unable to extract the IdentityPoolId, ${null_resource.create-identitypool.IdentityPoolId}, to use in the second resource. I understand the null_resource doesn't have output attributes, so how can I get this JSON object out of the command line output. I'll also want to use tirggers and run aws cognito-identity list-identity-pools and possibly delete-identity-pool to make this all repeatable from which I'll also need the output.

Any ideas? And apologies if I've missed this information somewhere else. I've also asked this question on the Terraform mailing list, but I thought I'd try for a wider audience.

Thanks, Tim

big_tommy_7bb
  • 1,257
  • 2
  • 21
  • 37
  • 2
    I haven't had to do this, so I can't vouch for it, but you could try having the command redirect its output to a file (maybe send it through `sed` to extract the exact value you need), and then use the file resource where you need the output. Then make sure to add the appropriate `depends_on` attributes in resources that need the file to make sure they run after it gets generated. – Karen B Jul 25 '16 at 17:53
  • 1
    Also, if there's not already a feature request in their GItHub repo about defining outputs from a `null_resource`, you might want to open one. I'm sure other people would find it useful, and it has a good chance of making it in a future release. – Karen B Jul 25 '16 at 17:56
  • 1
    @tim_barber_7BB how did you eventually do this? Is anybody onto this? – Christine Aug 19 '16 at 02:51
  • @Christine I didn't solve this. I think this is going to be a manual part of the process (unless someone comes up with something), I'm not going to spend any more time investigating at the moment. – big_tommy_7bb Aug 31 '16 at 08:46

3 Answers3

16

There is a new data source in Terraform 0.8, external that allows you to run external commands and extract output. See data.external.

The data source should only be used for the retrieval of the Cognito data, not the execution of it. Since this is a Terraform data source, it should not have any side effects.

Paul Tyng
  • 7,924
  • 1
  • 33
  • 57
5

Paul's answer is correct. However, external data only works if the shell script sends data back in JSON format , which requires more work.

So, Matti Paksula made a module for this. (https://github.com/matti/terraform-shell-resource).

Using that module , we can get stdout, stderr, and exit status of ANY shell script local-exec calls.

Here is an example main.tf file. You can modify this any way you want to run any command you want including the one in your question.

#  Defining a variable , we will feed to the shell script
variable "location" { default = "us-central1-f" }


# Calling Matti's Module
module "shell_execute" {
  source  = "github.com/matti/terraform-shell-resource"
  command = "./scripts/setenv.sh"
}


# Creating a shell script on the fly
resource "local_file" "setenvvars" {
  filename = "./scripts/setenv.sh"
  content  = <<-EOT
    #!/bin/bash
    export LOCATION=${var.modinput_location}
    echo LOCATION $LOCATION
  EOT
}

#  Now, we get back the output of the script
output "shell_stdout" {
  value = module.shell_execute.stdout
}

#  Now, we get back if there are any errors
output "shell_stderr" {
  value = module.shell_execute.stderr
}

#  Now, we get back exit status of the script
output "shell_exitstatus" {
  value = module.shell_execute.exitstatus
}
Mamun
  • 2,322
  • 4
  • 27
  • 41
  • 2
    shell_execute works for me with v1.0.5 on linux_amd64 – jws Sep 02 '21 at 13:47
  • 2
    Beware of the security implications - the `terraform-shell-resource` module uses temporary files to capture output. – jws Sep 02 '21 at 15:39
5

The external data source can be used for this.

Here's a full example below.


Let's say that you want to run an AWS CLI command to retrieve information about a certain EC2 instance (this is just for demonstration purposes), for instance the AMI ID and the key name.

First of all, create a bash script (get_instance_details.sh) that fetches and returns the required information:

#!/bin/bash
INSTANCE_ID=$1

INSTANCE_DETAILS=$(aws ec2 describe-instances --instance-id $INSTANCE_ID)
AMI_ID=$(echo $INSTANCE_DETAILS | jq -r '.Reservations[].Instances[].ImageId')
KEY_NAME=$(echo $INSTANCE_DETAILS | jq -r '.Reservations[].Instances[].KeyName')

jq -n --arg ami_id "$AMI_ID" --arg key_name "$KEY_NAME" '{"ami_id":$ami_id,"key_name":$key_name}'

then, create a data source that runs the script:

data "external" "get_instance_details" {
  program = ["bash", "get_instance_details.sh","${aws_instance.my_instance.id}"]
}

you can then check the results with terraform console (after terraform apply):

> data.external.get_instance_details.result.ami_id
"ami-..."
> data.external.get_instance_details.result.key_name
"my-ec2-key..."

and obviously you can use those identifiers as properties for other resources.

Paolo
  • 21,270
  • 6
  • 38
  • 69