Answers: <p>The command being run by the <code>external</code> provider here is equivalent to the following:</p>
  
  <pre><code>bash "export ENC_HOOK_URL=$(aws --profile ${AWS_PROFILE} \
  kms encrypt --key-id ${KMS_ALIAS} --plaintext ${HOOK_URL} --output json \
  --region ${AWS_REGION})"
  </code></pre>
  
  <p>Bash is failing here because when used in this way it expects its first argument to be the filename of a script to run.</p>
  
  <p>The <code>-c</code> option changes this interpretation so that it will instead expect this argument to be an <em>inline script</em> to run, which seems to be what you intended here. This could be expressed in the Terraform configuration like this:</p>
  
  <pre><code>data "external" "encrypt_url" {  
    program = ["bash", "-c", "export ENC_HOOK_URL=$(aws --profile $${AWS_PROFILE} kms encrypt --key-id $${KMS_ALIAS} --plaintext $${HOOK_URL} --output json --region $${AWS_REGION})"]
  }
  </code></pre>
  
  <p>Along with adding the <code>-c</code> option, it also seems like this script doesn't actually do anything, instead just assigning a result to a variable. Given that you asked the AWS command to produce JSON, I think perhaps the following will do what you want:</p>
  
  <pre><code>data "external" "encrypt_url" {  
    program = ["bash", "-c", "aws --profile $${AWS_PROFILE} kms encrypt --key-id $${KMS_ALIAS} --plaintext $${HOOK_URL} --output json --region $${AWS_REGION}"]
  }
  </code></pre>
  
  <p>In the interests of readability though, I'd recommend moving this command into an external script file (e.g. <code>encrypt_url.sh</code>) and then referencing it from the config:</p>
  
  <pre><code>#!/usr/bin/env bash
  
  aws --profile ${AWS_PROFILE} \
      kms encrypt --key-id ${KMS_ALIAS} \
                  --plaintext ${HOOK_URL} \
                  --output json \
                  --region ${AWS_REGION}
  </code></pre>
  
  <p>...</p>
  
  <pre><code>data "external" "encrypt_url" {  
    program = ["bash", "${path.module}/encrypt_url.sh"]
  }
  </code></pre>
  
  <p>This avoids the need for escaping the dollar signs, allows you to edit it in your text editor's shell script highlighting mode, and allows the script to be easily run outside of Terraform for testing purposes.</p>
  
Answers: CC BY-SA 3.0
Answers: 2017-08-08T21:19:32.067Z
Answers: 2017-08-08T21:19:32.067Z
Answers: Martin Atkins
Body: <p>so my issue here is that terraform really does not like a command I'm trying to run using an external datasource.</p>
  
  <p>I'm trying to hack my way around a problem where terraform wont wait for a KMS encrypt operation to complete before attempting to use the ciphertext returned by KMS. I've also raised an issue in the terraform for this.</p>
  
  <p>So here is what I'm attempting to do:</p>
  
  <pre><code>resource "aws_kms_alias" "kms_alias" {
      name          = "alias/${var.lambda_name}"
      target_key_id = "${aws_kms_key.hook_url.arn}"
  }
  
  data "external" "encrypt_url" {  
      program = ["bash", "export ENC_HOOK_URL=$(aws --profile $${AWS_PROFILE} \
      kms encrypt --key-id $${KMS_ALIAS} --plaintext $${HOOK_URL} --output json \
      --region $${AWS_REGION})"]
  }
  
  resource "aws_lambda_function" "lambda_function" {
      depends_on       = ["data.external.encrypt_url"]
      filename         = "deploymentPackage.zip"
      function_name    = "${var.lambda_name}"
      role             = "${var.lambda_role_arn}"
      handler          = "index.lambda_handler"
      source_code_hash = "${base64sha256(file("deploymentPackage.zip"))}"
      runtime          = "python3.6"
      publish          = "true"
  
      environment {
          variables = {
              HOOK_URL = "${data.external.encrypt_url.result.ciphertextBlob}"
          }
      }
  
      tags {
          LastUpdated = "${var.timestamp}-${var.iamuser}-Terraform"
      }
  }
  </code></pre>
  
  <p>This will run fine without the <code>encrypt_url</code> datasource but will fail with the following error when trying to run:</p>
  
  <pre><code>data.external.encrypt_url: data.external.encrypt_url: failed to execute "bash":
  bash: export ENC_HOOK_URL=$(aws --profile ${AWS_PROFILE} kms encrypt --key-id ${KMS_ALIAS} --plaintext ${HOOK_URL} --output json --region ${AWS_REGION}): 
  No such file or directory
  </code></pre>
  
  <p>Now no file or directory is attempting to be called here and I'm unable to find much information about this because this function is not well documented.</p>
  
  <p>If anyone has any pointers that'd be great!</p>
  
