generated from CDCgov/template
-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
devsecops/emvaldes/terraform-stats (#17024)
* Importing JosiahSiegel/terraform-stats default state. * Appending default configuration changes. * Purging the custom entries made in this ignore-file. * Purging build-status badge from README.md * Cleaning up the README.md file. * Removing the Code-of-Conduct file. --------- Co-authored-by: bethbeza <[email protected]>
- Loading branch information
Showing
6 changed files
with
341 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
terraform/*.git | ||
.github |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2022 Josiah Siegel | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
# Terraform Stats | ||
## Synopsis | ||
|
||
Output the following statistics for the Terraform environment: | ||
1. Terraform version | ||
2. Drift count | ||
* "Drift" refers to changes made outside of Terraform and does not necessary match any resources listed for changes. | ||
3. Resource drifts | ||
4. Change count | ||
* "Change" refers to change actions that Terraform plans to use to move from the prior state to a new state. | ||
5. Change percent | ||
* Percentage of changes to total resources. | ||
6. Resource changes | ||
|
||
## Usage | ||
|
||
```yml | ||
- name: Terraform stats | ||
uses: josiahsiegel/terraform-stats@<latest-version> | ||
id: stats | ||
with: | ||
terraform-directory: ${{ env.tf-dir }} | ||
terraform-version: 1.1.9 | ||
- name: Get outputs | ||
run: | | ||
echo "terraform-version: ${{ steps.stats.outputs.terraform-version }}" | ||
echo "drift-count: ${{ steps.stats.outputs.drift-count }}" | ||
echo "resource-drifts: ${{ steps.stats.outputs.resource-drifts }}" | ||
echo "change-count: ${{ steps.stats.outputs.change-count }}" | ||
echo "change-percent: ${{ steps.stats.outputs.change-percent }}" | ||
echo "resource-changes: ${{ steps.stats.outputs.resource-changes }}" | ||
``` | ||
## Workflow summary | ||
### :construction: Terraform Stats :construction: | ||
* change-count: 2 | ||
* change-percent: 100 | ||
* resource-changes: | ||
```json | ||
[ | ||
{ | ||
"address": "docker_container.nginx", | ||
"changes": [ | ||
"create" | ||
] | ||
}, | ||
{ | ||
"address": "docker_image.nginx", | ||
"changes": [ | ||
"create" | ||
] | ||
} | ||
] | ||
``` | ||
|
||
## Inputs | ||
|
||
```yml | ||
inputs: | ||
terraform-directory: | ||
description: Terraform commands will run in this location. | ||
required: true | ||
default: "./terraform" | ||
include-no-op: | ||
description: "\"no-op\" refers to the before and after Terraform changes are identical as a value will only be known after apply." | ||
required: true | ||
default: false | ||
add-args: | ||
description: Pass additional arguments to Terraform plan. | ||
required: true | ||
default: "" | ||
upload-plan: | ||
description: Upload plan file. true or false | ||
required: true | ||
default: false | ||
upload-retention-days: | ||
description: Number of days to keep uploaded plan. | ||
required: true | ||
default: 7 | ||
plan-file: | ||
description: Name of plan file. | ||
required: true | ||
default: tf__stats__plan.bin | ||
terraform-version: | ||
description: Specify a specific version of Terraform | ||
required: true | ||
default: latest | ||
``` | ||
## Outputs | ||
```yml | ||
outputs: | ||
terraform-version: | ||
description: 'Terraform version' | ||
drift-count: | ||
description: 'Count of drifts' | ||
resource-drifts: | ||
description: 'JSON output of resource drifts' | ||
change-count: | ||
description: 'Count of changes' | ||
change-percent: | ||
description: 'Percentage of changes to total resources' | ||
resource-changes: | ||
description: 'JSON output of resource changes' | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
# action.yml | ||
name: 'Generate Terraform statistics' | ||
description: 'Output Terraform stats for drift and pending changes' | ||
branding: | ||
icon: 'bar-chart' | ||
color: 'purple' | ||
inputs: | ||
terraform-directory: | ||
description: Terraform commands will run in this location. | ||
required: true | ||
default: "./terraform" | ||
include-no-op: | ||
description: "\"no-op\" refers to the before and after Terraform changes are identical as a value will only be known after apply." | ||
required: true | ||
default: false | ||
add-args: | ||
description: Pass additional arguments to Terraform plan. | ||
required: true | ||
default: "" | ||
upload-plan: | ||
description: Upload plan file. true or false | ||
required: true | ||
default: false | ||
upload-retention-days: | ||
description: Number of days to keep uploaded plan. | ||
required: true | ||
default: 7 | ||
plan-file: | ||
description: Name of plan file. | ||
required: true | ||
default: tf__stats__plan.bin | ||
terraform-version: | ||
description: Specify a specific version of Terraform | ||
required: true | ||
default: latest | ||
|
||
outputs: | ||
terraform-version: | ||
description: 'Terraform version' | ||
value: ${{ steps.local-action.outputs.terraform-version }} | ||
drift-count: | ||
description: 'Count of drifts' | ||
value: ${{ steps.local-action.outputs.drift-count }} | ||
resource-drifts: | ||
description: 'JSON output of resource drifts' | ||
value: ${{ steps.local-action.outputs.resource-drifts }} | ||
change-count: | ||
description: 'Count of changes' | ||
value: ${{ steps.local-action.outputs.change-count }} | ||
resource-changes: | ||
description: 'JSON output of resource changes' | ||
value: ${{ steps.local-action.outputs.resource-changes }} | ||
change-percent: | ||
description: 'Percentage of changes to total resources' | ||
value: ${{ steps.local-action.outputs.change-percent }} | ||
|
||
runs: | ||
using: "composite" | ||
steps: | ||
- name: Use specific version of Terraform | ||
uses: hashicorp/setup-terraform@v3 | ||
with: | ||
terraform_version: ${{ inputs.terraform-version }} | ||
terraform_wrapper: false | ||
- name: Run Terraform stats | ||
id: local-action | ||
run: | | ||
${{ github.action_path }}/lib/tf_stats.sh \ | ||
"${{ inputs.terraform-directory }}" \ | ||
${{ inputs.include-no-op }} \ | ||
"${{ inputs.add-args }}" \ | ||
"${{ inputs.plan-file }}" | ||
shell: bash | ||
|
||
- name: Upload Artifact | ||
if: inputs.upload-plan == 'true' | ||
uses: actions/[email protected] | ||
with: | ||
name: ${{ inputs.plan-file }} | ||
path: "${{ inputs.terraform-directory }}/${{ inputs.plan-file }}" | ||
retention-days: ${{ inputs.upload-retention-days }} | ||
|
||
- name: Create summary | ||
if: | | ||
steps.local-action.outputs.change-count > 0 || | ||
steps.local-action.outputs.drift-count > 0 | ||
run: | | ||
echo "### :construction: Terraform Stats :construction:" >> $GITHUB_STEP_SUMMARY | ||
if [[ ${{ steps.local-action.outputs.change-count }} > 0 ]]; then | ||
resource_changes=$(echo "${{ steps.local-action.outputs.resource-changes }}" | jq .) | ||
echo " | ||
* change-count: ${{ steps.local-action.outputs.change-count }} | ||
* change-percent: ${{ steps.local-action.outputs.change-percent }} | ||
* resource-changes: | ||
\`\`\`json | ||
$resource_changes | ||
\`\`\`" >> $GITHUB_STEP_SUMMARY | ||
fi | ||
if [[ ${{ steps.local-action.outputs.drift-count }} > 0 ]]; then | ||
resource_drifts=$(echo "${{ steps.local-action.outputs.resource-drifts }}" | jq .) | ||
echo " | ||
* drift-count: ${{ steps.local-action.outputs.drift-count }} | ||
* resource-drift: | ||
\`\`\`json | ||
$resource_drifts | ||
\`\`\`" >> $GITHUB_STEP_SUMMARY | ||
fi | ||
shell: bash |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#!/bin/bash | ||
|
||
tf_dir=$1 | ||
#For ["no-op"], the before and | ||
#after values are identical. The "after" value will be incomplete if there | ||
#are values within it that won't be known until after apply. | ||
include_no_op=$2 | ||
add_args=$3 | ||
plan_file=$4 | ||
|
||
# Define a function to run terraform plan with common arguments | ||
tf_plan() { | ||
terraform -chdir=$tf_dir plan $add_args -input=false -no-color -lock-timeout=120s -out=$plan_file "$@" | ||
} | ||
|
||
# Try to run terraform plan and init if needed | ||
if ! tf_plan &>/dev/null; then | ||
terraform -chdir=$tf_dir init >/dev/null || exit 1 | ||
tf_plan >/dev/null || exit 1 | ||
fi | ||
|
||
# Get the plan output in text and json formats | ||
PLAN_TXT=$( terraform -chdir=$tf_dir show -no-color $plan_file ) | ||
PLAN_JSON=$( terraform -chdir=$tf_dir show -no-color -json $plan_file ) | ||
|
||
# Define a function to parse the plan json with jq | ||
parse_plan_json() { | ||
echo $PLAN_JSON | jq "$@" | ||
} | ||
|
||
# Define a function to make output friendly | ||
make_output_friendly() { | ||
local output=$1 | ||
output="${output//'%'/'%25'}" | ||
output="${output//$'\n'/'%0A'}" | ||
output="${output//$'\r'/'%0D'}" | ||
output="${output//'"'/'\"'}" | ||
output="${output//'\\"'/'\\\"'}" | ||
echo $output | ||
} | ||
|
||
# Define a function to write the output to the github output file | ||
write_output() { | ||
local key=$1 | ||
local value=$2 | ||
echo "$key=$(make_output_friendly $value)" >> $GITHUB_OUTPUT | ||
} | ||
|
||
# Get the terraform version from the plan json | ||
VERSION=$(parse_plan_json .terraform_version) | ||
|
||
# Get the resource drift from the plan json | ||
DRIFT=$(parse_plan_json .resource_drift) | ||
DRIFT_COUNT=$(echo $DRIFT | jq length) | ||
DRIFTED_RESOURCES=$(echo $DRIFT | jq -c '[.[] | {address: .address, changes: .change.actions}]') | ||
|
||
# Get the resource changes from the plan json | ||
CHANGES=$(parse_plan_json .resource_changes) | ||
if [[ $include_no_op = true ]]; then | ||
CHANGES_FILTERED=$CHANGES | ||
else | ||
CHANGES_FILTERED=$(echo $CHANGES | jq -c '[.[] | {address: .address, changes: .change.actions} | select( .changes[] != "no-op")]') | ||
fi | ||
CHANGE_COUNT=$(echo $CHANGES_FILTERED | jq length) | ||
|
||
# Get the total resources and percent changed from the plan json | ||
TOTAL_RESOURCES=$(parse_plan_json .planned_values.root_module) | ||
TOTAL_ROOT=$(echo $TOTAL_RESOURCES | jq -c .resources | jq length) | ||
TOTAL_CHILD=$(echo $TOTAL_RESOURCES | jq -c .child_modules | jq -c '[.[]?.resources | length] | add') | ||
TOTAL_COUNT=$(( TOTAL_ROOT + TOTAL_CHILD )) | ||
CHANGE_PERC=$(echo "scale=0 ; $CHANGE_COUNT / $TOTAL_COUNT * 100" | bc) | ||
|
||
# Write the output to the github output file | ||
write_output "terraform-version" "$VERSION" | ||
write_output "change-percent" "$CHANGE_PERC" | ||
write_output "drift-count" "$DRIFT_COUNT" | ||
write_output "change-count" "$CHANGE_COUNT" | ||
write_output "resource-drifts" "$DRIFTED_RESOURCES" | ||
write_output "resource-changes" "$CHANGES_FILTERED" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
terraform { | ||
required_providers { | ||
docker = { | ||
source = "kreuzwerker/docker" | ||
version = "~> 2.13.0" | ||
} | ||
} | ||
} | ||
|
||
provider "docker" {} | ||
|
||
resource "docker_image" "nginx" { | ||
name = "nginx:latest" | ||
keep_locally = false | ||
} | ||
|
||
resource "docker_container" "nginx" { | ||
image = docker_image.nginx.latest | ||
name = "tutorial" | ||
ports { | ||
internal = 80 | ||
external = 8000 | ||
} | ||
} |