Extract change of sensitive resource in Terraform
In terraform 0.14+, it won't be possible to see the changes in sensitives types of arguments while using the plan and apply commands, so we have to find another solution.
In terraform 0.14+
, it won't be possible to see the changes in sensitives types of arguments while using the plan
and apply
commands. What you can do is export the result of the plan in a json
format:
$ terraform plan -out=tfplan
$ terraform show -json tfplan > tfplan.json
$ jq '.' tfplan.json > tfplan-hr.json
Then use jq
to extract the sensitive values you are looking for. In this exemple we will take a kubernetes_secret
ressource:
$ jq '.resource_changes[] | select(.address == "module.adress.kubernetes_secret.my-secret") | .change["before"] | {data}' tfplan-hr.json
This command will allow you to recover the state of the data
argument before the wanted change by targeting the before
key located in the ressource_changes
bloc of the appropriate ressource. It becomes then easy to do the same for the after
key, which correspond to the result of the change wanted:
$ jq '.resource_changes[] | select(.address == "module.adress.kubernetes_secret.my-secret") | .change["after"] | {data}' tfplan-hr.json
Both commands should look something similar (depending on your secret data):
{
"data": {
"my-super-secret.json": "bar"
}
}
Now let's make a script that will check the differences only for the kubernetes_secrets
ressource type and return them. The final result will be at the end of the post, right now i will just explain how i did things.
First we will need a to find all the secrets that will be changed during this terraform plan
run, for this we will still use jq
as follows (we will also have to use sed
to remove all quotes generated by jq
:
$ jq '.resource_changes[] | select(.address | contains("kubernetes_secret")) | .address' tfplan-hr.json | sed 's/\\"//g'
The result should be as follow for as many secret you will be modifying:
module.module_one.kubernetes_secret.secret_one
module.module_two.kubernetes_secret.secret_two
kubernetes_secret.another_secret
...
Once this is done, we will use this command as the base of a for
loop:
#!/bin/bash
terraform init
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
# nice to have in case of debug
jq '.' tfplan.json > tfplan-hr.json
for resource in $(jq '.resource_changes[] | select(.address | contains("kubernetes_secret")) | .address' tfplan-hr.json | sed 's/\\"//g'); do
done
In this loop, we will cycle through every secrets present in the tfplan-hr.json
file that we created before using the resource
variable. It is now time to use the previous commands that will show the before
and after
keys of the selected ressource.
We will have to modify the jq
command in order to select the address of the ressource we want to target by using the --arg
flag. This will allow us a variable inside the jq
expression we used to look for the kubernetes_secret
resources by specifying a pair of key/values that will be used inside this expression. The result will be stored in two variables BEFORE
and AFTER
:
#!/bin/bash
terraform init
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
# nice to have in case of debug
jq '.' tfplan.json > tfplan-hr.json
for resource in $(jq '.resource_changes[] | select(.address | contains("kubernetes_secret")) | .address' tfplan-hr.json | sed 's/\\"//g'); do
BEFORE=$(jq --arg secret_address $resource '.resource_changes[] | select(.address == $secret_address) | .change["before"] | {data}' tfplan-hr.json)
AFTER=$(jq --arg secret_address $resource '.resource_changes[] | select(.address == $secret_address) | .change["after"] | {data}' tfplan-hr.json)
done
This will then allow us to add an conditional if
bloc to output if there are changes or not. For the condition, we will compare the content of both variables in order to start our bloc, then if the content is the same, output a string to let the user know, otherwise let the user know the difference between the two data blocs:
#!/bin/bash
terraform init
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
# nice to have in case of debug
jq '.' tfplan.json > tfplan-hr.json
for resource in $(jq '.resource_changes[] | select(.address | contains("kubernetes_secret")) | .address' tfplan-hr.json | sed 's/\\"//g'); do
BEFORE=$(jq --arg secret_address $resource '.resource_changes[] | select(.address == $secret_address) | .change["before"] | {data}' tfplan-hr.json)
AFTER=$(jq --arg secret_address $resource '.resource_changes[] | select(.address == $secret_address) | .change["after"] | {data}' tfplan-hr.json)
if [ "$BEFORE" == "$AFTER" ]; then
echo "No changes on Data argument for resource $resource"
else
echo "####################################################################"
echo " Changes detected for secret data in ressource "
echo " $resource"
echo "####################################################################"
diff <(echo "$BEFORE") <(echo "$AFTER")
fi
done
This should send you the following output:
No changes on Data argument for resource module.module_one.kubernetes_secret.secret_one
No changes on Data argument for resource module.module_two.kubernetes_secret.secret_two
No changes on Data argument for resource kubernetes_secret.another_secret
####################################################################
Changes detected for secret data in ressource
kubernetes_secret.another_secret
####################################################################
3c3
< "secret.json": "foo"
---
> "secret.json": "bar"
We will now wrap things up by removing the files we generated in the script:
#!/bin/bash
terraform init
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
# nice to have in case of debug
jq '.' tfplan.json > tfplan-hr.json
for resource in $(jq '.resource_changes[] | select(.address | contains("kubernetes_secret")) | .address' tfplan-hr.json | sed 's/\\"//g'); do
BEFORE=$(jq --arg secret_address $resource '.resource_changes[] | select(.address == $secret_address) | .change["before"] | {data}' tfplan-hr.json)
AFTER=$(jq --arg secret_address $resource '.resource_changes[] | select(.address == $secret_address) | .change["after"] | {data}' tfplan-hr.json)
if [ "$BEFORE" == "$AFTER" ]; then
echo "No changes on Data argument for resource $resource"
else
echo "####################################################################"
echo " Changes detected for secret data in ressource "
echo " $resource"
echo "####################################################################"
diff <(echo "$BEFORE") <(echo "$AFTER")
fi
done
rm tfplan
rm tfplan.json
rm tfplan-hr.json
You should be good to go 🙂