diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 16a6442..d6f06a5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,6 +59,10 @@ env: TF_PLAN: "tfplan" TF_VERSION: "0.12.30" # "latest" is supported TF_WORKING_DIR: ./terraform + # https://github.com/terraform-linters/tflint-ruleset-azurerm/releases + TFLINT_RULESET_AZURERM_VERSION: "v0.8.2" + # https://github.com/terraform-linters/tflint/releases + TFLINT_VERSION: "v0.24.1" # Env var concatenation is currently not supported at Workflow or Job scope. See workaround below: # https://github.community/t5/GitHub-Actions/How-can-we-concatenate-multiple-env-vars-at-workflow-and-job/td-p/48489 @@ -139,28 +143,12 @@ jobs: terraform validate working-directory: ${{ env.TF_WORKING_DIR }} - # - name: tflint - # uses: reviewdog/action-tflint@v1.4.2 - # with: - # github_token: ${{ secrets.github_token }} - # working_directory: ${{ env.TF_WORKING_DIR }} - # reporter: github-pr-check # Optional. Change reporter - # fail_on_error: "true" # Optional. Fail action if errors are found - # filter_mode: "nofilter" # Optional. Check all files, not just the diff - # flags: "--module" # Optional. Add custom tflint flags - - # - name: Terraform Lint - # uses: rickardl/tflint-action@v1.5 - # with: - # tflint_action_comment: true - # tflint_action_folder: ${{ env.TF_WORKING_DIR }} - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Terraform Lint run: ./scripts/tflint.sh env: TF_WORKING_DIR: ${{ env.TF_WORKING_DIR }} + TFLINT_RULESET_AZURERM_VERSION: ${{ env.TFLINT_RULESET_AZURERM_VERSION }} + TFLINT_VERSION: ${{ env.TFLINT_VERSION }} - name: Terraform Plan id: plan @@ -188,8 +176,8 @@ jobs: run: ./scripts/wait.sh + # Ansible # TODO: enable Ansible Lint once this issue has been resolved: https://github.com/ansible/ansible-lint-action/issues/36 - # # Ansible # - name: Lint Ansible Playbook # uses: ansible/ansible-lint-action@6c8c141 # with: diff --git a/.github/workflows/destroy.yml b/.github/workflows/destroy.yml index a9c5596..2018693 100644 --- a/.github/workflows/destroy.yml +++ b/.github/workflows/destroy.yml @@ -89,12 +89,6 @@ jobs: echo "VELERO_STORAGE_ACCOUNT=${{ env.PREFIX }}stbckuksouth001" >> $GITHUB_ENV echo "VELERO_STORAGE_RG=${{ env.PREFIX }}-rg-velero-dev-001" >> $GITHUB_ENV - # # Show event info - # - name: Show triggered event data - # run: pwsh -command "./scripts/Get-EventData.ps1" - # env: - # GITHUB_CONTEXT: ${{ toJson(github) }} - # Login - name: Login to Azure run: ./scripts/azure_login.sh diff --git a/.gitignore b/.gitignore index 07fe551..b07bc59 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,11 @@ # Misc -terraform/.terraform +.terraform +terraform.tfstate* localonly credentials* /temp *.egg-info +*.cer # Azure Functions artifacts bin diff --git a/ansible/site.yml b/ansible/site.yml index e24c10e..1854036 100644 --- a/ansible/site.yml +++ b/ansible/site.yml @@ -40,7 +40,7 @@ name: docker_repo - import_role: name: helm_repo - - import_role: - name: pypi_repo +# - import_role: +# name: pypi_repo - import_role: name: raw_repo diff --git a/function_app/profile.ps1 b/function_app/profile.ps1 index 5b999c8..0be2628 100644 --- a/function_app/profile.ps1 +++ b/function_app/profile.ps1 @@ -13,7 +13,7 @@ # Remove this if you are not planning on using MSI or Azure PowerShell. if ($env:MSI_SECRET -and (Get-Module -ListAvailable Az.Accounts)) { Write-Output "Authenticating PowerShell using Managed Identity..." - # Disable-AzContextAutosave -Scope Process | Out-Null + Disable-AzContextAutosave -Scope Process | Out-Null Connect-AzAccount -Identity } elseif ($env:ARM_TENANT_ID -and $env:ARM_SUBSCRIPTION_ID -and $env:ARM_CLIENT_ID -and $env:ARM_CLIENT_SECRET) { diff --git a/function_app/requirements.psd1 b/function_app/requirements.psd1 index 35f155b..ab3deba 100644 --- a/function_app/requirements.psd1 +++ b/function_app/requirements.psd1 @@ -2,8 +2,8 @@ # See https://aka.ms/functionsmanageddependency for additional information. # @{ - # 'Az' = '5.*' + 'Az' = '5.*' # Only need Account and Compute cmdlets for VMSS and VM status checks - 'Az.Accounts' = '2.*' - 'Az.Compute' = '4.*' + # 'Az.Accounts' = '2.*' + # 'Az.Compute' = '4.*' } diff --git a/scripts/tflint.sh b/scripts/tflint.sh index 26bfcf6..77e5ea1 100644 --- a/scripts/tflint.sh +++ b/scripts/tflint.sh @@ -1,22 +1,43 @@ #! /usr/bin/env bash # # installs and runs tflint with tflint-ruleset-azurerm plugin +# rules: https://github.com/terraform-linters/tflint-ruleset-azurerm/blob/master/docs/rules/ # ensure strict mode and predictable failure set -euo pipefail trap "echo 'error: Script failed: see failed command above'" ERR # vars +# Set local vars from env var, with default fallbacks +TFLINT_VERSION="${TFLINT_VERSION:-v0.23.1}" +TFLINT_RULESET_AZURERM_VERSION="${TFLINT_RULESET_AZURERM_VERSION:-v0.7.0}" +TF_FLAGS=("$TF_WORKING_DIR") +export TFLINT_LOG=debug +# use empty array to skip adding disabled rules, eg: "DISABLED_RULES=()" DISABLED_RULES=("azurerm_log_analytics_workspace_invalid_retention_in_days") -message="Downloading tflint and azurerm plugin" +# use dynamic flags +if [ ${#DISABLED_RULES[@]} -gt 0 ]; then + echo "${#DISABLED_RULES[@]} DISABLED_RULES were defined: [${DISABLED_RULES[*]}]." + + # repeat flag multiple times + for rule in "${DISABLED_RULES[@]}"; do + echo "Adding [$rule] to flags" + TF_FLAGS+=(--disable-rule="$rule") + done + +else + echo "DISABLED_RULES were not defined. Skipping." +fi + +message="Downloading tflint ($TFLINT_VERSION) and azurerm plugin ($TFLINT_RULESET_AZURERM_VERSION)" echo "STARTED: $message..." # download tflint -curl -L "$(curl -Ls https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" -o tflint.zip && unzip tflint.zip && rm tflint.zip +curl -L "https://github.com/terraform-linters/tflint/releases/download/$TFLINT_VERSION/tflint_linux_amd64.zip" -o tflint.zip && unzip tflint.zip && rm tflint.zip # download tflint-ruleset-azurerm plugin -curl -L "$(curl -Ls https://api.github.com/repos/terraform-linters/tflint-ruleset-azurerm/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" -o tflint-ruleset-azurerm_linux_amd64.zip && unzip tflint-ruleset-azurerm_linux_amd64.zip && rm tflint-ruleset-azurerm_linux_amd64.zip +curl -L "https://github.com/terraform-linters/tflint-ruleset-azurerm/releases/download/$TFLINT_RULESET_AZURERM_VERSION/tflint-ruleset-azurerm_linux_amd64.zip" -o tflint-ruleset-azurerm_linux_amd64.zip && unzip tflint-ruleset-azurerm_linux_amd64.zip && rm tflint-ruleset-azurerm_linux_amd64.zip # move tflint-ruleset-azurerm plugin to correct path install -D -m 777 tflint-ruleset-azurerm ./.tflint.d/plugins/tflint-ruleset-azurerm @@ -35,5 +56,5 @@ EOF cat .tflint.hcl # run tflint -# expand array for disabled rules -TFLINT_LOG=debug ./tflint "$TF_WORKING_DIR" --disable-rule="${DISABLED_RULES[*]}" +echo "Running tflint with the following flags: [${TF_FLAGS[*]}]" +./tflint "${TF_FLAGS[@]}" diff --git a/terraform/aks.tf b/terraform/aks.tf index 43d5548..5fac5db 100644 --- a/terraform/aks.tf +++ b/terraform/aks.tf @@ -54,7 +54,7 @@ resource "azurerm_log_analytics_solution" "aks" { # https://registry.terraform.io/modules/adamrushuk/aks/azurerm/latest module "aks" { source = "adamrushuk/aks/azurerm" - version = "0.4.2" + version = "0.7.0" kubernetes_version = var.kubernetes_version location = azurerm_resource_group.aks.location @@ -67,17 +67,20 @@ module "aks" { # override defaults default_node_pool = { - name = var.agent_pool_profile_name - count = var.agent_pool_node_count - # availability_zones = null - vm_size = var.agent_pool_profile_vm_size - enable_auto_scaling = var.agent_pool_enable_auto_scaling - max_count = var.agent_pool_node_max_count - max_pods = 90 - min_count = var.agent_pool_node_min_count - os_disk_size_gb = var.agent_pool_profile_disk_size_gb + name = var.agent_pool_profile_name + count = var.agent_pool_node_count + orchestrator_version = var.kubernetes_version + vm_size = var.agent_pool_profile_vm_size + enable_auto_scaling = var.agent_pool_enable_auto_scaling + max_count = var.agent_pool_node_max_count + max_pods = 90 + min_count = var.agent_pool_node_min_count + os_disk_size_gb = var.agent_pool_profile_disk_size_gb } # add-ons log_analytics_workspace_id = var.aks_container_insights_enabled == true ? azurerm_log_analytics_workspace.aks[0].id : "" + + # Add existing group to the new AKS cluster admin group + aks_admin_group_member_name = var.aks_admins_aad_group_name } diff --git a/terraform/argocd_sso/README.md b/terraform/argocd_sso/README.md new file mode 100644 index 0000000..8152b38 --- /dev/null +++ b/terraform/argocd_sso/README.md @@ -0,0 +1,211 @@ + +# Argo CD Notes + +A collection of notes whilst testing Argo CD. + +Full SSO configuration currently cannot be done with Terraform, so I've partial automated the Application Registration, +and it's Service Principle (which makes an "Enterprise App"), but there are manual steps afterwards: + +- Add `Sign on URL` +- Add `email` User Claim +- Create `SAML Signing Cert` +- Download SAML cert (base64), ready for the ConfigMap yaml +- Create yaml ConfigMaps for SSO and RBAC +- Apply ConfigMaps + + +## Contents + +- [Reference](#reference) +- [Getting Started](#getting-started) +- [Add Repository](#add-repository) +- [Configure SSO for Argo CD](#configure-sso-for-argo-cd) + +## Reference + +- https://github.com/argoproj/argo-cd/blob/master/docs/faq.md#i-forgot-the-admin-password-how-do-i-reset-it + +## Getting Started + +Use `--grpc-web` if you get the `argocd transport: received the unexpected content-type "text/plain; charset=utf-8"` error + +```bash +# vars +ARGO_SERVER="argocd.thehypepipe.co.uk" + +# install +VERSION=$(curl --silent "https://api.github.com/repos/argoproj/argo-cd/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') +sudo curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/$VERSION/argocd-linux-amd64 +sudo chmod +x /usr/local/bin/argocd + +# show version +argocd version --grpc-web --server "$ARGO_SERVER" + +# get admin password +# default password is server pod name, eg: "argocd-server-89c6cd7d4-h7vmn" +ARGO_ADMIN_PASSWORD=$(kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2) + +# login +argocd logout -h +argocd logout "$ARGO_SERVER" +argocd login -h +argocd login "$ARGO_SERVER" --grpc-web --username admin --password "$ARGO_ADMIN_PASSWORD" + +# change password +read -s NEW_ARGO_ADMIN_PASSWORD +# echo "$NEW_ARGO_ADMIN_PASSWORD" +argocd account update-password --grpc-web -h +argocd account update-password --grpc-web --account admin --current-password "$ARGO_ADMIN_PASSWORD" --new-password "$NEW_ARGO_ADMIN_PASSWORD" + +# test new admin password +argocd logout "$ARGO_SERVER" +argocd login "$ARGO_SERVER" --grpc-web --username admin --password "$NEW_ARGO_ADMIN_PASSWORD" + +# account tasks +argocd account list +argocd account -h + +# misc +argocd -h +``` + +## Add Repository + +```bash +# Add a Git repository via SSH using a private key for authentication, ignoring the server's host key +# argocd repo add git@github.com:adamrushuk/charts-private.git --insecure-ignore-host-key --ssh-private-key-path ~/.ssh/id_ed25519 +argocd repo add -h +argocd repo add git@github.com:adamrushuk/charts-private.git --ssh-private-key-path ~/.ssh/id_ed25519 + +# add known_host entries for private git server +ssh-keyscan gitlab.thehypepipe.co.uk | argocd cert add-ssh --batch + +# create ssh key for private git repo access +# ~/.ssh/id_ed25519 +ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_argocd -C "argocd@gitlab.thehypepipe.co.uk" +ll ~/.ssh + +# check public key fingerprint +ssh-keygen -lf ~/.ssh/id_ed25519_argocd.pub + +# copy public key and enter into source git repo settings +# eg, GitLab repo deploy key: https://gitlab.thehypepipe.co.uk/helm-charts/-/settings/repository > Deploy Keys +cat ~/.ssh/id_ed25519_argocd.pub + +# add helm chart repository +argocd repo add git@gitlab.thehypepipe.co.uk/helm-charts.git --ssh-private-key-path ~/.ssh/id_ed25519_argocd + +# show repo +argocd repo list +``` + +## Configure SSO for Argo CD + +https://argoproj.github.io/argo-cd/operator-manual/user-management/microsoft/ + +```bash +# subscription where ArgoCD is deployed +AR-Dev + +# created new AAD groups, eg: +AR-Dev_ArgoCD_Admin +AR-Dev_ArgoCD_ReadOnly + +# created argo enterprise app +AR-Dev_ArgoCD + + +# Basic SAML Configuration +# Identifier (Entity ID) +https://argocd.thehypepipe.co.uk/api/dex/callback +# Reply URL (Assertion Consumer Service URL) +https://argocd.thehypepipe.co.uk/api/dex/callback +# Sign on URL +https://argocd.thehypepipe.co.uk/auth/login + +# User Attributes & Claims +# + Add new claim | Name: email | Source: Attribute | Source attribute: user.userprincipalname ++ Add new claim | Name: email | Source: Attribute | Source attribute: user.primaryauthoritativeemail + ++ Add group claim | Which groups: All groups | Source attribute: Group ID | Customize: True | Name: Group | Namespace: | Emit groups as role claims: False + +# Create a "Sign SAML assertion" SAML Signing Cert (SHA-256) +# Download and base64 the cert, ready for the ConfigMap yaml + +# Login URL (ssoURL) +https://login.microsoftonline.com//saml2 +# Azure AD Identifier +https://sts.windows.net// +# Logout URL +https://login.microsoftonline.com//saml2 + + +# SSO: User Attributes & Claims +# select user.userprincipalname instead of user.mail ++ Add new claim | Name: email | Source: Attribute | Source attribute: user.userprincipalname + + + + +## Create RBAC patch ## +# RBAC vars +ARGO_ADMIN_GROUP_NAME="AR-Dev_ArgoCD_Admins" +ARGO_ADMIN_GROUP_ID=$(az ad group show --group "$ARGO_ADMIN_GROUP_NAME" --query "objectId" --output tsv) + +# Create RBAC patch yaml +cat > argocd-rbac-cm-patch.yaml << EOF +# Patch ConfigMap to add RBAC config +data: + policy.default: role:readonly + + # Map AAD Group Object Id to an Argo CD role + # (Nested groups work fine) + # g, , role:admin + policy.csv: | + g, $ARGO_ADMIN_GROUP_ID, role:admin +EOF + +# Apply yaml RBAC patch for default admin and readonly roles +kubectl patch configmap/argocd-rbac-cm --namespace argocd --type merge --patch "$(cat argocd-rbac-cm-patch.yaml)" + + + +## Create SSO patch yaml ## +# SSO vars +ARGO_FQDN="argocd.thehypepipe.co.uk" +TENANT_ID=$(az account show --query "tenantId" --output tsv) +# assumes SAML Signing Certificate has been downloaded/saved as "ArgoCD.cer" (choosing Certificate (Base64) option) +SAML_CERT_BASE64=$(cat ArgoCD.cer | base64) +echo "$SAML_CERT_BASE64" + +# created indented string ready for caData YAML multi-line block +SAML_CERT_BASE64_INDENTED=$(cat ArgoCD.cer | base64 | sed 's/^/ /') +echo "$SAML_CERT_BASE64_INDENTED" + +cat > argocd-cm-sso-patch.yaml << EOF +# Patch ConfigMap to add dex SSO config +# source: https://argoproj.github.io/argo-cd/operator-manual/user-management/microsoft/ +data: + dex.config: | + logger: + level: debug + format: json + connectors: + - type: saml + id: saml + name: saml + config: + entityIssuer: https://$ARGO_FQDN/api/dex/callback + ssoURL: https://login.microsoftonline.com/$TENANT_ID/saml2 + caData: | +$SAML_CERT_BASE64_INDENTED + redirectURI: https://$ARGO_FQDN/api/dex/callback + usernameAttr: email + emailAttr: email + groupsAttr: Group +EOF + +# Apply SSO patch +kubectl patch configmap/argocd-cm --namespace argocd --type merge --patch "$(cat argocd-cm-sso-patch.yaml)" + +``` diff --git a/terraform/argocd_sso/argocd-cm-sso-patch.TEMPLATE.yaml b/terraform/argocd_sso/argocd-cm-sso-patch.TEMPLATE.yaml new file mode 100644 index 0000000..b32c731 --- /dev/null +++ b/terraform/argocd_sso/argocd-cm-sso-patch.TEMPLATE.yaml @@ -0,0 +1,25 @@ +# Patch ConfigMap to add dex SSO config +# source: https://argoproj.github.io/argo-cd/operator-manual/user-management/microsoft/ +# +# After following steps above, modify content and run command below to apply patch: +# kubectl patch configmap/argocd-cm --namespace argocd --type merge --patch "$(cat argocd-cm-patch-dev.yaml)" + +# TEMPLATE +data: + dex.config: | + logger: + level: debug + format: json + connectors: + - type: saml + id: saml + name: saml + config: + entityIssuer: https://$ARGO_FQDN/api/dex/callback + ssoURL: https://login.microsoftonline.com/$TENANT_ID/saml2 + caData: | + $SAML_CERT_BASE64 + redirectURI: https://$ARGO_FQDN/api/dex/callback + usernameAttr: email + emailAttr: email + groupsAttr: Group diff --git a/terraform/argocd_sso/argocd-rbac-cm-patch.TEMPLATE.yaml b/terraform/argocd_sso/argocd-rbac-cm-patch.TEMPLATE.yaml new file mode 100644 index 0000000..2a55d75 --- /dev/null +++ b/terraform/argocd_sso/argocd-rbac-cm-patch.TEMPLATE.yaml @@ -0,0 +1,16 @@ +# Patch ConfigMap to add RBAC config +# source: +# - https://argoproj.github.io/argo-cd/operator-manual/rbac/ +# +# Run command below to apply patch for default admin and readonly roles: +# kubectl patch configmap/argocd-rbac-cm --namespace argocd --type merge --patch-file "argocd-rbac-cm-patch.yaml" + +# TEMPLATE +data: + policy.default: role:readonly + + # Map AAD Group Object Id to an Argo CD role + # (Nested groups work fine) + # g, , role:admin + policy.csv: | + g, $ARGO_ADMIN_GROUP_ID, role:admin diff --git a/terraform/argocd_sso/argocd_aad.tf b/terraform/argocd_sso/argocd_aad.tf new file mode 100644 index 0000000..e413157 --- /dev/null +++ b/terraform/argocd_sso/argocd_aad.tf @@ -0,0 +1,145 @@ +provider "azurerm" { + version = "2.44.0" + features {} +} + +variable "dns_zone_name" { + default = "thehypepipe.co.uk" +} + +variable "admin_consent" { + default = true +} + + +# TODO: remove temp outputs +# data "azuread_application" "argocd_manual" { +# display_name = "AR-Dev_ArgoCD" +# } + +# output "azure_app_object_manual" { +# value = data.azuread_application.argocd_manual +# } + +# data "azuread_service_principal" "argocd_manual" { +# display_name = "AR-Dev_ArgoCD" +# } + +# output "azure_sp_object_manual" { +# value = data.azuread_service_principal.argocd_manual +# } + +output "azure_ad_object_argocd" { + value = azuread_application.argocd +} +output "azure_sp_object_argocd" { + value = azuread_service_principal.argocd +} + +# https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application +# source: https://github.com/hashicorp/terraform-provider-azuread/issues/173#issuecomment-663727531 +resource "azuread_application" "argocd" { + display_name = "ArgoCD" + prevent_duplicate_names = true + homepage = "https://argocd.${var.dns_zone_name}" + identifier_uris = ["https://argocd.${var.dns_zone_name}/api/dex/callback"] + reply_urls = ["https://argocd.${var.dns_zone_name}/api/dex/callback"] + available_to_other_tenants = false + oauth2_allow_implicit_flow = true + # type = "webapp/api" + # owners = ["00000004-0000-0000-c000-000000000000"] + group_membership_claims = "All" + + required_resource_access { + //https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/grant-admin-consent + resource_app_id = "00000003-0000-0000-c000-000000000000" + resource_access { + id = "5f8c59db-677d-491f-a6b8-5f174b11ec1d" + type = "Scope" + } + resource_access { + id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" + type = "Scope" + } + } + + app_role { + allowed_member_types = [ + "User" + ] + + description = "User" + display_name = "User" + is_enabled = true + } + + app_role { + allowed_member_types = [ + "User" + ] + + description = "msiam_access" + display_name = "msiam_access" + is_enabled = true + } + + // We need to wait because Azure Graph API returns a 200 before its call-able #eventualconsistancy... + provisioner "local-exec" { + command = "sleep 20" + } + + //https://github.com/Azure/azure-cli/issues/7579 + //Add metadata URL + // provisioner "local-exec" { + // command = "az ad app update --id ${self.application_id} --set samlMetadataUrl=${var.saml_metadata_url}" + // } + // We need to wait because Azure Graph API returns a 200 before its call-able #eventualconsistancy... + // provisioner "local-exec" { + // command = "sleep 5" + // } + //https://github.com/Azure/azure-cli/issues/12946 + //https://github.com/Azure/azure-cli/issues/11534 + //https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims + //Optional Claims for tokens + provisioner "local-exec" { + command = "az rest --method PATCH --uri 'https://graph.microsoft.com/v1.0/applications/${self.object_id}' --body '{\"optionalClaims\": {\"saml2Token\": [{\"name\": \"groups\", \"additionalProperties\": []}]}}'" + } +} + +resource "azuread_service_principal" "argocd" { + //https://github.com/Azure/azure-cli/issues/9250 + application_id = azuread_application.argocd.application_id + tags = [ + "WindowsAzureActiveDirectoryIntegratedApp", + "WindowsAzureActiveDirectoryCustomSingleSignOnApplication", + "WindowsAzureActiveDirectoryGalleryApplicationNonPrimaryV1" + ] + + // We need to wait because Azure Graph API returns a 200 before its call-able #eventualconsistancy... + provisioner "local-exec" { + command = "sleep 20" + } + + # https://docs.microsoft.com/en-us/graph/application-saml-sso-configure-api?tabs=http#set-single-sign-on-mode + provisioner "local-exec" { + command = "az ad sp update --id ${azuread_application.argocd.application_id} --set preferredSingleSignOnMode='saml'" + } + + # depends_on = [ + # azuread_application.argocd + # ] +} + +resource "null_resource" "grant_admin_constent" { + count = var.admin_consent ? 1 : 0 + // https://docs.microsoft.com/en-us/cli/azure/ad/app/permission?view=azure-cli-latest#code-try-3 + provisioner "local-exec" { + command = "sleep 20" + } + provisioner "local-exec" { + command = "az ad app permission admin-consent --id ${azuread_application.argocd.application_id}" + } + depends_on = [ + azuread_service_principal.argocd + ] +} diff --git a/terraform/argocd_sso/sso_claims.sh b/terraform/argocd_sso/sso_claims.sh new file mode 100644 index 0000000..8b09dbe --- /dev/null +++ b/terraform/argocd_sso/sso_claims.sh @@ -0,0 +1,62 @@ +# Adding extra SSO claim +# Set "ArgoCD" app reg () +ARGO_APP_OBJECT_ID="" +az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$ARGO_APP_OBJECT_ID" --body '{\"optionalClaims\": {\"saml2Token\": [{\"name\": \"test\", \"additionalProperties\": [\"sam_account_name\"]}]}}' + +az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$ARGO_APP_OBJECT_ID" --body '{\"optionalClaims\": {\"saml2Token\": [{\"name\": \"userprincipalname\", \"source\": \"user\", \"additionalProperties\": [\"email\"]}]}}' + +# works via PS +$ARGO_APP_OBJECT_ID = "" +az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$ARGO_APP_OBJECT_ID" --body '{\"optionalClaims\": {\"saml2Token\": [{\"name\": \"groups\", \"additionalProperties\": [\"sam_account_name\"]}]}}' + +# add custom email claim +az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$ARGO_APP_OBJECT_ID" --body '{\"optionalClaims\": {\"saml2Token\": [{\"name\": \"userprincipalname\", \"source\": \"user\", \"additionalProperties\": [\"email\"]}]}}' + +# add custom group claim +az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$ARGO_APP_OBJECT_ID" --body '{\"optionalClaims\": {\"saml2Token\": [{\"name\": \"groups\", \"source\": null}]}}' + + + +# Get +az rest --method GET --uri "https://graph.microsoft.com/v1.0/applications/$ARGO_APP_OBJECT_ID" +az rest --method GET --uri "https://graph.microsoft.com/v1.0/applications/$ARGO_APP_OBJECT_ID" | clip.exe + + "optionalClaims": { + "accessToken": [], + "idToken": [], + "saml2Token": [ + { + "additionalProperties": [], + "essential": false, + "name": "groups", + "source": null + } + ] + }, + + +# TF created "ArgoCD" App Reg +az rest --method GET --uri "https://graph.microsoft.com/v1.0/applications/$ARGO_APP_OBJECT_ID" + + "optionalClaims": { + "accessToken": [], + "idToken": [], + "saml2Token": [ + { + "additionalProperties": [ + "sam_account_name" + ], + "essential": false, + "name": "test", + "source": null + } + ] + }, + +# AR-Dev_ArgoCD - App reg +az rest --method GET --uri "https://graph.microsoft.com/v1.0/applications/$ARGO_APP_OBJECT_ID" + +# manual "AR-Dev_ArgoCD" Enterprise App +SERVICE_PRINCIPLE_ID="" +az rest --method GET --uri "https://graph.microsoft.com/v1.0/servicePrincipals/$SERVICE_PRINCIPLE_ID" +az rest --method GET --uri "https://graph.microsoft.com/v1.0/servicePrincipals/$SERVICE_PRINCIPLE_ID" | clip.exe diff --git a/terraform/argocd_sso/tf_test.ps1 b/terraform/argocd_sso/tf_test.ps1 new file mode 100644 index 0000000..7251a13 --- /dev/null +++ b/terraform/argocd_sso/tf_test.ps1 @@ -0,0 +1,16 @@ +# testing Terraform config for Enterprise App +# use WSL +cd ./terraform/argocd_sso + +# login +az login +az account show + +# init +terraform init + +# apply +terraform apply + +# destroy +terraform destroy diff --git a/terraform/data.tf b/terraform/data.tf index 9a0dea9..d251f4d 100644 --- a/terraform/data.tf +++ b/terraform/data.tf @@ -1,10 +1,6 @@ # Data sources data "azurerm_subscription" "current" {} -data "azuread_group" "aks" { - name = var.aad_group_name -} - data "azurerm_resource_group" "aks_node_rg" { name = module.aks.node_resource_group } diff --git a/terraform/files/argocd-apps.yaml b/terraform/files/argocd-apps.yaml new file mode 100644 index 0000000..d6f9b1c --- /dev/null +++ b/terraform/files/argocd-apps.yaml @@ -0,0 +1,32 @@ +# App of Apps pattern +# https://argoproj.github.io/argo-cd/operator-manual/cluster-bootstrapping/ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: argocd-apps + # namespace of argo cd deployment + namespace: argocd +spec: + project: default + source: + repoURL: git@github.com:adamrushuk/charts-private.git + targetRevision: main + path: charts/argocd-apps + helm: + # target helm version + # * NOT required if "Chart.yaml" helm metadata contains "apiVersion: v2" + # version: v3 + + # values file path is relative from the source.path folder + valueFiles: + - values.yaml + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + # https://argoproj.github.io/argo-cd/user-guide/auto_sync/#automated-sync-policy + automated: {} + # sync options which modifies sync behavior + # syncOptions: + # # namespace Auto-Creation ensures that namespace specified as the application destination exists in the destination cluster + # - CreateNamespace=true diff --git a/terraform/files/argocd-gitlab.yaml b/terraform/files/argocd-gitlab.yaml index 72206fe..dbdbe0a 100644 --- a/terraform/files/argocd-gitlab.yaml +++ b/terraform/files/argocd-gitlab.yaml @@ -11,7 +11,7 @@ spec: source: repoURL: git@github.com:adamrushuk/charts-private.git targetRevision: main - path: gitlab-https + path: charts/gitlab-https helm: # target helm version version: v3 diff --git a/terraform/helm/velero_values.yaml b/terraform/helm/velero_values.yaml index 1a25bd6..571b502 100644 --- a/terraform/helm/velero_values.yaml +++ b/terraform/helm/velero_values.yaml @@ -1,4 +1,4 @@ -# source: https://github.com/vmware-tanzu/helm-charts/blob/velero-2.14.1/charts/velero/values.yaml +# source: https://github.com/vmware-tanzu/helm-charts/blob/velero-2.14.8/charts/velero/values.yaml ## ## Configuration settings that directly affect the Velero deployment YAML. @@ -9,7 +9,7 @@ image: # https://hub.docker.com/r/velero/velero/tags repository: velero/velero - tag: v1.5.2 + tag: v1.5.3 # Digest value example: sha256:d238835e151cec91c6a811fe3a89a66d3231d9f64d09e5f3c49552672d271f38. If used, it will # take precedence over the image.tag. # digest: @@ -41,7 +41,7 @@ dnsPolicy: ClusterFirst initContainers: - name: velero-plugin-for-microsoft-azure # https://hub.docker.com/r/velero/velero-plugin-for-microsoft-azure/tags - image: velero/velero-plugin-for-microsoft-azure:v1.1.1 + image: velero/velero-plugin-for-microsoft-azure:v1.1.2 imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /target @@ -75,6 +75,7 @@ extraVolumeMounts: [] metrics: enabled: true scrapeInterval: 30s + scrapeTimeout: 10s # Pod annotations for Prometheus podAnnotations: @@ -85,6 +86,8 @@ metrics: serviceMonitor: enabled: false additionalLabels: {} + # ServiceMonitor namespace. Default to Velero namespace. + # namespace: # Install CRDs as a templates. Enabled by default. installCRDs: true diff --git a/terraform/helm_argocd.tf b/terraform/helm_argocd.tf index 92f74e3..744a2d7 100644 --- a/terraform/helm_argocd.tf +++ b/terraform/helm_argocd.tf @@ -93,7 +93,7 @@ resource "null_resource" "argocd_configure" { command = <<-EOT chmod -R +x ./files/scripts - timeout 5m ./files/scripts/argocd_config.sh + timeout 10m ./files/scripts/argocd_config.sh EOT } @@ -103,15 +103,20 @@ resource "null_resource" "argocd_configure" { ] } -# create argo app definitions +# create argo apps definition +# https://argoproj.github.io/argo-cd/operator-manual/cluster-bootstrapping/ resource "null_resource" "argocd_apps" { + triggers = { + argocd_app_yaml_contents = filemd5(var.argocd_apps_path) + } + provisioner "local-exec" { interpreter = ["/bin/bash", "-c"] environment = { KUBECONFIG = var.aks_config_path } command = <<-EOT - kubectl apply -f ${var.gitlab_argocd_app_path} + kubectl apply -f ${var.argocd_apps_path} EOT } diff --git a/terraform/providers.tf b/terraform/providers.tf index 1205713..af623f3 100644 --- a/terraform/providers.tf +++ b/terraform/providers.tf @@ -11,13 +11,13 @@ terraform { # versioning syntax: https://www.terraform.io/docs/configuration/modules.html#module-versions required_providers { # https://github.com/hashicorp/terraform-provider-helm/releases - helm = "2.0.1" + helm = "2.0.3" # https://github.com/hashicorp/terraform-provider-kubernetes/releases - kubernetes = "1.13.3" + kubernetes = "2.0.2" # https://github.com/terraform-providers/terraform-provider-azuread/releases - azuread = "1.2.2" + azuread = "1.4.0" random = "~> 2.2" # ~> 2.2 = 2.X.Y tls = "~> 2.1" @@ -30,14 +30,13 @@ terraform { # must include blank features block # https://github.com/terraform-providers/terraform-provider-azurerm/releases provider "azurerm" { - version = "2.43.0" + version = "2.51.0" features {} } # use statically defined credentials # https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#statically-defined-credentials provider "kubernetes" { - load_config_file = false # when you wish not to load the local config file host = module.aks.full_object.kube_admin_config[0].host client_certificate = base64decode(module.aks.full_object.kube_admin_config[0].client_certificate) client_key = base64decode(module.aks.full_object.kube_admin_config[0].client_key) diff --git a/terraform/variables.tf b/terraform/variables.tf index 4c8bedc..d845623 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -6,7 +6,7 @@ # https://github.com/Azure/AKS/releases # az aks get-versions --location uksouth --output table variable "kubernetes_version" { - default = "1.16.15" + default = "1.17.16" } # Helm charts @@ -21,20 +21,20 @@ variable "kubernetes_version" { # helm search repo ingress-nginx/ingress-nginx # * also update terraform/helm/nginx_values.yaml variable "nginx_chart_version" { - default = "3.20.1" + default = "3.23.0" } # https://hub.helm.sh/charts/jetstack/cert-manager # helm search repo jetstack/cert-manager variable "cert_manager_chart_version" { - default = "v1.1.0" + default = "v1.2.0" } # https://github.com/vmware-tanzu/helm-charts/releases # helm search repo vmware-tanzu/velero # * also update terraform/helm/velero_values.yaml variable "velero_chart_version" { - default = "2.14.5" + default = "2.14.8" } # https://hub.docker.com/r/sonatype/nexus3/tags @@ -54,31 +54,31 @@ variable "nexus_chart_version" { # https://github.com/SparebankenVest/public-helm-charts/blob/master/stable/akv2k8s/Chart.yaml#L5 # helm search repo spv-charts/akv2k8s variable "akv2k8s_chart_version" { - default = "1.1.26" + default = "1.1.28" } # https://github.com/Azure/aad-pod-identity/blob/master/charts/aad-pod-identity/Chart.yaml#L4 # helm search repo aad-pod-identity/aad-pod-identity variable "aad_pod_identity_chart_version" { - default = "3.0.0" + default = "3.0.3" } # https://bitnami.com/stack/external-dns/helm # https://github.com/bitnami/charts/blob/master/bitnami/external-dns/Chart.yaml#L21 # helm search repo bitnami/external-dns variable "external_dns_chart_version" { - default = "4.5.3" + default = "4.8.0" } # https://github.com/weaveworks/kured/tree/master/charts/kured # helm search repo kured/kured variable "kured_chart_version" { - default = "2.3.1" + default = "2.3.2" } # https://github.com/weaveworks/kured#kubernetes--os-compatibility variable "kured_image_tag" { - default = "1.4.4" + default = "1.5.1" } @@ -86,12 +86,12 @@ variable "kured_image_tag" { # https://github.com/argoproj/argo-helm/blob/master/charts/argo-cd/Chart.yaml#L5 # helm search repo argo/argo-cd variable "argocd_chart_version" { - default = "2.11.0" + default = "2.14.6" } # https://hub.docker.com/r/argoproj/argocd/tags variable "argocd_image_tag" { - default = "v1.8.2" + default = "v1.8.4" } #endregion Versions @@ -150,8 +150,8 @@ variable "azurerm_kubernetes_cluster_name" { default = "__AKS_CLUSTER_NAME__" } -variable "aad_group_name" { - description = "Name of the Azure AD group for cluster-admin access" +variable "aks_admins_aad_group_name" { + description = "Name an existing Azure AD group for AKS admins" type = string default = "AKS-Admins" } @@ -349,13 +349,16 @@ __HELM_CHART_REPO_DEPLOY_PRIVATE_KEY__ EOT } - +variable "argocd_apps_path" { + default = "files/argocd-apps.yaml" +} # gitlab variable "gitlab_cert_sync_yaml_path" { default = "files/gitlab-akvs-certificate-sync.yaml" } -variable "gitlab_argocd_app_path" { - default = "files/argocd-gitlab.yaml" -} +# TODO: remove if no longer required +# variable "gitlab_argocd_app_path" { +# default = "files/argocd-gitlab.yaml" +# } diff --git a/terraform/velero.tf b/terraform/velero.tf index 5678f06..9e9b554 100644 --- a/terraform/velero.tf +++ b/terraform/velero.tf @@ -78,14 +78,6 @@ EOT depends_on = [kubernetes_namespace.velero] } -# Manually test new values: -# helm upgrade \ -# velero vmware-tanzu/velero \ -# --install --atomic \ -# --namespace velero \ -# --reuse-values \ -# -f terraform/helm/velero_values.yaml - resource "helm_release" "velero" { count = var.velero_enabled ? 1 : 0 chart = "velero"