diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 08b3c55..12c5db1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,62 +1,86 @@ name: CI on: + push: + branches: + - main + pull_request: + branches: + - main workflow_dispatch: jobs: - terraform: - name: 'Terraform' + terraform-fmt-check: + if: github.event_name == 'pull_request' + defaults: + run: + working-directory: "./Terraform-AZURE-Services-Creation/AKS" + name: Terraform + environment: production + runs-on: ubuntu-latest + permissions: + contents: write env: ARM_CLIENT_ID: ${{ secrets.AZURE_AD_CLIENT_ID }} ARM_CLIENT_SECRET: ${{ secrets.AZURE_AD_CLIENT_SECRET }} ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} ARM_TENANT_ID: ${{ secrets.AZURE_AD_TENANT_ID }} - TF_VERSION: 1.5.7 - runs-on: ubuntu-latest - environment: production - - # Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest - defaults: - run: - shell: bash - + tf_resource_group_name: "thomasthorntoncloud" + tf_storage_account_name: "thomasthorntontfstate" + tf_state_container: "devopsthehardwaygithub" + tf_state_key: "terraform.tfstate" steps: - # Checkout the repository to the GitHub Actions runner - - name: Checkout + - name: Checkout Code uses: actions/checkout@v4 - - - name: 'Terraform Format' - uses: hashicorp/terraform-github-actions@master - with: - tf_actions_version: ${{ env.TF_VERSION }} - tf_actions_subcommand: 'fmt' - tf_actions_working_dir: "./Terraform-AZURE-Services-Creation/AKS" - - - name: 'Terraform Init' - uses: hashicorp/terraform-github-actions@master - with: - tf_actions_version: ${{ env.TF_VERSION }} - tf_actions_subcommand: 'init' - tf_actions_working_dir: "./Terraform-AZURE-Services-Creation/AKS" - - - name: 'Terraform Validate' - uses: hashicorp/terraform-github-actions@master + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 with: - tf_actions_version: ${{ env.TF_VERSION }} - tf_actions_subcommand: 'validate' - tf_actions_working_dir: "./Terraform-AZURE-Services-Creation/AKS" - - - name: 'Terraform Plan' - uses: hashicorp/terraform-github-actions@master + terraform_version: 1.7.4 + terraform_wrapper: true + + - name: Terraform Init + id: init + run: terraform init + env: + ARM_CLIENT_ID: ${{ secrets.AZURE_AD_CLIENT_ID }} + ARM_CLIENT_SECRET: ${{ secrets.AZURE_AD_CLIENT_SECRET }} + ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + ARM_TENANT_ID: ${{ secrets.AZURE_AD_TENANT_ID }} + working-directory: "./Terraform-AZURE-Services-Creation/AKS" + + - name: Terraform Fmt + id: fmt + run: terraform fmt + working-directory: "./Terraform-AZURE-Services-Creation/AKS" + + - name: Auto Commit Changes + uses: stefanzweifel/git-auto-commit-action@v5 with: - tf_actions_version: ${{ env.TF_VERSION }} - tf_actions_subcommand: 'plan' - tf_actions_working_dir: "./Terraform-AZURE-Services-Creation/AKS" - - - name: Terraform Apply + commit_message: "Terraform fmt" + file_pattern: "*.tf *.tfvars" + commit_user_name: "github-actions[bot]" + + - name: Terraform Plan + id: plan + run: terraform plan -no-color -input=false + env: + ARM_CLIENT_ID: ${{ secrets.AZURE_AD_CLIENT_ID }} + ARM_CLIENT_SECRET: ${{ secrets.AZURE_AD_CLIENT_SECRET }} + ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + ARM_TENANT_ID: ${{ secrets.AZURE_AD_TENANT_ID }} + DEPLOYMENT_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + working-directory: "./Terraform-AZURE-Services-Creation/AKS" + continue-on-error: false + + - name: Terraform Apply + id: apply + run: terraform apply -auto-approve -input=false if: github.ref == 'refs/heads/main' - uses: hashicorp/terraform-github-actions@master - with: - tf_actions_version: ${{ env.TF_VERSION }} - tf_actions_subcommand: 'apply' - tf_actions_working_dir: "./Terraform-AZURE-Services-Creation/AKS" \ No newline at end of file + env: + ARM_CLIENT_ID: ${{ secrets.AZURE_AD_CLIENT_ID }} + ARM_CLIENT_SECRET: ${{ secrets.AZURE_AD_CLIENT_SECRET }} + ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + ARM_TENANT_ID: ${{ secrets.AZURE_AD_TENANT_ID }} + working-directory: "./Terraform-AZURE-Services-Creation/AKS" + continue-on-error: false diff --git a/Azure/1-Configure-Terraform-Remote-Storage.md b/Azure/1-Configure-Terraform-Remote-Storage.md index aa0ad65..fa3657d 100644 --- a/Azure/1-Configure-Terraform-Remote-Storage.md +++ b/Azure/1-Configure-Terraform-Remote-Storage.md @@ -5,7 +5,7 @@ The purpose of this lab is to create the location that will store the remote Ter ## Create Blob Storage location for Terraform State file 1. Edit the [variables](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/blob/main/Azure/create-terraform-storage.sh#L3-L4) 2. Run the script `./create-terraform-storage.sh` -3. The script will create -- Azure Resource Group -- Azure Storage Account -- Azure Blob storage location within Azure Storage Account \ No newline at end of file +3. The script will: + - Create an Azure Resource Group + - Set up an Azure Storage Account + - Establish an Azure Blob storage location within the Azure Storage Account diff --git a/Docker/1-Create-Docker-Image.md b/Docker/1-Create-Docker-Image.md index 07109de..7922b6b 100644 --- a/Docker/1-Create-Docker-Image.md +++ b/Docker/1-Create-Docker-Image.md @@ -34,11 +34,11 @@ Now that the Docker image is created, you can run the container locally just to 1. To run the Docker container, run the following command: `docker run -tid uberapp` -- `t` stands for a TTY console -- `i` stands for interactive -- `d` stands for detach so your terminal isn't directly connected to the Docker container +- `t` enables a TTY console. +- `i` enables an interactive session. +- `d` detaches the terminal from the Docker container. -2. To confirm the Docker container is running, run the following command: +2. Confirm that the Docker container is running by running the following command: `docker container ls` -You should now see the container running. +You should now see the container running successfully. \ No newline at end of file diff --git a/Docker/2-Push Image To ACR.md b/Docker/2-Push Image To ACR.md index 005a752..9164c20 100644 --- a/Docker/2-Push Image To ACR.md +++ b/Docker/2-Push Image To ACR.md @@ -2,6 +2,9 @@ The ACR repo will be where you store the Docker image that you created on your local computer in step 1. +Ensure to replace `devopsthehardwayacr` with your ACR name and `v1` with the appropriate version tag if needed. + + ## Log Into The ECR Repository 1. Log in to ACR with Azure CLI `az acr login --name devopsthehardwayacr` @@ -14,3 +17,4 @@ The ACR repo will be where you store the Docker image that you created on your l ## Push The Docker Image To ACR 1. Push the Docker image to ACR `docker push devopsthehardwayacr.azurecr.io/uberapp:v1` + diff --git a/README.md b/README.md index 0ecdaae..0688d99 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # DevOps-The-Hard-Way-Azure -This tutorial contains a full, real-world solution for setting up an environment that is using DevOps technologies and practices for deploying apps and cloud services/cloud infrastructure to Azure. +Welcome to the DevOps-The-Hard-Way-Azure tutorial! This comprehensive guide provides a real-world solution for implementing DevOps practices and technologies to deploy applications and cloud services/infrastructure on Microsoft Azure. The repository contains free labs, documentation, diagrams, and docs for setting up an entire workflow and DevOps environment from a real-world perspective in Azure. @@ -9,8 +9,9 @@ The scenario that you're currently facing is you work in an organization that is You're brought in to the company and team to make things more modern so the organization can not only succeed, but stay ahead of their competition. Management now understands the needs and complexity that comes with staying ahead of their competition and they know that they need to. Otherwise, the organization will fall... + ## DevOps Solution -The solution is to deploy the Uber API for the sign-up page. Currently this solution is sitting on a bunch of baremetal, but it's time to sprinkle a little DevOps on it. +The solution is to deploy the Uber API for the sign-up page. Currently this solution is sitting on a bunch of baremetal, but it's time to sprinkle a little DevOps on it. Although we won't be coding the application itself, we'll cover the deployment process using DevOps tools and methodologies. ![](images/uber.png) @@ -21,26 +22,16 @@ As a DevOps Engineer, you're more or less (most likely) not writing the app, but https://github.com/AdminTurnedDevOps/Python-Sample-Application ## Technology Details -You will be using the following technologies and platforms to set up a DevOps environment. - -1. Azure - - Azure will be used to host the application, cloud infrastructure, and any other services we may need to ensure the Uber app is deployed properly. -2. GitHub - - To store the application and infrastructure/automation code -3. Python - - Python will be used for the Uber app (it is written in Python) and some automation efforts that aren't in Terraform. -4. Terraform - - Create an Azure ACR repository with Terraform - - Create an AKS cluster -5. Docker - - Create a Docker image - - Store the Docker image in Azure ACR -6. Kubernetes - - To run the Docker image that's created for the containerized Uber app. Kubernetes, in this case, AKS, will be used to orchestrate the container. -7. CI/CD - - Use GitHub Action to create an AKS cluster -8. Automated testing - - Testing Terraform code with Checkov +Get ready to utilise a range of cutting-edge technologies and platforms to establish your DevOps environment: + +1. Azure: Hosts the application, cloud infrastructure, and necessary services. +2. GitHub: Stores application and infrastructure/automation code. +3. Python: Powers the Uber application and some automation tasks. +4. Terraform: Orchestrates Azure resources, including Azure Container Registry (ACR) and Azure Kubernetes Service (AKS). +5. Docker: Creates containerized images for the Uber app and stores them in Azure ACR. +6. Kubernetes: Orchestrates Docker containers, with AKS managing our Kubernetes clusters. +7. CI/CD: Automates deployment using GitHub Actions. +8. Automated Testing: Ensures Terraform code integrity using Checkov. ## Labs 1. [Prerequisites](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/blob/main/prerequisites.md) @@ -67,3 +58,10 @@ In this scenario, the Terraform State file will be stored in remote state locati - [Install And Run Checkov](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/blob/main/Terraform-Static-Code-Analysis/1-Checkov-For-Terraform.md) 6. CICD - The purpose of this section is to automatically create an AKS cluster with CICD using GitHub Actions - [Create a GitHub Actions CICD pipeline](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/blob/main/Terraform-AZURE-Services-Creation/5-Run-CICD-For-AKS-Cluster.md) + +## Conclusion +By following this tutorial, you'll not only deploy an example app on Azure but also gain valuable insights into modern DevOps practices and tools. + +Let's embark on this journey to transform your organization into a lean, agile, and competitive force in the digital landscape. Happy deploying! 🚀🔧 + + diff --git a/Terraform-AZURE-Services-Creation/1-Create-ACR.md b/Terraform-AZURE-Services-Creation/1-Create-ACR.md index 43cfaff..f5f89eb 100644 --- a/Terraform-AZURE-Services-Creation/1-Create-ACR.md +++ b/Terraform-AZURE-Services-Creation/1-Create-ACR.md @@ -1,12 +1,14 @@ # Create an Azure Container Registry Repository +Before proceeding, ensure that the values in the terraform.tfvars file are accurate for your environment. You may need to customize these values to match your specific configuration. + In this lab you will create a repository to store the Docker image that you created for the Uber app. ## Create the ACR Terraform Configuration 1. You can find the Terraform configuration for Azure Container Registry (ACR) [here](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/tree/main/Terraform-AZURE-Services-Creation/ACR). The Terraform configuration files are used to create a repository in Azure Container Registry (ACR). -The Terraform `main.tf` will do a few things: +The Terraform `acr.tf` will: - Use a Terraform backend to store the `.tfstate` in an Azure Storage Account - Use the `uksouth` region, but feel free to change that if you'd like - Use the `azurerm_resource_group` Terraform resource to create a new Resource Group. diff --git a/Terraform-AZURE-Services-Creation/2-Create-VNET.md b/Terraform-AZURE-Services-Creation/2-Create-VNET.md index 9811ffb..2ee26db 100644 --- a/Terraform-AZURE-Services-Creation/2-Create-VNET.md +++ b/Terraform-AZURE-Services-Creation/2-Create-VNET.md @@ -1,17 +1,33 @@ # Create an Azure VNET -In this lab you will create a Virtual Network (VNET) that will be used to deploy your AKS instance into +Before proceeding, ensure that the values in the terraform.tfvars file are accurate for your environment. You may need to customize these values to match your specific configuration. + +In this lab you will: +- Create a Virtual Network (VNET) that will be used to deploy your AKS instance into +- Create a Network Security Group (NSG) and assign to the relevant subnets +- Create an Azure Application Gateway for Containers and associate it with the VNET ## Create the Azure VNET Terraform Configuration 1. You can find the Terraform configuration for Azure Virtual Network [here](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/tree/main/Terraform-AZURE-Services-Creation/VNET). The Terraform configuration files are used to create an Azure Vitual Network. -The Terraform `main.tf` will do a few things: +The Terraform `vnet.tf` will: +- Use a Terraform backend to store the `.tfstate` in an Azure Storage Account - Use the `azurerm_virtual_network` Terraform resource to create a VNET. - Use the `azurerm_subnet` Terraform resource to create relevant subnets. - Use the `uksouth` region, but feel free to change that if you'd like -2. Create the VNET by running the following: +The Terraform `nsg.tf` will: +- Use the `azurerm_network_security_group` Terraform resource to create a NSG. +- Use the `azurerm_subnet_network_security_group_association` Terraform resource to associate the NSG to the relevant subnets. + +The Terraform `alb.tf` will: +- Use the `azurerm_application_load_balancer` Terraform resource to create an Azure Application Gateway for Containers. +- Use the `azurerm_application_load_balancer_subnet_association` Terraform resource to associate the Azure Application Gateway with the VNET. +- Use the `azurerm_application_load_balancer_frontend` Terraform resource to create a frontend for the Azure Application Gateway. + + +2. Create the VNET, NSG & Azure Application Gateway for Containers by running the following: - `terraform init` - To initialize the working directory and pull down the provider - `terraform plan` - To go through a "check" and confirm the configurations are valid - `terraform apply` - To create the resource \ No newline at end of file diff --git a/Terraform-AZURE-Services-Creation/3-Create-Log-Analytics.md b/Terraform-AZURE-Services-Creation/3-Create-Log-Analytics.md index 83473c4..b7c0f1f 100644 --- a/Terraform-AZURE-Services-Creation/3-Create-Log-Analytics.md +++ b/Terraform-AZURE-Services-Creation/3-Create-Log-Analytics.md @@ -1,12 +1,15 @@ # Create an Azure Log Analytics Workspace +Before proceeding, ensure that the values in the terraform.tfvars file are accurate for your environment. You may need to customize these values to match your specific configuration. + In this lab you will create a Log Analytics workspace that will be used to view container insights of your AKS Cluster ## Create the Log Analytics Workspace Terraform Configuration 1. You can find the Terraform configuration for Log Analytics [here](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/tree/main/Terraform-AZURE-Services-Creation/Log-Analytics). The Terraform configuration files are used to create the Log Analytiocs workspace. -The Terraform `main.tf` will do a few things: +The Terraform `la.tf` will: +- Use a Terraform backend to store the `.tfstate` in an Azure Storage Account - Use the `azurerm_log_analytics_workspace` Terraform resource to create a Log Analytics workspace. - Use the `azurerm_log_analytics_solution` Terraform resource to enable the Log Analytics solution ContainerInsights. - Use the `uksouth` region, but feel free to change that if you'd like diff --git a/Terraform-AZURE-Services-Creation/4-Create-AKS-Cluster-IAM-Roles.md b/Terraform-AZURE-Services-Creation/4-Create-AKS-Cluster-IAM-Roles.md index f4da8aa..53f5ffc 100644 --- a/Terraform-AZURE-Services-Creation/4-Create-AKS-Cluster-IAM-Roles.md +++ b/Terraform-AZURE-Services-Creation/4-Create-AKS-Cluster-IAM-Roles.md @@ -1,5 +1,7 @@ # Create An AKS Cluster and IAM Roles +Before proceeding, ensure that the values in the terraform.tfvars file are accurate for your environment. You may need to customize these values to match your specific configuration. + In this lab you will create: - The AKS cluster - The appropriate IAM roles for AKS @@ -8,11 +10,19 @@ In this lab you will create: 1. You can find the Terraform configuration for AKS [here](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/tree/main/Terraform-AZURE-Services-Creation/AKS). The Terraform configuration files are used to create an AKS cluster and IAM Role/Policy for AKS. -The Terraform `main.tf` will do a few things: +The Terraform `aks.tf` will: - Use the `azurerm_kubernetes_cluster` Terraform resource to AKS Cluster - Use the `azurerm_role_assignment` Terraform resource to create the two neccessary role assignments - Use the `uksouth` region, but feel free to change that if you'd like +The Terraform `managed_identity.tf` will: +- Use the `azurerm_user_assigned_identity` Terraform resource to create a user assigned identity as part of the Azure Application Gateway for Containers setup +- Use the `azurerm_federated_identity_credential` Terraform resource to create a federated identity credential as part of the Azure Application Gateway for Containers setup + +The Terraform `rbac.tf` will: +- Use the `azurerm_role_assignment` Terraform resource to create the necessary role assignments for the AKS cluster +- Use the `azurerm_role_definition` Terraform resource to create the necessary role definitions for the AKS cluster + 2. In line 8 of `terraform.tfvars` replace the actual Azure AD Group ID you noted down [earlier](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/blob/main/Azure/2-Create-Azure-AD-Group-AKS-Admins.md) 3. Create the bucket by running the following: diff --git a/Terraform-AZURE-Services-Creation/5-Run-CICD-For-AKS-Cluster.md b/Terraform-AZURE-Services-Creation/5-Run-CICD-For-AKS-Cluster.md index a79dae3..27fb6b3 100644 --- a/Terraform-AZURE-Services-Creation/5-Run-CICD-For-AKS-Cluster.md +++ b/Terraform-AZURE-Services-Creation/5-Run-CICD-For-AKS-Cluster.md @@ -1,5 +1,7 @@ # Create AKS Cluster With CICD +Before proceeding, ensure that the values in the terraform.tfvars file are accurate for your environment. You may need to customize these values to match your specific configuration. + In this lab, you'll learn how to create an AKS cluster using GitHub Actions. The code can be found [here](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/tree/main/Terraform-AZURE-Services-Creation/AKS) @@ -33,16 +35,16 @@ Now that the secrets are created, it's time to create the pipeline. 1. Under the GitHub repository, click on the **Actions** tab 2. You will see a workflow already called `CI` -3. Select `CI` workflow and then select `Run workflow` `from` main branch +3. Select `CI` workflow and then select `Run workflow` `from` main branch (Also note, the pipeline is configured to run also during a Pull Request and or a Push to the main branch) The pipeline does a few things: -- On line 4, you'll see `workflow_dispatch`, which means the pipeline won't automatically run unless you kick it off. You can of course change this to have the pipeline automatically run if you, for example, push code to the `dev` or `main` branch. +- On line 10, you'll see `workflow_dispatch`, which means the pipeline won't automatically run unless you kick it off. You can of course change this to have the pipeline automatically run if you, for example, push code to the `dev` or `main` branch. - The code is checked-out - Authentication occurs to Azure - Terraform is set up -- Terraform format occurs +- Terraform format occurs & formats incorrect terraform, then pushes back into the branch - Terraform init occurs - Terraform plan occurs - Terraform apply occurs -4. Run the pipeline and watch as the pipeline automatically creates the AKS cluster +4. Run the pipeline and watch as the pipeline automatically create the AKS cluster diff --git a/Terraform-AZURE-Services-Creation/ACR/main.tf b/Terraform-AZURE-Services-Creation/ACR/acr.tf similarity index 93% rename from Terraform-AZURE-Services-Creation/ACR/main.tf rename to Terraform-AZURE-Services-Creation/ACR/acr.tf index 25b3c8c..3070dbd 100644 --- a/Terraform-AZURE-Services-Creation/ACR/main.tf +++ b/Terraform-AZURE-Services-Creation/ACR/acr.tf @@ -21,7 +21,7 @@ resource "azurerm_resource_group" "acr_resource_group" { } resource "azurerm_container_registry" "acr" { - name = "${var.name}acr" + name = "${var.name}tamopsacracr" resource_group_name = azurerm_resource_group.acr_resource_group.name location = azurerm_resource_group.acr_resource_group.location sku = "Standard" diff --git a/Terraform-AZURE-Services-Creation/AKS/aks.tf b/Terraform-AZURE-Services-Creation/AKS/aks.tf index 2eefe5d..0e581e0 100644 --- a/Terraform-AZURE-Services-Creation/AKS/aks.tf +++ b/Terraform-AZURE-Services-Creation/AKS/aks.tf @@ -1,25 +1,31 @@ terraform { required_version = ">= 1.5.7" backend "azurerm" { - resource_group_name = "devopshardway-rg" - storage_account_name = "devopshardwaysa" - container_name = "tfstate" - key = "aks-terraform.tfstate" + # resource_group_name = "devopshardway-rg" + # storage_account_name = "devopshardwaysa" + # container_name = "tfstate" + # key = "aks-terraform.tfstate" + resource_group_name = "thomasthorntoncloud" + storage_account_name = "thomasthorntontfstate" + container_name = "devopsthehardwaygithub" + key = "terraform.tfstate" } } + provider "azurerm" { features {} } resource "azurerm_kubernetes_cluster" "k8s" { - name = "${var.name}aks" - location = var.location - resource_group_name = data.azurerm_resource_group.resource_group.name - dns_prefix = "${var.name}dns" - kubernetes_version = var.kubernetes_version - - node_resource_group = "${var.name}-node-rg" + name = "${var.name}aks" + location = var.location + resource_group_name = data.azurerm_resource_group.resource_group.name + dns_prefix = "${var.name}dns" + kubernetes_version = var.kubernetes_version + oidc_issuer_enabled = true + workload_identity_enabled = true + node_resource_group = "${var.name}-node-rg" linux_profile { admin_username = "ubuntu" @@ -46,11 +52,6 @@ resource "azurerm_kubernetes_cluster" "k8s" { log_analytics_workspace_id = data.azurerm_log_analytics_workspace.workspace.id } - - ingress_application_gateway { - gateway_id = azurerm_application_gateway.aks.id - } - network_profile { load_balancer_sku = "standard" network_plugin = "azure" @@ -64,8 +65,4 @@ resource "azurerm_kubernetes_cluster" "k8s" { tags = var.tags - depends_on = [ - azurerm_application_gateway.aks - ] - -} +} \ No newline at end of file diff --git a/Terraform-AZURE-Services-Creation/AKS/application_gateway.tf b/Terraform-AZURE-Services-Creation/AKS/application_gateway.tf deleted file mode 100644 index 7f9d9a5..0000000 --- a/Terraform-AZURE-Services-Creation/AKS/application_gateway.tf +++ /dev/null @@ -1,76 +0,0 @@ -resource "azurerm_public_ip" "example" { - name = "appgateway-pip" - resource_group_name = data.azurerm_resource_group.resource_group.name - location = var.location - allocation_method = "Static" - sku = "Standard" -} - -# since these variables are re-used - a locals block makes this more maintainable -locals { - backend_address_pool_name = "aks-beap" - frontend_port_name = "aks-feport" - frontend_ip_configuration_name = "aks-feip" - http_setting_name = "aks-be-htst" - listener_name = "aks-httplstn" - request_routing_rule_name = "aks-rqrt" - redirect_configuration_name = "aks-rdrcfg" -} - -resource "azurerm_application_gateway" "aks" { - name = "aks-appgateway" - resource_group_name = data.azurerm_resource_group.resource_group.name - location = var.location - - sku { - name = "Standard_v2" - tier = "Standard_v2" - capacity = 2 - } - - gateway_ip_configuration { - name = "my-gateway-ip-configuration" - subnet_id = data.azurerm_subnet.appgwsubnet.id - } - - frontend_port { - name = local.frontend_port_name - port = 80 - } - - frontend_ip_configuration { - name = local.frontend_ip_configuration_name - public_ip_address_id = azurerm_public_ip.example.id - } - - backend_address_pool { - name = local.backend_address_pool_name - } - - backend_http_settings { - name = local.http_setting_name - cookie_based_affinity = "Disabled" - port = 80 - protocol = "Http" - request_timeout = 60 - } - - http_listener { - name = local.listener_name - frontend_ip_configuration_name = local.frontend_ip_configuration_name - frontend_port_name = local.frontend_port_name - protocol = "Http" - } - - request_routing_rule { - name = local.request_routing_rule_name - priority = 9 - rule_type = "Basic" - http_listener_name = local.listener_name - backend_address_pool_name = local.backend_address_pool_name - backend_http_settings_name = local.http_setting_name - } - - tags = var.tags - -} \ No newline at end of file diff --git a/Terraform-AZURE-Services-Creation/AKS/managed_identity.tf b/Terraform-AZURE-Services-Creation/AKS/managed_identity.tf new file mode 100644 index 0000000..04bf1a7 --- /dev/null +++ b/Terraform-AZURE-Services-Creation/AKS/managed_identity.tf @@ -0,0 +1,20 @@ +resource "azurerm_user_assigned_identity" "alb_identity" { + location = var.location + resource_group_name = data.azurerm_resource_group.resource_group.name + name = "azure-alb-identity" +} + +resource "azurerm_federated_identity_credential" "alb_federated_identity" { + name = "azure-alb-identity" + resource_group_name = data.azurerm_resource_group.resource_group.name + audience = ["api://AzureADTokenExchange"] + issuer = azurerm_kubernetes_cluster.k8s.oidc_issuer_url + parent_id = azurerm_user_assigned_identity.alb_identity.id + subject = "system:serviceaccount:azure-alb-system:alb-controller-sa" + + depends_on = [ + azurerm_user_assigned_identity.alb_identity, + azurerm_kubernetes_cluster.k8s + + ] +} \ No newline at end of file diff --git a/Terraform-AZURE-Services-Creation/AKS/rbac.tf b/Terraform-AZURE-Services-Creation/AKS/rbac.tf index 1841276..bbc1489 100644 --- a/Terraform-AZURE-Services-Creation/AKS/rbac.tf +++ b/Terraform-AZURE-Services-Creation/AKS/rbac.tf @@ -15,7 +15,7 @@ resource "azurerm_role_assignment" "node_infrastructure_update_scale_set" { } data "azurerm_container_registry" "acr" { - name = "${var.name}acr" + name = "${var.name}tamopsacr" resource_group_name = data.azurerm_resource_group.resource_group.name } @@ -28,14 +28,28 @@ resource "azurerm_role_assignment" "acr_pull" { ] } -# resource "azurerm_role_assignment" "appgateway_noderg_contributor" { -# scope = data.azurerm_resource_group.node_resource_group.id -# role_definition_name = "Contributor" -# principal_id = azurerm_kubernetes_cluster.k8s.ingress_application_gateway[0].ingress_application_gateway_identity[0].object_id -# } +#fixing for "The client '62119122-6287-4620-98b4-bf86535e2ece' with object id '62119122-6287-4620-98b4-bf86535e2ece' does not have authorization to perform action 'Microsoft.ServiceNetworking/register/action' over scope '/subscriptions/XXXXX' or the scope is invalid. (As part of App Gw for containers - maanged by ALB controller setup)" -resource "azurerm_role_assignment" "appgateway_aksrg_contributor" { +# Delegate AppGw for Containers Configuration Manager role to RG containing Application Gateway for Containers resource +# az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal --scope $resourceGroupId --role "fbc52c3f-28ad-4303-a892-8a056630b8f1" +resource "azurerm_role_assignment" "appgwcontainerfix2" { + principal_id = azurerm_user_assigned_identity.alb_identity.principal_id scope = data.azurerm_resource_group.resource_group.id - role_definition_name = "Contributor" - principal_id = azurerm_kubernetes_cluster.k8s.ingress_application_gateway[0].ingress_application_gateway_identity[0].object_id + role_definition_name = "AppGw for Containers Configuration Manager" + depends_on = [ + azurerm_kubernetes_cluster.k8s, + azurerm_user_assigned_identity.alb_identity + ] } + +# Delegate Network Contributor permission for join to association subnet +# az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal --scope $ALB_SUBNET_ID --role "4d97b98b-1d4f-4787-a291-c67834d212e7" +resource "azurerm_role_assignment" "appgwcontainerfix3" { + principal_id = azurerm_user_assigned_identity.alb_identity.principal_id + scope = data.azurerm_subnet.appgwsubnet.id + role_definition_name = "Network Contributor" + depends_on = [ + azurerm_kubernetes_cluster.k8s, + azurerm_user_assigned_identity.alb_identity + ] +} \ No newline at end of file diff --git a/Terraform-AZURE-Services-Creation/AKS/terraform.tfvars b/Terraform-AZURE-Services-Creation/AKS/terraform.tfvars index 0ba41d1..7e425dc 100644 --- a/Terraform-AZURE-Services-Creation/AKS/terraform.tfvars +++ b/Terraform-AZURE-Services-Creation/AKS/terraform.tfvars @@ -1,7 +1,7 @@ name = "devopsthehardway" location = "uksouth" -kubernetes_version = "1.28.3" +kubernetes_version = "1.28.5" agent_count = 3 vm_size = "Standard_DS2_v2" ssh_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDrt/GYkYpuQYRxM3lgjOr3Wqx8g5nQIbrg6Mr53wZGb35+ft+PibDMqxXZ7xq7fC3YuLnnO022IPgEjkF9fP03ZmfUeLjJJvw8YcutN9DD/2cx93BpKFPNUsqEB+za1iJ16kMsCojy35c1R64O+rw20D6iP96rmDAyIc5FR03y00eyAzQ8vo7/u9+VPwpdGEI7QCokZROcj6iNVz1V/1t6G4AEufPLokdj8J0gla/dN+tvnSLRQVBTDiD4jmVGImpWFqqKaH6R9SSXmRzj0uhvJUmSiZAZCb1caPEYgPEvNITuGQFdykPoY/4Z/3B+x/ipEQbWy8yL7bDFSXZTYhVKlPVyPbUtN5QFt7QtCtg84xDAZ6GA6AnONTtMxX2jvdzB9yh1ZsteNrOZ/Jo3ecuie573syQfG23Tu6qTqak8O7ZTOLY9iPx2ego3KvTWH/Q3lIvjnlpfCQtFtSgkNxjalMBk+NwwEgZHWRREOHwJmQIKVN0gSitN1KXobrqwxNk= tamops@Synth" diff --git a/Terraform-AZURE-Services-Creation/Log-Analytics/main.tf b/Terraform-AZURE-Services-Creation/Log-Analytics/la.tf similarity index 100% rename from Terraform-AZURE-Services-Creation/Log-Analytics/main.tf rename to Terraform-AZURE-Services-Creation/Log-Analytics/la.tf diff --git a/Terraform-AZURE-Services-Creation/VNET/alb.tf b/Terraform-AZURE-Services-Creation/VNET/alb.tf new file mode 100644 index 0000000..c21fb83 --- /dev/null +++ b/Terraform-AZURE-Services-Creation/VNET/alb.tf @@ -0,0 +1,19 @@ +# Azure Application Load Balancer for Containers +resource "azurerm_application_load_balancer" "alb" { + name = "devopsthehardway-alb" + location = var.location + resource_group_name = data.azurerm_resource_group.resource_group.name + + tags = var.tags +} + +resource "azurerm_application_load_balancer_subnet_association" "alb" { + name = "alb-subnet-association" + application_load_balancer_id = azurerm_application_load_balancer.alb.id + subnet_id = azurerm_subnet.app_gwsubnet.id +} + +resource "azurerm_application_load_balancer_frontend" "example" { + name = "alb-frontend" + application_load_balancer_id = azurerm_application_load_balancer.alb.id +} \ No newline at end of file diff --git a/Terraform-AZURE-Services-Creation/VNET/nsg.tf b/Terraform-AZURE-Services-Creation/VNET/nsg.tf new file mode 100644 index 0000000..5c7985d --- /dev/null +++ b/Terraform-AZURE-Services-Creation/VNET/nsg.tf @@ -0,0 +1,16 @@ +resource "azurerm_network_security_group" "nsg" { + name = "devopsthehardway-nsg" + location = var.location + resource_group_name = data.azurerm_resource_group.resource_group.name + tags = var.tags +} + +resource "azurerm_subnet_network_security_group_association" "aks_subnet" { + subnet_id = azurerm_subnet.aks_subnet.id + network_security_group_id = azurerm_network_security_group.nsg.id +} + +resource "azurerm_subnet_network_security_group_association" "app_gwsubnet" { + subnet_id = azurerm_subnet.app_gwsubnet.id + network_security_group_id = azurerm_network_security_group.nsg.id +} \ No newline at end of file diff --git a/Terraform-AZURE-Services-Creation/VNET/terraform.tfvars b/Terraform-AZURE-Services-Creation/VNET/terraform.tfvars index 859f4ce..3d621a3 100644 --- a/Terraform-AZURE-Services-Creation/VNET/terraform.tfvars +++ b/Terraform-AZURE-Services-Creation/VNET/terraform.tfvars @@ -5,3 +5,9 @@ aks_subnet_address_name = "aks" aks_subnet_address_prefix = "192.168.0.0/24" subnet_address_name = "appgw" subnet_address_prefix = "192.168.1.0/24" + +tags = { + "DeployedBy" = "Terraform" + "Environment" = "production" + "Project" = "devopsthehardway" +} \ No newline at end of file diff --git a/Terraform-AZURE-Services-Creation/VNET/main.tf b/Terraform-AZURE-Services-Creation/VNET/vnet.tf similarity index 83% rename from Terraform-AZURE-Services-Creation/VNET/main.tf rename to Terraform-AZURE-Services-Creation/VNET/vnet.tf index 2f5875d..257138f 100644 --- a/Terraform-AZURE-Services-Creation/VNET/main.tf +++ b/Terraform-AZURE-Services-Creation/VNET/vnet.tf @@ -22,7 +22,7 @@ resource "azurerm_virtual_network" "virtual_network" { resource_group_name = data.azurerm_resource_group.resource_group.name address_space = [var.network_address_space] - tags = var.tags + tags = var.tags } @@ -38,4 +38,14 @@ resource "azurerm_subnet" "app_gwsubnet" { resource_group_name = data.azurerm_resource_group.resource_group.name virtual_network_name = azurerm_virtual_network.virtual_network.name address_prefixes = [var.subnet_address_prefix] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.ServiceNetworking/trafficControllers" + actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"] + + } + } } \ No newline at end of file diff --git a/kubernetes_manifest/1-Connect-To-ACR.md b/kubernetes_manifest/1-Connect-To-ACR.md index f5d7209..e204cd5 100644 --- a/kubernetes_manifest/1-Connect-To-ACR.md +++ b/kubernetes_manifest/1-Connect-To-ACR.md @@ -1,13 +1,15 @@ -# Connecting To Elastic Kubernetes Service (AKS) +# Connecting To Azure Kubernetes Service (AKS) -When you're deploying locally, without any CI/CD to AKS, you'll need to authenticate from your local terminal. +When deploying locally to Azure Kubernetes Service (AKS) without employing any CI/CD pipelines, you'll need to authenticate from your local terminal. -Once you authenticate to AKS from your local terminal, a `kubeconfig` gets stored on your computer. The `kubeconfig` has all of the connection information and authentication needs to connect to AKS. +Upon successful authentication to AKS from your local terminal, a kubeconfig file is generated and stored on your computer. This kubeconfig file contains all the necessary connection information and authentication details required to access AKS. ## Connecting To AKS 1. Run the following command to connect to AKS: -`az aks get-credentials --resource-group devopsthehardway-rg --name devopsthehardwayaks` +`az aks get-credentials --resource-group devopsthehardway-rg --name devopsthehardwayaks --overwrite-existing` 2. Once connected, you should be able to run commands like the following to confirm you're connected: -`kubectl get nodes` \ No newline at end of file +`kubectl get nodes` + +Running this command should return information about the nodes in your AKS cluster, confirming your successful connection. \ No newline at end of file diff --git a/kubernetes_manifest/2-Create-Kubernetes-Manifest.md b/kubernetes_manifest/2-Create-Kubernetes-Manifest.md index 42544c0..01a2359 100644 --- a/kubernetes_manifest/2-Create-Kubernetes-Manifest.md +++ b/kubernetes_manifest/2-Create-Kubernetes-Manifest.md @@ -1,18 +1,13 @@ # Create The Kubernetes Manifest -At this point you have successfully created a Docker image from the Uber app and stored it in ACR. +Once you've successfully created a Docker image from the Uber app and stored it in Azure Container Registry (ACR), the next step is to configure the Kubernetes manifest for deploying the application to Azure Kubernetes Service (AKS). -Now it's time to set up the Kubernetes manifest, which will take the application and deploy it to AKS. +## Understanding the Manifest -## The Manifest +The Kubernetes manifest comprises three key components: -The Kubernetes manifest will consist of two components: -- The deployment -- The service -- The Application Gateway Ingress +- The Deployment: This component manages the application's deployment within Kubernetes. +- The Service: Responsible for exposing the Kubernetes application, allowing access from external sources such as load balancer hostnames or IPs. +- Namespace: A mechanism for organizing and isolating resources within a Kubernetes cluster, enabling resource scoping. -The deployment is what gets the application running in Kubernetes - -The service is what exposes the Kubernetes application so you can, for example, reach the frontend from a load balancer hostname or IP. - -The manifest `deployment.yml` can be found in the `kubernetes_manifest` directory. Ensure on line `31` to change the image URL to the one you have in your Azure account. \ No newline at end of file +The manifest `deployment.yml` can be found in the `kubernetes_manifest` directory. Ensure to update the image URL on line 39 to match the image stored in your Azure account before applying the manifest. \ No newline at end of file diff --git a/kubernetes_manifest/3-Deploy-Uber-App.md b/kubernetes_manifest/3-Deploy-Uber-App.md index f4b0593..b2d6ebc 100644 --- a/kubernetes_manifest/3-Deploy-Uber-App.md +++ b/kubernetes_manifest/3-Deploy-Uber-App.md @@ -1,20 +1,27 @@ # Deploy The Uber App -Once the AKS cluster is built and the Kubernetes manifest is ready, you're now ready to deploy the Kubernetes manifest. +Once the AKS cluster is provisioned and the Kubernetes manifest is prepared, it's time to deploy the Uber app. 1. `cd` into the `kubernetes_manifest` directory -2. Run the following command: +2. Run the following commands: +- Install ALB Controller (YModify values if necessary: [here](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/tree/main/kubernetes_manifest/scripts/1-install-alb-controller.sh#L3-8) ): +`./scripts/1-install-alb-controller.sh` +- Install Gateway API resources (Modify values if necessary: [here](https://github.com/thomast1906/DevOps-The-Hard-Way-Azure/tree/main/kubernetes_manifest/scripts/2-gateway-api-resources.sh#L1-3) ): +`./scripts/2-install-gateway-api.sh` +- Deploy the Uber app: `kubectl create -f deployment.yml` + You'll see an output that specifies the service and deployment was created. 3. Run the following command to confirm that the deployment was successful: `kubectl get deployments` -4. Access uber-ui via Application Gateway -`kubectl get ingress` +4. Access uber-ui via Azure Application Gateway Controller for Containers -`NAME CLASS HOSTS ADDRESS PORTS AGE -uber-ui * 20.90.236.246 80 11s` +`fqdn=$(kubectl get gateway gateway-01 -n alb-devopsthehardway -o jsonpath='{.status.addresses[0].value}') +echo "http://$fqdn" +` -Access the uber-ui using the address mentioned above, `http://20.90.236.246` \ No newline at end of file +Access the uber-ui using the address mentioned above, example: +`http://bye7fxhjesf7enf7.fz32.alb.azure.com` \ No newline at end of file diff --git a/kubernetes_manifest/deployment.yml b/kubernetes_manifest/deployment.yml index 163d6d9..7fc3923 100644 --- a/kubernetes_manifest/deployment.yml +++ b/kubernetes_manifest/deployment.yml @@ -1,7 +1,14 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: uber-ui +--- + apiVersion: v1 kind: Service metadata: name: uber-ui + namespace: uber-ui spec: selector: app: uber-ui @@ -16,6 +23,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: uber-ui + namespace: uber-ui spec: selector: matchLabels: @@ -28,24 +36,7 @@ spec: spec: containers: - name: uber-ui - image: devopsthehardwayacr.azurecr.io/uberapp:v1 + image: devopsthehardwaytamopsacr.azurecr.io/uberapp:v1 ports: - containerPort: 5000 ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: uber-ui - annotations: - kubernetes.io/ingress.class: azure/application-gateway -spec: - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: uber-ui - port: - number: 80 \ No newline at end of file +--- \ No newline at end of file diff --git a/kubernetes_manifest/scripts/1-alb-controller-install-k8s.sh b/kubernetes_manifest/scripts/1-alb-controller-install-k8s.sh new file mode 100755 index 0000000..46de95b --- /dev/null +++ b/kubernetes_manifest/scripts/1-alb-controller-install-k8s.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +RESOURCE_GROUP="devopsthehardway-rg" +AKS_NAME="devopsthehardwayaks" +helm_resource_namespace="azure-alb-system" +VNET_NAME="devopsthehardway-vnet" +ALB_SUBNET_NAME="appgw" +ALB_CONTROLLER_VERSION="1.0.0" + +#create namespace +kubectl create namespace $helm_resource_namespace + +# az aks get-credentials --resource-group $RESOURCE_GROUP --name $AKS_NAME +helm install alb-controller oci://mcr.microsoft.com/application-lb/charts/alb-controller --namespace $helm_resource_namespace --version $ALB_CONTROLLER_VERSION --set albController.namespace=$helm_resource_namespace --set albController.podIdentity.clientID=$(az identity show -g $RESOURCE_GROUP -n azure-alb-identity --query clientId -o tsv) diff --git a/kubernetes_manifest/scripts/2-gateway-api-resources.sh b/kubernetes_manifest/scripts/2-gateway-api-resources.sh new file mode 100755 index 0000000..ebdda12 --- /dev/null +++ b/kubernetes_manifest/scripts/2-gateway-api-resources.sh @@ -0,0 +1,44 @@ +RESOURCE_GROUP='devopsthehardway-rg' +ALB_RESOURCE_NAME='devopsthehardway-alb' +ALB_FRONTEND_NAME='alb-frontend' + +RESOURCE_ID=$(az network alb show --resource-group $RESOURCE_GROUP --name $ALB_RESOURCE_NAME --query id -o tsv) + +# Create a Gateway +kubectl apply -f - <