|
| 1 | +--- |
| 2 | +name: action-rotate-hcp-terraform-user-token |
| 3 | +description: Rotates a HCP Terraform user token stored as a secret in a GitHub repository. |
| 4 | +author: Ben Dwyer (github.com/bendwyer) |
| 5 | + |
| 6 | +inputs: |
| 7 | + github_token: |
| 8 | + description: GitHub token used for writing the HCP Terraform user token to the repository secret store. |
| 9 | + required: false |
| 10 | + default: ${{ github.token }} |
| 11 | + github_secrets_name: |
| 12 | + description: Name of the secret in the repository secret store where the token will be written. |
| 13 | + required: false |
| 14 | + default: HCP_TERRAFORM_USER_TOKEN |
| 15 | + hcp_terraform_user_token: |
| 16 | + description: HCP Terraform user token to be rotated. This token must already exist and be saved as a repository secret before running this action. |
| 17 | + required: true |
| 18 | + hcp_terraform_user_token_description: |
| 19 | + description: Description for the HCP Terraform user token. Must be the same for the original and new tokens. |
| 20 | + required: false |
| 21 | + default: github-token |
| 22 | + hcp_terraform_user_token_expiration: |
| 23 | + description: Time in days when the HCP Terraform user token will expire. |
| 24 | + default: "30" |
| 25 | + |
| 26 | +runs: |
| 27 | + using: composite |
| 28 | + steps: |
| 29 | + - name: Rotate HCP Terraform user token |
| 30 | + env: |
| 31 | + GH_TOKEN: ${{ inputs.github_token }} |
| 32 | + run: | |
| 33 | + echo "Set token" |
| 34 | + TOKEN=${{ inputs.hcp_terraform_user_token }} |
| 35 | + echo "Mask token" |
| 36 | + echo "::add-mask::$TOKEN" |
| 37 | + echo "Set token description" |
| 38 | + TOKEN_DESCRIPTION=${{ inputs.hcp_terraform_user_token_description }} |
| 39 | + echo "Set expiration" |
| 40 | + TOKEN_EXPIRATION=${{ inputs.hcp_terraform_user_token_expiration }} |
| 41 | + SECRETS_NAME=${{ inputs.github_secrets_name }} |
| 42 | + echo "Set user ID" |
| 43 | + ID=$(curl -Ss --fail-with-body --header "Authorization: Bearer $TOKEN" --header "Content-Type: application/vnd.api+json" --request GET https://app.terraform.io/api/v2/account/details | jq -r '.data.id') |
| 44 | + echo "Check if date should be calculated" |
| 45 | + if [[ -n $TOKEN_EXPIRATION ]]; |
| 46 | + then |
| 47 | + echo "Calculate date" |
| 48 | + DATE=$(date -d "+$TOKEN_EXPIRATION days" -u +%Y-%m-%dT%H:%M:%S.%3NZ) |
| 49 | + fi |
| 50 | + echo "Get all token info" |
| 51 | + ALL_TOKENS=$(curl -Ss --fail-with-body --header "Authorization: Bearer $TOKEN" --header "Content-Type: application/vnd.api+json" --request GET https://app.terraform.io/api/v2/users/$ID/authentication-tokens) |
| 52 | + echo "Filter matched token info" |
| 53 | + MATCHED_TOKEN_INFO=$(echo $ALL_TOKENS | jq -r --arg TOKEN_DESCRIPTION "$TOKEN_DESCRIPTION" --arg DATE "$DATE" '[.data[] | {id,attributes} | select(.attributes.description==$TOKEN_DESCRIPTION) | select(.attributes."expired-at"!=$DATE)]') |
| 54 | + echo "Get matched token IDs" |
| 55 | + MATCHED_TOKEN_IDS=$(echo $MATCHED_TOKEN_INFO | jq -c 'map(.id)') |
| 56 | + echo "Get matched token count" |
| 57 | + MATCHED_TOKEN_COUNT=$(echo $MATCHED_TOKEN_INFO | jq -c 'map(.id) | length') |
| 58 | + if [[ $MATCHED_TOKEN_COUNT -ge 2 ]] |
| 59 | + then |
| 60 | + echo "ERROR: $MATCHED_TOKEN_COUNT tokens matched search criteria. Please ensure that 0 or 1 tokens match the search criteria." >> /dev/stderr |
| 61 | + exit 1 |
| 62 | + else |
| 63 | + echo "Matched token IDs: $MATCHED_TOKEN_IDS" |
| 64 | + echo "Matched token count: $MATCHED_TOKEN_COUNT" |
| 65 | + fi |
| 66 | + echo "Create json payload" |
| 67 | + PAYLOAD=$(cat <<EOF |
| 68 | + { |
| 69 | + "data": { |
| 70 | + "type": "authentication-tokens", |
| 71 | + "attributes": { |
| 72 | + "description":"$TOKEN_DESCRIPTION", |
| 73 | + "expired-at": "$DATE" |
| 74 | + } |
| 75 | + } |
| 76 | + } |
| 77 | + EOF |
| 78 | + ) |
| 79 | + echo "Compress payload" |
| 80 | + PAYLOAD_COMPRESSED=$(echo $PAYLOAD | jq -c) |
| 81 | + echo "Check if new user token should be created" |
| 82 | + if [[ $MATCHED_TOKEN_COUNT == 0 || $MATCHED_TOKEN_COUNT == 1 ]] |
| 83 | + then |
| 84 | + echo "Create new token" |
| 85 | + NEW_TOKEN=$(curl -Ss --fail-with-body --header "Authorization: Bearer $TOKEN" --header "Content-Type: application/vnd.api+json" --request POST --data $PAYLOAD_COMPRESSED https://app.terraform.io/api/v2/users/$ID/authentication-tokens | jq -r '.data.attributes.token') |
| 86 | + echo "Mask new token" |
| 87 | + echo "::add-mask::$NEW_TOKEN" |
| 88 | + echo "Write new token to repository secrets" |
| 89 | + gh secret set $SECRETS_NAME --body "$NEW_TOKEN" --app actions |
| 90 | + fi |
| 91 | + echo "Check if old user token should be deleted" |
| 92 | + if [[ $MATCHED_TOKEN_COUNT == 1 ]] |
| 93 | + then |
| 94 | + echo "Get matched token ID" |
| 95 | + MATCHED_TOKEN_ID=$(echo $MATCHED_TOKEN_IDS | jq -r '.[]') |
| 96 | + echo "Delete old user token" |
| 97 | + curl -Ss --fail-with-body --header "Authorization: Bearer $NEW_TOKEN" --header "Content-Type: application/vnd.api+json" --request DELETE "https://app.terraform.io/api/v2/authentication-tokens/$MATCHED_TOKEN_ID" |
| 98 | + fi |
| 99 | + shell: bash |
0 commit comments