├── providers.tf ├── main.tf ├── README.md ├── policies ├── s3-bucket-state.json ├── role-trusted-entity.json └── ssm-permisions.json ├── .gitignore └── github-action.yml /providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" {} 3 | } -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ssm_parameter" "foo" { 2 | name = "foo" 3 | type = "String" 4 | value = "barr" 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # github-oidc-terraform 2 | 3 | You need to set the file github-action.yml inside this directory: 4 | 5 | ```bash 6 | .github/workflows/ 7 | ``` 8 | 9 | Blog post : [https://cloudscalr.com/deploy-to-aws-with-terraform-within-a-github-action/](https://cloudscalr.com/deploy-to-aws-with-terraform-within-a-github-action/) 10 | 11 | Youtube video : [https://www.youtube.com/watch?v=GowFk_5Rx_I](https://www.youtube.com/watch?v=GowFk_5Rx_I) 12 | -------------------------------------------------------------------------------- /policies/s3-bucket-state.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "s3:PutObject", 8 | "s3:GetObject", 9 | "s3:ListBucket" 10 | ], 11 | "Resource": [ 12 | "arn:aws:s3:::YOUR_BUCKET/*", 13 | "arn:aws:s3:::YOUR_BUCKET" 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /policies/role-trusted-entity.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2008-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Federated": "arn:aws:iam::YOUR_ACCOUNT_NUMBER:oidc-provider/token.actions.githubusercontent.com" 8 | }, 9 | "Action": "sts:AssumeRoleWithWebIdentity", 10 | "Condition": { 11 | "StringLike": { 12 | "token.actions.githubusercontent.com:sub": "repo:YOUR_GITHUB_USERNAME/YOUR_REPO_NAME:*" 13 | } 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /policies/ssm-permisions.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "VisualEditor0", 6 | "Effect": "Allow", 7 | "Action": [ 8 | "ssm:PutParameter", 9 | "ssm:LabelParameterVersion", 10 | "ssm:DeleteParameter", 11 | "ssm:UnlabelParameterVersion", 12 | "ssm:DescribeParameters", 13 | "ssm:GetParameterHistory", 14 | "ssm:ListTagsForResource", 15 | "ssm:GetParametersByPath", 16 | "ssm:GetParameters", 17 | "ssm:GetParameter", 18 | "ssm:DeleteParameters" 19 | ], 20 | "Resource": "*" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | 32 | # Ignore CLI configuration files 33 | .terraformrc 34 | terraform.rc -------------------------------------------------------------------------------- /github-action.yml: -------------------------------------------------------------------------------- 1 | name: "Terraform action" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | permissions: 8 | id-token: write # This is required for aws oidc connection 9 | contents: read # This is required for actions/checkout 10 | pull-requests: write # This is required for gh bot to comment PR 11 | env: 12 | TF_LOG: INFO 13 | AWS_REGION: ${{ secrets.AWS_REGION }} 14 | jobs: 15 | deploy: 16 | runs-on: ubuntu-latest 17 | defaults: 18 | run: 19 | shell: bash 20 | working-directory: . 21 | steps: 22 | - name: Git checkout 23 | uses: actions/checkout@v3 24 | 25 | - name: Configure AWS credentials from AWS account 26 | uses: aws-actions/configure-aws-credentials@v1 27 | with: 28 | role-to-assume: ${{ secrets.AWS_ROLE }} 29 | aws-region: ${{ secrets.AWS_REGION }} 30 | role-session-name: GitHub-OIDC-TERRAFORM 31 | 32 | - name: Setup Terraform 33 | uses: hashicorp/setup-terraform@v2 34 | with: 35 | terraform_version: 1.2.5 36 | 37 | - name: Terraform fmt 38 | id: fmt 39 | run: terraform fmt -check 40 | continue-on-error: true 41 | 42 | - name: Terraform Init 43 | id: init 44 | env: 45 | AWS_BUCKET_NAME: ${{ secrets.AWS_BUCKET_NAME }} 46 | AWS_BUCKET_KEY_NAME: ${{ secrets.AWS_BUCKET_KEY_NAME }} 47 | run: terraform init -backend-config="bucket=${AWS_BUCKET_NAME}" -backend-config="key=${AWS_BUCKET_KEY_NAME}" -backend-config="region=${AWS_REGION}" 48 | 49 | - name: Terraform Validate 50 | id: validate 51 | run: terraform validate -no-color 52 | 53 | - name: Terraform Plan 54 | id: plan 55 | run: terraform plan -no-color 56 | if: github.event_name == 'pull_request' 57 | continue-on-error: true 58 | 59 | - uses: actions/github-script@v6 60 | if: github.event_name == 'pull_request' 61 | env: 62 | PLAN: "terraform\n${{ steps.plan.outputs.stdout }}" 63 | with: 64 | github-token: ${{ secrets.GITHUB_TOKEN }} 65 | script: | 66 | const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` 67 | #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` 68 | #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\` 69 |
Validation Output 70 | 71 | \`\`\`\n 72 | ${{ steps.validate.outputs.stdout }} 73 | \`\`\` 74 | 75 |
76 | 77 | #### Terraform Plan 📖\`${{ steps.plan.outcome }}\` 78 | 79 |
Show Plan 80 | 81 | \`\`\`\n 82 | ${process.env.PLAN} 83 | \`\`\` 84 | 85 |
86 | 87 | *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; 88 | 89 | github.rest.issues.createComment({ 90 | issue_number: context.issue.number, 91 | owner: context.repo.owner, 92 | repo: context.repo.repo, 93 | body: output 94 | }) 95 | 96 | - name: Terraform Plan Status 97 | if: steps.plan.outcome == 'failure' 98 | run: exit 1 99 | 100 | - name: Terraform Apply 101 | if: github.ref == 'refs/heads/main' && github.event_name == 'push' 102 | run: terraform apply -auto-approve -input=false 103 | --------------------------------------------------------------------------------