diff --git a/README.md b/README.md index 3f9e9f9..8e9218c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Costa Rica [![GitHub](https://img.shields.io/badge/--181717?logo=github&logoColor=ffffff)](https://github.com/) [brown9804](https://github.com/brown9804) -Last updated: 2025-02-21 +Last updated: 2025-04-15 ------------------------------------------ @@ -27,6 +27,80 @@ Last updated: 2025-02-21 +## Prerequisites + +- An `Azure subscription is required`. All other resources, including instructions for creating a Resource Group, are provided in this workshop. +- `Contributor role assigned or any custom role that allows`: access to manage all resources, and the ability to deploy resources within subscription. +- If you choose to use a Terraform approach, please ensure that: + - [Terraform is installed on your local machine](https://developer.hashicorp.com/terraform/tutorials/azure-get-started/install-cli#install-terraform). + - [Install the Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) to work with both Terraform and Azure commands. + +## Infrastructure as Code (IaC) + +> Is crucial for modern cloud-based solutions and applications. Here is why: + +
+1. Consistency and Reproducibility + + - **Consistent Environments**: IaC ensures that your development, testing, and production environments are consistent. `This reduces the it works on my machine problem` and ensures that applications run reliably across different environments. + - **Reproducibility**: With IaC, you can `recreate your infrastructure from scratch in a consistent manner.` This is particularly useful for `disaster recovery and scaling`. + +
+ +
+2. Version Control + + - **Source Control**: By storing IaC configurations in version control systems like GitHub, you `can track changes, collaborate with team members, and roll back to previous versions if needed.` + - **Change Management**: Version control `provides a history of changes, making it easier to understand what changes were made, who made them, and why.` + +
+ +
+3. Flexibility and Options + + +> Microsoft provides several IaC tools, including Terraform, Bicep, and ARM templates. Each tool offers different features and benefits, allowing you to choose the one that best fits your needs. + + - **Terraform**: A popular IaC tool that uses a high-level configuration language to define and provision infrastructure. It `supports multiple cloud providers, making it a versatile choice.` + - **Bicep**: A domain-specific language that uses declarative syntax to deploy Azure resources. It offers a `concise and easy-to-read alternative to JSON-based ARM templates.` + - **ARM Templates**: JSON files that` define the infrastructure and configuration for your Azure solution.` They provide a detailed and flexible way to manage Azure resources. + +
+ +
+4. Enhanced Security + +- **Automated Security Policies**: IaC allows you to `define and enforce security policies automatically.` This ensures that security best practices are `consistently applied across all environments.` +- **Compliance**: IaC helps maintain compliance with `regulatory requirements by providing a clear and auditable trail of infrastructure changes.` + +
+ +
+5. Scalability + +- **Dynamic Scaling**: IaC enables `dynamic scaling of resources based on demand.` This ensures that your infrastructure can handle varying workloads efficiently. +- **Resource Optimization**: By automating the `provisioning and de-provisioning of resources,` IaC helps optimize resource usage and reduce costs. + +
+ +
+6. Automation + +- **Automated Provisioning**: IaC allows you to `automate the provisioning of infrastructure. This reduces manual errors, speeds up deployments, and ensures that infrastructure changes are applied consistently.` +- **CI/CD Integration**: Integrating IaC with `Continuous Integration/Continuous Deployment (CI/CD) pipelines automates the deployment process, ensuring that infrastructure changes are tested and deployed alongside application code.` + +
+ + +> [!TIP] +> Just in case, find here some [additional Terraform templates for different Azure resources across different areas](https://github.com/MicrosoftCloudEssentials-LearningHub/AzureTerraformTemplates-v0.0.0). + +> E.g [Demonstration: Deploying Azure Resources for a Data Platform](./Terraform) + +
+ Centered Image +
+

Total Visitors

diff --git a/Terraform/README.md b/Terraform/README.md index 0e7f439..843cc90 100644 --- a/Terraform/README.md +++ b/Terraform/README.md @@ -5,7 +5,7 @@ Costa Rica [![GitHub](https://img.shields.io/badge/--181717?logo=github&logoColor=ffffff)](https://github.com/) [brown9804](https://github.com/brown9804) -Last updated: 2025-03-13 +Last updated: 2025-04-15 ------------------------------------------ @@ -34,6 +34,7 @@ Last updated: 2025-03-13 - [Overview](#overview) - [Finding admin_principal_id Using Azure CLI](#finding-admin_principal_id-using-azure-cli) +- [Configure Remote Storage for a Terraform deployment](#configure-remote-storage-for-a-terraform-deployment) - [How to execute it](#how-to-execute-it) @@ -78,6 +79,16 @@ Here is an example value for `admin_principal_id` which is Object ID you retriev admin_principal_id = "12345678-1234-1234-1234-1234567890ab" ``` +## Configure Remote Storage for a Terraform deployment + +> To configure remote storage for a Terraform deployment, you need to set up a backend configuration in your Terraform files. This backend configuration specifies where Terraform should store the state file, which keeps track of the resources it manages. + +> 1. Create an Azure Storage Account:
+> - Go to the Azure portal and create a new storage account (if you don't have one already).
+> - Note down the storage account name and the access key.
+> 2. Create a Storage Container: Within the storage account, create a new container to store the Terraform state file. +> 3. Configure Terraform Backend: In your Terraform configuration file (e.g., [remote-storage.tf](./src/remote-storage.tf), add the backend configuration for Azure Blob Storage. + ## How to execute it ```mermaid diff --git a/Terraform/microsoft-fabric-basic-resources.drawio b/Terraform/docs/microsoft-fabric-basic-resources.drawio similarity index 100% rename from Terraform/microsoft-fabric-basic-resources.drawio rename to Terraform/docs/microsoft-fabric-basic-resources.drawio diff --git a/Terraform/src/main.tf b/Terraform/src/main.tf new file mode 100644 index 0000000..a2be097 --- /dev/null +++ b/Terraform/src/main.tf @@ -0,0 +1,104 @@ +# Retrieve the client configuration of the AzureRM provider +data "azurerm_client_config" "example" {} + +# Check the directory object type +data "azuread_directory_object" "example" { + object_id = data.azurerm_client_config.example.object_id +} + +# Get information about the Entra user +data "azuread_user" "example" { + object_id = data.azurerm_client_config.example.object_id +} + +# Local value to determine if the client is a user or not +locals { + fabric_admin = can(data.azuread_directory_object.example.type == "User") ? data.azuread_user.example.user_principal_name : data.azurerm_client_config.example.object_id +} + +# Create a resource group +resource "azurerm_resource_group" "example" { + name = var.resource_group_name # Name of the resource group + location = var.location # Location of the resource group +} + +# Create a storage account for remote state +resource "azurerm_storage_account" "example" { + name = var.storage_account_name + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + account_tier = "Standard" + account_replication_type = "LRS" + depends_on = [azurerm_resource_group.example] # Ensure resource group is created first +} + +# Create a storage container for remote state +resource "azurerm_storage_container" "example" { + name = var.container_name + storage_account_name = azurerm_storage_account.example.name + container_access_type = "private" + depends_on = [azurerm_storage_account.example] # Ensure storage account is created first +} + +# Create an MSSQL Server +resource "azurerm_mssql_server" "example" { + name = var.sql_server_name # Name of the SQL Server + resource_group_name = azurerm_resource_group.example.name # Resource group name + location = azurerm_resource_group.example.location # Location of the SQL Server + version = "12.0" # SQL Server version + administrator_login = var.admin_username # Administrator username + administrator_login_password = var.admin_password # Administrator password + depends_on = [azurerm_resource_group.example] # Ensure resource group is created first +} + +# Add a null resource to introduce a delay +resource "null_resource" "wait_for_sql_server" { + depends_on = [azurerm_mssql_server.example] + + provisioner "local-exec" { + command = "Start-Sleep -Seconds 60" + interpreter = ["PowerShell", "-Command"] + } +} + +# Create an MSSQL Database +resource "azurerm_mssql_database" "example" { + name = var.sql_database_name # Name of the SQL Database + server_id = azurerm_mssql_server.example.id # ID of the SQL Server + sku_name = "Basic" # SKU name for the SQL Database + depends_on = [null_resource.wait_for_sql_server] # Ensure SQL Server is fully provisioned first +} + +# Create Microsoft Fabric Capacity +resource "azurerm_fabric_capacity" "example" { + name = "fc${var.solution_name}" + resource_group_name = azurerm_resource_group.example.name + location = var.location + + administration_members = setunion([local.fabric_admin], var.fabric_capacity_admin_upns) + + sku { + name = var.fabric_capacity_sku + tier = "Fabric" + } + depends_on = [azurerm_resource_group.example] # Ensure resource group is created first +} + +# Get the Fabric Capacity details +data "fabric_capacity" "example" { + display_name = azurerm_fabric_capacity.example.name + + lifecycle { + postcondition { + condition = self.state == "Active" + error_message = "Fabric Capacity is not in Active state. Please check the Fabric Capacity status." + } + } +} + +# Create a Fabric Workspace +resource "fabric_workspace" "example" { + capacity_id = data.fabric_capacity.example.id + display_name = "ws-${var.solution_name}" + depends_on = [data.fabric_capacity.example] # Ensure Fabric Capacity data source is available first +} diff --git a/Terraform/src/outputs.tf b/Terraform/src/outputs.tf new file mode 100644 index 0000000..41bc404 --- /dev/null +++ b/Terraform/src/outputs.tf @@ -0,0 +1,33 @@ +# Output the name of the resource group +output "resource_group_name" { + value = azurerm_resource_group.example.name # The name of the resource group +} + +# Output the name of the storage account +output "storage_account_name" { + value = azurerm_storage_account.example.name # The name of the storage account +} + +# Output the name of the storage container +output "storage_container_name" { + value = azurerm_storage_container.example.name # The name of the storage container +} + +# Output the name of the MSSQL Server +output "sql_server_name" { + value = azurerm_mssql_server.example.name # The name of the SQL Server +} + +# Output the name of the MSSQL Database +output "sql_database_name" { + value = azurerm_mssql_database.example.name # The name of the SQL Database +} + +# Output the name of the Microsoft Fabric Capacity +output "fabric_capacity_name" { + value = azurerm_fabric_capacity.example.name +} + +output "fabric_workspace_name" { + value = fabric_workspace.example.display_name +} diff --git a/Terraform/src/provider.tf b/Terraform/src/provider.tf new file mode 100644 index 0000000..b06d72d --- /dev/null +++ b/Terraform/src/provider.tf @@ -0,0 +1,21 @@ +terraform { + required_version = ">= 1.8, < 2.0" + # Specify the required providers and their versions + required_providers { + azurerm = { + source = "hashicorp/azurerm" # Source of the AzureRM provider + version = "~> 4.16.0" # Version of the AzureRM provider + } + fabric = { + source = "microsoft/fabric" + version = "0.1.0-beta.7" + } + } +} + +provider "azurerm" { + features {} # Enable all features for the AzureRM provider + subscription_id = var.subscription_id # Add your subscription ID here +} + +provider "fabric" {} diff --git a/Terraform/src/remote-storage.tf b/Terraform/src/remote-storage.tf new file mode 100644 index 0000000..49c8931 --- /dev/null +++ b/Terraform/src/remote-storage.tf @@ -0,0 +1,8 @@ +terraform { + backend "azurerm" { + resource_group_name = "RGWorkshopUserName" # The name of the resource group for remote state storage + storage_account_name = "examplestorageacctworkshop" # The name of the storage account for remote state storage + container_name = "tfstate" # The name of the container for remote state storage + key = "terraform.tfstate" # The key for the remote state file + } +} diff --git a/Terraform/src/terraform.tfvars b/Terraform/src/terraform.tfvars new file mode 100644 index 0000000..0a2575a --- /dev/null +++ b/Terraform/src/terraform.tfvars @@ -0,0 +1,32 @@ +# ---------------------------------- +### Here is some sample data. Please make any necessary modifications.  +# ---------------------------------- + +# Variable for subscription ID +subscription_id = "" # Add your subscription id number + +# Resource group name +resource_group_name = "" # Add your desired RG name + +# Location of the resources +location = "Central US" + +# SQL Server configuration +sql_server_name = "usernamesqlserver" # replace username with your name +admin_username = "adminuser" # replace with your desired user name +admin_password = "P@ssw0rd1234" # Make sure to use a strong password + +# SQL Database name +sql_database_name = "workshopdbusername" # replace username with your name + +# Variables for remote state storage +storage_account_name = "storageacctworkshop01" +container_name = "tfstate" + +# Variable for administrator principal ID +admin_principal_id = "" + +# Microsoft Fabric Capacity configuration +solution_name = "capacitynamews" # choose your capacity name +fabric_capacity_admin_upns = ["user-email-here", "another-user-email-here"] +fabric_capacity_sku = "F64" # Choose your SKU like F64 diff --git a/Terraform/src/variables.tf b/Terraform/src/variables.tf new file mode 100644 index 0000000..cc9f6ea --- /dev/null +++ b/Terraform/src/variables.tf @@ -0,0 +1,80 @@ +# Variable for the resource group name +variable "resource_group_name" { + description = "The name of the resource group" + type = string +} + +# Variable for the location of the resources +variable "location" { + description = "The location of the resources" + type = string + default = "East US" # Default location +} + +# Variable for the SQL Server name +variable "sql_server_name" { + description = "The name of the SQL Server" + type = string +} + +# Variable for the administrator username for the SQL Server +variable "admin_username" { + description = "The administrator username for the SQL Server" + type = string +} + +# Variable for the administrator password for the SQL Server +variable "admin_password" { + description = "The administrator password for the SQL Server" + type = string + sensitive = true # Mark this variable as sensitive +} + +# Variable for the SQL Database name +variable "sql_database_name" { + description = "The name of the SQL Database" + type = string +} + +# Variable for the storage account name +variable "storage_account_name" { + description = "The name of the storage account for remote state storage" + type = string +} + +# Variable for the container name +variable "container_name" { + description = "The name of the container for remote state storage" + type = string +} + +# Variable for the subscription ID +variable "subscription_id" { + description = "The subscription ID for the Azure account" + type = string +} + +# Variable for the administrator principal ID +variable "admin_principal_id" { + description = "The principal ID of the capacity administrator" + type = string +} + +# Variable for the SKU name for the Microsoft Fabric Capacity +variable "fabric_capacity_sku" { + description = "The SKU for the Fabric Capacity." + type = string +} + +# Variable for the solution name +variable "solution_name" { + description = "The name of the solution." + type = string +} + +# Variable for additional UPNs to be added as Fabric Capacity administrators +variable "fabric_capacity_admin_upns" { + description = "Additional UPNs to be added as Fabric Capacity administrators." + type = list(string) + default = [] +} diff --git a/Terraform/troubleshooting.md b/Terraform/troubleshooting.md new file mode 100644 index 0000000..a0182ee --- /dev/null +++ b/Terraform/troubleshooting.md @@ -0,0 +1,180 @@ +# Troubleshooting: Known Errors + +Costa Rica + +[![GitHub](https://img.shields.io/badge/--181717?logo=github&logoColor=ffffff)](https://github.com/) +[brown9804](https://github.com/brown9804) + +Last updated: 2025-04-15 + +------------------------------------------ + +## Content + +- [Terraform is not recognized](#terraform-is-not-recognized) + - [Step 1: Download Terraform](#step-1-download-terraform) + - [Step 2: Install Terraform](#step-2-install-terraform) + - [For Windows:](#for-windows) + - [For macOS:](#for-macos) + - [For Linux:](#for-linux) + - [Step 3: Verify the Installation](#step-3-verify-the-installation) + - [Step 4: Initialize Terraform](#step-4-initialize-terraform) +- [Resource Group Not Found](#resource-group-not-found) +- [Resource Not Found](#resource-not-found) + +## Terraform is not recognized + +> Terraform is not recognized because it's not installed or not added to your system's PATH + +``` +terraform : The term 'terraform' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the +spelling of the name, or if a path was included, verify that the path is correct and try again. +At line:1 char:1 ++ terraform init ++ ~~~~~~~~~ + + CategoryInfo : ObjectNotFound: (terraform:String) [], CommandNotFoundException + + FullyQualifiedErrorId : CommandNotFoundException +``` + +

+ image +

+ + +### Step 1: Download Terraform + +> By command line: +1. Open your command prompt. +2. Use curl to download Terraform. Replace VERSION with the desired version number (e.g., 1.1.4): + + ``` + curl -o terraform.zip https://releases.hashicorp.com/terraform/VERSION/terraform_VERSION_windows_amd64.zip + ``` + + image + +3. Use tar to extract the ZIP file: + + ``` + tar -xvf terraform.zip + ``` + + image + +> By GUI: +1. Go to the [Terraform download page](https://developer.hashicorp.com/terraform/install). +2. Download the appropriate package for your operating system (e.g., Windows, macOS, Linux). + + image + +### Step 2: Install Terraform + +#### For Windows: + +1. Extract the downloaded ZIP file to a directory of your choice (e.g., `C:\terraform`). + + image + +2. Add the directory to your system's PATH: + + > By command line:
`Assuming you have moved terraform.exe to C:\terraform, you can add this directory to the PATH using the following command` + + ``` + setx PATH "%PATH%;C:\terraform" + ``` + + + image + + + > By GUI: + - Open the Start menu and search for `Environment Variables`. + - Click on `Edit the system environment variables` + + image + + - In the System Properties window, click on `Environment Variables`. + + image + + - Under `System variables`, find the `Path` variable and click `Edit`. + - Click `New` and add the path to the directory where you extracted Terraform (e.g., `C:\terraform`). + - Click `OK` to close all windows. + +#### For macOS: + +1. Open a terminal. +2. Move the Terraform binary to a directory included in your PATH (e.g., `/usr/local/bin`): + ```sh + sudo mv ~/Downloads/terraform /usr/local/bin/ + ``` +3. Ensure the directory is in your PATH by adding the following line to your `~/.bash_profile` or `~/.zshrc` file: + ```sh + export PATH=$PATH:/usr/local/bin + ``` +4. Reload your profile: + ```sh + source ~/.bash_profile # or source ~/.zshrc + ``` + +#### For Linux: + +1. Open a terminal. +2. Move the Terraform binary to a directory included in your PATH (e.g., `/usr/local/bin`): + ```sh + sudo mv ~/Downloads/terraform /usr/local/bin/ + ``` +3. Ensure the directory is in your PATH by adding the following line to your `~/.bashrc` or `~/.profile` file: + ```sh + export PATH=$PATH:/usr/local/bin + ``` +4. Reload your profile: + ```sh + source ~/.bashrc # or source ~/.profile + ``` + +### Step 3: Verify the Installation +1. Open a new terminal or command prompt. +2. Run the following command to verify the installation. You should see the installed version of Terraform. + + ```sh + terraform -version + ``` + +### Step 4: Initialize Terraform + +Now you can run the `terraform init` command in your project directory: + +```sh +terraform init +``` + +## Resource Group Not Found + +> Please ensure you create your resource group before running the configuration and deployment. This error occurs because the specified resource group does not exist. + +``` +Error: Failed to get existing workspaces: Error retrieving keys for Storage Account "examplestorageacct": storage.AccountsClient#ListKeys: Failure responding to request: StatusCode=404 -- Original Error: autorest/azure: Service returned an error. Status=404 Code="ResourceGroupNotFound" Message="Resource group 'RGWorkshopUserName' could not be found." but I want to create it +``` + +

+ image +

+ +## Resource Not Found + +> Please ensure you create your storage account and container for backend before running the configuration and deployment. This error occurs because the specified storage account does not exist. + +``` +Error: Failed to get existing workspaces: Error retrieving keys for Storage Account "examplestorageacct": storage.AccountsClient#ListKeys: Failure responding to request: StatusCode=404 -- Original Error: autorest/azure: Service returned an error. Status=404 Code="ResourceNotFound" Message="The Resource 'Microsoft.Storage/storageAccounts/examplestorageacct' under resource group 'RGWorkshopUserName' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix" +``` + +

+ image +

+ + +
+

Total Visitors

+ Visitor Count +