diff --git a/.github/wiki/assets/images/architecture/diagram-complete.svg b/.github/wiki/assets/images/architecture/diagram-complete.svg index e582dad5..06b104e3 100644 --- a/.github/wiki/assets/images/architecture/diagram-complete.svg +++ b/.github/wiki/assets/images/architecture/diagram-complete.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/.github/workflows/increment-version.yml b/.github/workflows/increment-version.yml new file mode 100644 index 00000000..66b849fd --- /dev/null +++ b/.github/workflows/increment-version.yml @@ -0,0 +1,136 @@ +name: Increment version + +on: + push: + branches: + - main + workflow_dispatch: + inputs: + newVersion: + description: Version to increment + required: true + default: auto + type: string + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + VERSION_FILE: ./package.json + +jobs: + set-next-version: + name: Set next version automatically + runs-on: ubuntu-latest + if: github.event_name != 'workflow_dispatch' || github.event.inputs.newVersion == 'auto' + outputs: + version: ${{ steps.set-next-version.outputs.version }} + + steps: + - name: Checkout main + uses: actions/checkout@v4 + with: + ref: main + + - name: Get version on main + id: get-main-version + run: | + currentVersion=$(node -p -e "require('${{ env.VERSION_FILE }}').version") + echo "version=$currentVersion" >> $GITHUB_OUTPUT + + - name: Checkout develop + uses: actions/checkout@v4 + with: + ref: develop + + - name: Get version on develop + id: get-develop-version + run: | + currentVersion=$(node -p -e "require('${{ env.VERSION_FILE }}').version") + echo "version=$currentVersion" >> $GITHUB_OUTPUT + + - name: Set next version + id: set-next-version + env: + VERSION_DELIMITER: . + run: | + function ver { printf "%03d%03d%03d%03d" $(echo "$1" | tr '${VERSION_DELIMITER}' ' '); } + + echo "main version: $(ver ${{ steps.get-main-version.outputs.version }})" + echo "develop version: $(ver ${{ steps.get-develop-version.outputs.version }})" + + if [[ $(ver ${{ steps.get-main-version.outputs.version }}) -gt $(ver ${{ steps.get-develop-version.outputs.version }}) ]]; then + echo "main version is greater than develop version" + + echo "version=${{ steps.get-main-version.outputs.version }}" >> $GITHUB_OUTPUT + else + echo "develop version is greater or equal to main version" + + versionComponents=($(echo "${{ steps.get-develop-version.outputs.version }}" | tr ${VERSION_DELIMITER} '\n')) + + versionComponents[1]=$((versionComponents[1]+1)) + versionComponents[2]=0 + + nextVersion=$(IFS=${VERSION_DELIMITER} ; echo "${versionComponents[*]}") + + echo "version=$nextVersion" >> $GITHUB_OUTPUT + fi + + - name: Print next version + run: | + echo "Next version: ${{ steps.set-next-version.outputs.version }}" + + increment-version: + name: Increment version + runs-on: ubuntu-latest + needs: [set-next-version] + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: develop + + - name: Set next version + id: next-version + run: | + if [ ${{ github.event_name }} != 'workflow_dispatch' ] || [ ${{ github.event.inputs.newVersion }} == 'auto' ]; then + echo "version=${{ needs.set-next-version.outputs.version }}" >> $GITHUB_OUTPUT + else + echo "version=${{ github.event.inputs.newVersion }}" >> $GITHUB_OUTPUT + fi + + - name: Change version in ${{ env.VERSION_FILE }} + run: | + jq ".version = \"${{ steps.next-version.outputs.version }}\"" ${{ env.VERSION_FILE }} > ${{ env.VERSION_FILE }}.tmp && mv ${{ env.VERSION_FILE }}.tmp ${{ env.VERSION_FILE }} + + - name: Update package-lock.json + run: npm install + + - name: Create a new pull request + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ github.token }} + branch: chore/bump-version-to-${{ steps.next-version.outputs.version }} + base: develop + delete-branch: true + title: "[Chore] Bump version to ${{ steps.next-version.outputs.version }}" + commit-message: "Bump version to ${{ steps.next-version.outputs.version }}" + labels: | + type : chore + body: | + ## What happened 👀 + + Bump version to ${{ steps.next-version.outputs.version }} + + ## Insight 📝 + + Automatically created by the GitHub Actions workflow. + + ## Proof Of Work 📹 + + On the Files changed tab diff --git a/package-lock.json b/package-lock.json index 4cfe26d7..4df7dda9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@nimblehq/infra-template", - "version": "2.0.2", + "version": "2.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@nimblehq/infra-template", - "version": "2.0.2", + "version": "2.3.0", "license": "MIT", "dependencies": { "@oclif/core": "^2", @@ -11605,9 +11605,9 @@ } }, "node_modules/ts-jest": { - "version": "29.0.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", - "integrity": "sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==", + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -11616,7 +11616,7 @@ "json5": "^2.2.3", "lodash.memoize": "4.x", "make-error": "1.x", - "semver": "7.x", + "semver": "^7.5.3", "yargs-parser": "^21.0.1" }, "bin": { @@ -11630,7 +11630,7 @@ "@jest/types": "^29.0.0", "babel-jest": "^29.0.0", "jest": "^29.0.0", - "typescript": ">=4.3" + "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { "@babel/core": { @@ -22239,9 +22239,9 @@ "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==" }, "ts-jest": { - "version": "29.0.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", - "integrity": "sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==", + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", "dev": true, "requires": { "bs-logger": "0.x", @@ -22250,7 +22250,7 @@ "json5": "^2.2.3", "lodash.memoize": "4.x", "make-error": "1.x", - "semver": "7.x", + "semver": "^7.5.3", "yargs-parser": "^21.0.1" } }, diff --git a/package.json b/package.json index 4dbe7bf1..3f2745fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nimblehq/infra-template", - "version": "2.2.0", + "version": "2.3.0", "description": "Nimble Infrastructure Template generator", "author": "Nimblehq", "bin": { diff --git a/src/generators/addons/aws/modules/core/iamUserAndGroup.ts b/src/generators/addons/aws/modules/core/iamUserAndGroup.ts index 7b7af950..62111ff7 100644 --- a/src/generators/addons/aws/modules/core/iamUserAndGroup.ts +++ b/src/generators/addons/aws/modules/core/iamUserAndGroup.ts @@ -15,8 +15,8 @@ const iamVariablesContent = dedent` type = list(string) } - variable "iam_bot_emails" { - description = "List of bot emails to provision IAM user account" + variable "iam_infra_service_account_emails" { + description = "List of infra service account emails to provision IAM user account" type = list(string) } @@ -43,35 +43,33 @@ const iamUsersModuleContent = dedent` usernames = var.iam_developer_emails } - module "iam_bot_users" { + module "iam_infra_service_account_users" { source = "../modules/iam_users" - usernames = var.iam_bot_emails + usernames = var.iam_infra_service_account_emails + has_login = false }`; const iamGroupMembershipModuleContent = dedent` - module "iam_admin_group_membership" { + module "iam_group_membership" { source = "../modules/iam_group_membership" - name = "admin-group-membership" - group = module.iam_groups.admin_group - users = var.iam_admin_emails - } - - module "iam_bot_group_membership" { - source = "../modules/iam_group_membership" - - name = "bot-group-membership" - group = module.iam_groups.bot_group - users = var.iam_bot_emails - } - - module "iam_developer_group_membership" { - source = "../modules/iam_group_membership" - - name = "developer-group-membership" - group = module.iam_groups.developer_group - users = var.iam_developer_emails + for_each = { + admin = { group = module.iam_groups.admin_group, users = var.iam_admin_emails }, + infra_service_account = { group = module.iam_groups.infra_service_account_group, users = var.iam_infra_service_account_emails }, + developer = { group = module.iam_groups.developer_group, users = var.iam_developer_emails } + } + + name = "\${each.key}-group-membership" + group = each.value.group + users = each.value.users + + depends_on = [ + module.iam_groups, + module.iam_admin_users, + module.iam_developer_users, + module.iam_infra_service_account_users, + ] }`; const iamOutputsContent = dedent` @@ -83,11 +81,6 @@ const iamOutputsContent = dedent` output "iam_developer_temporary_passwords" { description = "List of first time passwords for developer accounts. Must be changed at first time login and will no longer be valid." value = module.iam_developer_users.temporary_passwords - } - - output "iam_bot_temporary_passwords" { - description = "List of first time passwords for bot accounts. Must be changed at first time login and will no longer be valid." - value = module.iam_bot_users.temporary_passwords }`; const applyAwsIamUserAndGroup = async ({ projectName }: AwsOptions) => { diff --git a/src/generators/addons/aws/modules/core/vpc.ts b/src/generators/addons/aws/modules/core/vpc.ts index 0858abe9..5702bc14 100644 --- a/src/generators/addons/aws/modules/core/vpc.ts +++ b/src/generators/addons/aws/modules/core/vpc.ts @@ -20,6 +20,7 @@ const vpcModuleContent = dedent` source = "../modules/vpc" env_namespace = local.env_namespace + region = var.region }`; const applyAwsVpc = async (options: AwsOptions) => { diff --git a/templates/addons/aws/modules/iam_groups/data.tf b/templates/addons/aws/modules/iam_groups/data.tf index cb105971..a1e9a593 100644 --- a/templates/addons/aws/modules/iam_groups/data.tf +++ b/templates/addons/aws/modules/iam_groups/data.tf @@ -1,10 +1,10 @@ locals { # Comes from https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_aws_my-sec-creds-self-manage.html - # This policy allows users to view and edit their own passwords, access keys, MFA devices, X.509 certificates, SSH keys, and Git credentials. - # In addition, users are required to set up and authenticate using MFA before performing any other operations in AWS. - # It also means this policy does NOT allow users to reset a password while signing in to the AWS Management Console for the first time. + # This policy allows users to view and edit their own passwords, access keys, MFA devices, X.509 certificates, SSH keys, and Git credentials. + # In addition, users are required to set up and authenticate using MFA before performing any other operations in AWS. + # It also means this policy does NOT allow users to reset a password while signing in to the AWS Management Console for the first time. # They must first set up their MFA because allowing users to change their password without MFA can be a security risk. - # + # # The following actions are added to the initial policy from AWS # - iam:GetLoginProfile: allows the IAM user to view their account information on the security page. # - iam:GetAccessKeyLastUsed: allows the IAM user to view the last time their access key was used. @@ -120,16 +120,16 @@ locals { ] }) - # For the bot account + # For the infra-service-account account # It must be able to manage policies during terraform apply & create/delete users, permissions, etc. during terraform apply full_iam_access_policy = jsonencode({ - version = "2012-10-17" - statement = [ + Version = "2012-10-17" + Statement = [ { - sid = "AllowManageRoleAndPolicy" - effect = "Allow" - resources = ["arn:aws:iam::*"] - actions = ["iam:*"] + Sid = "AllowManageRoleAndPolicy" + Effect = "Allow" + Resource = ["arn:aws:iam::*"] + Action = ["iam:*"] } ] }) diff --git a/templates/addons/aws/modules/iam_groups/main.tf b/templates/addons/aws/modules/iam_groups/main.tf index efc7be6e..e4fc4a0b 100644 --- a/templates/addons/aws/modules/iam_groups/main.tf +++ b/templates/addons/aws/modules/iam_groups/main.tf @@ -4,8 +4,8 @@ resource "aws_iam_group" "admin" { } #tfsec:ignore:aws-iam-enforce-group-mfa -resource "aws_iam_group" "bot" { - name = "Bot-group" +resource "aws_iam_group" "infra-service-account" { + name = "Infra-service-account-group" } #tfsec:ignore:aws-iam-enforce-group-mfa @@ -30,12 +30,15 @@ resource "aws_iam_group_policy_attachment" "developer_power_user_access" { policy_arn = data.aws_iam_policy.power_user_access.arn } -resource "aws_iam_group_policy_attachment" "bot_power_user_access" { - group = aws_iam_group.bot.name +resource "aws_iam_group_policy_attachment" "infra_service_account_power_user_access" { + group = aws_iam_group.infra-service-account.name policy_arn = data.aws_iam_policy.power_user_access.arn } -resource "aws_iam_group_policy" "bot_full_iam_access" { - group = aws_iam_group.bot.name +# This IAM policy is needed for the infra-service-account account to manage IAM users & groups +# tfsec:ignore:aws-iam-no-policy-wildcards +resource "aws_iam_group_policy" "infra_service_account_full_iam_access" { + name = "AllowFullIamAccess" + group = aws_iam_group.infra-service-account.name policy = local.full_iam_access_policy } diff --git a/templates/addons/aws/modules/iam_groups/outputs.tf b/templates/addons/aws/modules/iam_groups/outputs.tf index 95166ff5..d851672d 100644 --- a/templates/addons/aws/modules/iam_groups/outputs.tf +++ b/templates/addons/aws/modules/iam_groups/outputs.tf @@ -8,7 +8,7 @@ output "developer_group" { value = aws_iam_group.developer.name } -output "bot_group" { - description = "IAM Group with bot permissions" - value = aws_iam_group.bot.name +output "infra_service_account_group" { + description = "IAM Group with infra-service-account permissions" + value = aws_iam_group.infra-service-account.name } diff --git a/templates/addons/aws/modules/vpc/main.tf b/templates/addons/aws/modules/vpc/main.tf index f288adba..f3762043 100644 --- a/templates/addons/aws/modules/vpc/main.tf +++ b/templates/addons/aws/modules/vpc/main.tf @@ -15,3 +15,22 @@ module "vpc" { one_nat_gateway_per_az = false enable_dns_hostnames = true } + +data "aws_route_tables" "private_route_table" { + vpc_id = module.vpc.vpc_id + + filter { + name = "tag:Name" + values = ["${var.env_namespace}-vpc-private"] + } +} + +resource "aws_vpc_endpoint" "logs" { + vpc_id = module.vpc.vpc_id + service_name = "com.amazonaws.${var.region}.logs" + route_table_ids = data.aws_route_tables.private_route_table.ids + + tags = { + Name = "${var.env_namespace}-vpc-endpoint-logs" + } +} diff --git a/templates/addons/aws/modules/vpc/variables.tf b/templates/addons/aws/modules/vpc/variables.tf index aa8b9f5f..b06ba14d 100644 --- a/templates/addons/aws/modules/vpc/variables.tf +++ b/templates/addons/aws/modules/vpc/variables.tf @@ -2,3 +2,8 @@ variable "env_namespace" { description = "The namespace with environment for the VPCs, used as the prefix for the VPC names, e.g. acme-web-staging" type = string } + +variable "region" { + description = "AWS region" + type = string +}