Commit 2638eac3 authored by nimrod's avatar nimrod
Browse files

Ansible playbook: project_ci_aws_creds.yaml

An Ansible playbook to create an IAM user for GitLab projects' CI. Also,
rotates the IAM access keys, sets the CI variables for the access key,
attaches an inline policy to limit the user by IP and requested region.
Lastly, create a policy with full access to Resource Groups because I
usually create one for each deployment but there isn't such an AWS
managed policy.
parent b52cae78
Loading
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -61,3 +61,4 @@ reports/
Ansible/*.crt
Ansible/*.csr
Ansible/*.key
Ansible/collections
+1 −0
Original line number Diff line number Diff line
[defaults]
callback_enabled = ansible.posix.profile_tasks, ansible.posix.timer
collections_path = {{CWD}}/collections
deprecation_warnings = True
fact_caching = jsonfile
fact_caching_connection = ~/.ansible/facts
+0 −0

Empty file added.

+208 −0
Original line number Diff line number Diff line
---
- name: Generate AWS credentials for a GitLab project CI
  hosts: localhost
  gather_facts: false
  become: false
  vars_prompt:
    - name: project_full_name
      # yamllint disable-line rule:line-length
      prompt: Full name for the GitLab project (namespace included, eg. group/project)
      private: false
    - name: aws_region
      prompt: List of AWS regions to limit the IAM user (comma separated)
      default: us-east-1
      private: false
  vars:
    aws_username: "{{ project_name }}-ci"
    aws_limit_policy:
      Statement:
        - Sid: LimitIP
          Effect: Deny
          Action: "*"
          Resource: "*"
          Condition:
            ForAnyValue:NotIpAddress:
              aws:SourceIp: '{{ shore_ip_addresses }}'
        - Sid: LimitRegion
          Effect: Deny
          NotAction:
            - cloudfront:*
            - iam:*
            - route53:*
            - support:*
          Resource: "*"
          Condition:
            ForAnyValue:StringNotEqualsIfExists:
              aws:RequestedRegion: '{{ aws_region.split(",") }}'
    aws_resourcegroup_policy:
      Version: 2012-10-17
      Statement:
        - Sid: ResourceGroupFullAccess
          Effect: Allow
          Action: "resource-groups:*"
          Resource: "*"
    gitlab_base_url: "{{ lookup('env', 'GITLAB_BASE_URL') }}"
    gitlab_token: "{{ lookup('env', 'GITLAB_TOKEN') }}"
    project_id: '{{ project_dict["id"] }}'
    project_name: '{{ project_dict["path"] }}'
    shore_ip_addresses:
      - "{{ lookup('community.general.dig', 'ns1.shore.co.il') }}"
      - "{{ lookup('community.general.dig', 'ns4.shore.co.il') }}"
  tasks:
    - name: Search for GitLab projects with the same name
      ansible.builtin.uri:
        headers: &gitlab_headers
          Authorization: Bearer {{ gitlab_token }}
        method: GET
        # yamllint disable-line rule:line-length
        url: "{{ gitlab_base_url }}/search?scope=projects&search={{ project_full_name.split('/')[1] }}"
      register: search_projects

    - name: Find the right GitLab project
      ansible.builtin.set_fact:
        project_dict: >-
          {{
            search_projects.json|
            selectattr("path_with_namespace", "equalto", project_full_name)|
            first
          }}

    - name: Create a full access resource group IAM policy
      community.aws.iam_managed_policy:
        policy: '{{ aws_resourcegroup_policy|to_json }}'
        policy_description: Provides full access to AWS Resource Groups
        policy_name: &rg_policy_name AWSResourceGroupsFullAccess
        state: present

    - name: Create the IAM user
      community.aws.iam_user:
        managed_policies:
          - *rg_policy_name
        name: "{{ aws_username }}"
        purge_policies: false
        state: present
        tags:
          Project: '{{ project_name }}'

    - name: Attach an inline policy to limit the IAM user
      community.aws.iam_policy:
        iam_name: '{{ aws_username }}'
        iam_type: user
        policy_json: '{{ aws_limit_policy|to_json }}'
        policy_name: '{{ aws_username }}-limit'
        skip_duplicates: true
        state: present

    # yamllint disable-line rule:line-length
    - name: Create access key for the IAM user (a new one every time, task will always change)
      community.aws.iam_access_key:
        active: true
        rotate_keys: true
        state: present
        user_name: '{{ aws_username }}'
      register: aws_access_key

    - name: Get the GitLab project CI variables
      ansible.builtin.uri:
        headers: *gitlab_headers
        method: GET
        url: "{{ gitlab_base_url }}/projects/{{ project_id }}/variables"
      changed_when: false
      register: project_vars

    - name: Create the access key ID GitLab CI variable
      # yamllint disable-line rule:line-length
      when: project_vars.json|selectattr("key", "equalto", "AWS_ACCESS_KEY_ID")|length == 0
      ansible.builtin.uri:
        body:
          environment_scope: "*"
          key: AWS_ACCESS_KEY_ID
          masked: false
          protected: false
          value: '{{ aws_access_key.access_key_id }}'
          variable_key: env_var
        body_format: form-urlencoded
        headers: *gitlab_headers
        method: POST
        status_code: 201
        url: "{{ gitlab_base_url }}/projects/{{ project_id }}/variables"

    - name: Update the access key ID GitLab CI variable (will always change)
      # yamllint disable-line rule:line-length
      when: project_vars.json|selectattr("key", "equalto", "AWS_ACCESS_KEY_ID")|length == 1
      changed_when: true
      ansible.builtin.uri:
        body:
          environment_scope: "*"
          masked: false
          protected: false
          value: '{{ aws_access_key.access_key_id }}'
          variable_key: env_var
        body_format: form-urlencoded
        headers: *gitlab_headers
        method: PUT
        status_code: 200
        # yamllint disable-line rule:line-length
        url: "{{ gitlab_base_url }}/projects/{{ project_id }}/variables/AWS_ACCESS_KEY_ID"

    - name: Create the secret access key GitLab CI variable
      # yamllint disable-line rule:line-length
      when: project_vars.json|selectattr("key", "equalto", "AWS_SECRET_ACCESS_KEY")|length == 0
      ansible.builtin.uri:
        body:
          environment_scope: "*"
          key: AWS_SECRET_ACCESS_KEY
          masked: true
          protected: false
          value: '{{ aws_access_key.secret_access_key }}'
          variable_key: env_var
        body_format: form-urlencoded
        headers: *gitlab_headers
        method: POST
        status_code: 201
        url: "{{ gitlab_base_url }}/projects/{{ project_id }}/variables"

    - name: Update the secret access key GitLab CI variable (will always change)
      # yamllint disable-line rule:line-length
      when: project_vars.json|selectattr("key", "equalto", "AWS_SECRET_ACCESS_KEY")|length == 1
      changed_when: true
      ansible.builtin.uri:
        body:
          environment_scope: "*"
          masked: true
          protected: false
          value: '{{ aws_access_key.secret_access_key }}'
          variable_key: env_var
        body_format: form-urlencoded
        headers: *gitlab_headers
        method: PUT
        status_code: 200
        # yamllint disable-line rule:line-length
        url: "{{ gitlab_base_url }}/projects/{{ project_id }}/variables/AWS_SECRET_ACCESS_KEY"

    - name: Find all of the IAM access keys for the user
      community.aws.iam_access_key_info:
        user_name: '{{ aws_username }}'
      register: aws_access_keys

    - name: Remove the old IAM access keys
      when: aws_access_keys.access_keys|length > 1
      community.aws.iam_access_key:
        id: '{{ access_key.access_key_id }}'
        state: absent
        user_name: '{{ aws_username }}'
      vars:
        # yamllint disable rule:line-length
        access_key: >-
          {{
            aws_access_keys.access_keys|
            rejectattr("access_key_id", "equalto", aws_access_key.access_key_id)|
            first
          }}
        # yamllint enable rule:line-length

    - name: AWS console URL for modifying the IAM user
      debug:
        # yamllint disable-line rule:line-length
        msg: https://us-east-1.console.aws.amazon.com/iam/home#/users/{{ aws_username }}
        verbosity: 0
+6 −0
Original line number Diff line number Diff line
---
collections:
  - name: amazon.aws
    version: '>=3.2.0'
  - name: community.aws
    version: '>=3.2.0'