Tags: <amazon-web-services><terraform><amazon-kms>
Title: Why does the datasource "external" always fail?
UserDisplayName: Briansbum
ThreadText: # Why does the datasource "external" always fail?
  
  (Asked by: username_0 on 2017-08-08)
  
  so my issue here is that terraform really does not like a command I'm trying to run using an external datasource.
  
  I'm trying to hack my way around a problem where terraform wont wait for a KMS encrypt operation to complete before attempting to use the ciphertext returned by KMS. I've also raised an issue in the terraform for this.
  
  So here is what I'm attempting to do:
  
  ```
  resource "aws_kms_alias" "kms_alias" {
      name          = "alias/${var.lambda_name}"
      target_key_id = "${aws_kms_key.hook_url.arn}"
  }
  
  data "external" "encrypt_url" {  
      program = ["bash", "export ENC_HOOK_URL=$(aws --profile $${AWS_PROFILE} \
      kms encrypt --key-id $${KMS_ALIAS} --plaintext $${HOOK_URL} --output json \
      --region $${AWS_REGION})"]
  }
  
  resource "aws_lambda_function" "lambda_function" {
      depends_on       = ["data.external.encrypt_url"]
      filename         = "deploymentPackage.zip"
      function_name    = "${var.lambda_name}"
      role             = "${var.lambda_role_arn}"
      handler          = "index.lambda_handler"
      source_code_hash = "${base64sha256(file("deploymentPackage.zip"))}"
      runtime          = "python3.6"
      publish          = "true"
  
      environment {
          variables = {
              HOOK_URL = "${data.external.encrypt_url.result.ciphertextBlob}"
          }
      }
  
      tags {
          LastUpdated = "${var.timestamp}-${var.iamuser}-Terraform"
      }
  }
  
  ```
  
  This will run fine without the `encrypt_url` datasource but will fail with the following error when trying to run:
  
  ```
  data.external.encrypt_url: data.external.encrypt_url: failed to execute "bash":
  bash: export ENC_HOOK_URL=$(aws --profile ${AWS_PROFILE} kms encrypt --key-id ${KMS_ALIAS} --plaintext ${HOOK_URL} --output json --region ${AWS_REGION}): 
  No such file or directory
  
  ```
  
  Now no file or directory is attempting to be called here and I'm unable to find much information about this because this function is not well documented.
  
  If anyone has any pointers that'd be great!
  
  **Tags:** amazon-web-services, terraform, amazon-kms
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2017-08-08. Score: 2 | Accepted answer
  
  The command being run by the `external` provider here is equivalent to the following:
  
  ```
  bash "export ENC_HOOK_URL=$(aws --profile ${AWS_PROFILE} \
  kms encrypt --key-id ${KMS_ALIAS} --plaintext ${HOOK_URL} --output json \
  --region ${AWS_REGION})"
  
  ```
  
  Bash is failing here because when used in this way it expects its first argument to be the filename of a script to run.
  
  The `-c` option changes this interpretation so that it will instead expect this argument to be an *inline script* to run, which seems to be what you intended here. This could be expressed in the Terraform configuration like this:
  
  ```
  data "external" "encrypt_url" {  
    program = ["bash", "-c", "export ENC_HOOK_URL=$(aws --profile $${AWS_PROFILE} kms encrypt --key-id $${KMS_ALIAS} --plaintext $${HOOK_URL} --output json --region $${AWS_REGION})"]
  }
  
  ```
  
  Along with adding the `-c` option, it also seems like this script doesn't actually do anything, instead just assigning a result to a variable. Given that you asked the AWS command to produce JSON, I think perhaps the following will do what you want:
  
  ```
  data "external" "encrypt_url" {  
    program = ["bash", "-c", "aws --profile $${AWS_PROFILE} kms encrypt --key-id $${KMS_ALIAS} --plaintext $${HOOK_URL} --output json --region $${AWS_REGION}"]
  }
  
  ```
  
  In the interests of readability though, I'd recommend moving this command into an external script file (e.g. `encrypt_url.sh`) and then referencing it from the config:
  
  ```
  #!/usr/bin/env bash
  
  aws --profile ${AWS_PROFILE} \
      kms encrypt --key-id ${KMS_ALIAS} \
                  --plaintext ${HOOK_URL} \
                  --output json \
                  --region ${AWS_REGION}
  
  ```
  
  ...
  
  ```
  data "external" "encrypt_url" {  
    program = ["bash", "${path.module}/encrypt_url.sh"]
  }
  
  ```
  
  This avoids the need for escaping the dollar signs, allows you to edit it in your text editor's shell script highlighting mode, and allows the script to be easily run outside of Terraform for testing purposes.
  
  
  ---
  
  