diff --git a/samples/servicebus/README.md b/samples/servicebus/README.md index 3e7476e..cf6d4f8 100644 --- a/samples/servicebus/README.md +++ b/samples/servicebus/README.md @@ -1,31 +1,80 @@ -# Azure ServiceBus (Java SDK) +# Azure Service Bus with Spring Boot -This sample creates a minimal Spring Boot application that sends and receive messages via Azure ServiceBus. +This sample demonstrates a Java Spring Boot application that sends and receives messages via [Azure Service Bus](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-overview). The application uses the [Spring Cloud Azure Service Bus Stream Binder](https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/configure-spring-cloud-stream-binder-java-app-with-service-bus) to connect to a Service Bus queue, send a `Hello, World!` message, and receive it back, and then exits. -## Overview +> [!NOTE] +> At this time, the Azure Web Apps and Azure Function Apps emulators in LocalStack for Azure do not support Java applications. The Spring Boot sample application must be executed directly on the host machine and cannot be deployed to the emulator. - - **`app`**: A folder that contains the Java application - - **`scripts/deploy.sh`**: One script that provisions the required resources for this sample and runs the application - -## Quick Start +## Architecture + +The solution is composed of the following Azure resources: + +1. [Azure Resource Group](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-cli): A logical container scoping all resources in this sample. +2. [Azure Service Bus Namespace](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-overview): The messaging namespace that hosts the queue used by the application. +3. [Azure Service Bus Queue](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-queues-topics-subscriptions#queues): The `myqueue` queue used to send and receive messages. + +> **Note** +> The Java application currently runs on the host machine. In a future iteration, it will be deployed to an emulator-hosted web app. + +## Prerequisites + +- [Azure Subscription](https://azure.microsoft.com/free/) +- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) +- [Azlocal CLI](https://azure.localstack.cloud/user-guides/sdks/az/): LocalStack Azure CLI wrapper +- [Java 21+](https://learn.microsoft.com/en-us/java/openjdk/download) +- [Maven 3.8+](https://maven.apache.org/download.cgi) +- [Terraform](https://developer.hashicorp.com/terraform/downloads), if you plan to deploy the sample via Terraform. +- [Bicep extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-bicep), if you plan to deploy the sample via Bicep. + +## Deployment + +Set up the Azure emulator using the LocalStack for Azure Docker image. Before starting, ensure you have a valid `LOCALSTACK_AUTH_TOKEN` to access the Azure emulator. Refer to the [Auth Token guide](https://docs.localstack.cloud/getting-started/auth-token/) to obtain your Auth Token and set it in the `LOCALSTACK_AUTH_TOKEN` environment variable. The Azure Docker image is available on the [LocalStack Docker Hub](https://hub.docker.com/r/localstack/localstack-azure-alpha). To pull the image, execute: -To deploy the scenario against a LocalStack Emulator: ```bash -bash ./scripts/deploy.sh +docker pull localstack/localstack-azure-alpha ``` -The script will: - 1. Creates a ResourceGroup - 2. Creates a Servicebus Namespace - 3. Creates a Servicebus Queue - 4. Starts the Java app - - The app then: - 1. Connects to the configured ServiceBus - 2. Sends a message to a queue - 3. Receives the message, and shuts down the application - - After the application has shutdown, the script will then: - 5. Deletes the resource group with all of it's resources +Start the LocalStack Azure emulator by running: + +```bash +export LOCALSTACK_AUTH_TOKEN= +IMAGE_NAME=localstack/localstack-azure-alpha localstack start +``` + +Deploy the application to LocalStack for Azure using one of these methods: + +- [Azure CLI Deployment](./java/scripts/deploy.sh) +- [Bicep Deployment](./java/bicep/deploy.sh) +- [Terraform Deployment](./java/terraform/deploy.sh) + +All deployment methods have been fully tested against Azure and the LocalStack for Azure local emulator. + +> **Note** +> When you deploy the application to LocalStack for Azure for the first time, the initialization process involves downloading and building Docker images. This is a one-time operation—subsequent deployments will be significantly faster. Depending on your internet connection and system resources, this initial setup may take several minutes. + +## How It Works + +The deploy script performs the following steps: + +1. Creates a resource group. +2. Creates a Service Bus namespace. +3. Creates a Service Bus queue (`myqueue`). +4. Retrieves the namespace connection string and exports it as `AZURE_SERVICEBUS_CONNECTION_STRING`. +5. Starts the Spring Boot application via `mvn clean spring-boot:run`. + +The application then: + +1. Connects to the configured Service Bus namespace using the connection string. +2. Sends a `Hello, World!` message to the `myqueue` queue. +3. Receives the message via a `@ServiceBusListener` consumer. +4. Shuts down after receiving the message. + +## References + +- [Azure Service Bus Documentation](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-overview) +- [Spring Cloud Azure Service Bus](https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/configure-spring-cloud-stream-binder-java-app-with-service-bus) +- [Azure Service Bus Queues](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-queues-topics-subscriptions) +- [Spring Boot Starter for Azure Service Bus](https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/spring-cloud-azure) +- [LocalStack for Azure](https://azure.localstack.cloud/) diff --git a/samples/servicebus/java/bicep/README.md b/samples/servicebus/java/bicep/README.md new file mode 100644 index 0000000..eeff474 --- /dev/null +++ b/samples/servicebus/java/bicep/README.md @@ -0,0 +1,176 @@ +# Bicep Deployment + +This directory contains a Bicep template and a deployment script for provisioning Azure services in LocalStack for Azure. For further details about the sample application, refer to the [Azure Service Bus with Spring Boot](../../README.md). + +## Prerequisites + +Before deploying this solution, ensure you have the following tools installed: + +- [LocalStack for Azure](https://azure.localstack.cloud/): Local Azure cloud emulator for development and testing +- [Visual Studio Code](https://code.visualstudio.com/): Code editor installed on one of the [supported platforms](https://code.visualstudio.com/docs/supporting/requirements#_platforms) +- [Bicep extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-bicep): VS Code extension for Bicep language support and IntelliSense +- [Docker](https://docs.docker.com/get-docker/): Container runtime required for LocalStack +- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli): Azure command-line interface +- [Azlocal CLI](https://azure.localstack.cloud/user-guides/sdks/az/): LocalStack Azure CLI wrapper +- [Java 21+](https://learn.microsoft.com/en-us/java/openjdk/download): Java runtime for compiling and running the sample application +- [Maven 3.8+](https://maven.apache.org/download.cgi): Build tool for managing Java project dependencies and compilation +- [jq](https://jqlang.org/): JSON processor for scripting and parsing command outputs + +### Installing azlocal CLI + +The [deploy.sh](deploy.sh) Bash script uses the `azlocal` CLI instead of the standard Azure CLI to work with LocalStack. Install it using: + +```bash +pip install azlocal +``` + +For more information, see [Get started with the az tool on LocalStack](https://azure.localstack.cloud/user-guides/sdks/az/). + +## Architecture Overview + +The [deploy.sh](deploy.sh) script creates the [Azure Resource Group](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-cli), while the Bicep template creates the following Azure resources: + +1. [Azure Resource Group](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-cli): A logical container scoping all resources in this sample. +2. [Azure Service Bus Namespace](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-overview): The messaging namespace that hosts the queue used by the application. +3. [Azure Service Bus Queue](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-queues-topics-subscriptions#queues): The `myqueue` queue used to send and receive messages. + +The Spring Boot sample application connects to the Service Bus namespace, sends a test message to the sample queue, receives it back, and exits. For more information on the sample application, see [Azure Service Bus with Spring Boot](../../README.md). + +## Configuration + +Before deploying the `main.bicep` template, update the [main.bicepparam](main.bicepparam) file with your specific values: + +```bicep +using 'main.bicep' + +param queueName = 'myqueue' +param zoneRedundant = false +param tags = { + environment: 'test' + iac: 'bicep' +} +``` + +## Provisioning Scripts + +You can use the [deploy.sh](deploy.sh) script to automate the deployment of all Azure resources and the sample application in a single step, streamlining setup and reducing manual configuration. The script executes the following steps: + +- Detects environment (LocalStack vs Azure Cloud) and uses the appropriate CLI. +- Creates the resource group if it doesn't exist. +- Optionally validates the Bicep template. +- Optionally runs a what-if deployment for preview. +- Deploys the `main.bicep` template with parameters from [main.bicepparam](main.bicepparam). +- Extracts the Service Bus namespace name from deployment outputs. +- Retrieves the namespace connection string and exports it as `AZURE_SERVICEBUS_CONNECTION_STRING`. +- Compiles the Spring Boot project and runs the app on the host machine. + +## Deployment + +You can set up the Azure emulator by utilizing LocalStack for Azure Docker image. Before starting, ensure you have a valid `LOCALSTACK_AUTH_TOKEN` to access the Azure emulator. Refer to the [Auth Token guide](https://docs.localstack.cloud/getting-started/auth-token/) to obtain your Auth Token and specify it in the `LOCALSTACK_AUTH_TOKEN` environment variable. The Azure Docker image is available on the [LocalStack Docker Hub](https://hub.docker.com/r/localstack/localstack-azure-alpha). To pull the Azure Docker image, execute the following command: + +```bash +docker pull localstack/localstack-azure-alpha +``` + +Start the LocalStack Azure emulator using the localstack CLI, execute the following command: + +```bash +export LOCALSTACK_AUTH_TOKEN= +IMAGE_NAME=localstack/localstack-azure-alpha localstack start +``` + +Navigate to the `bicep` folder: + +```bash +cd samples/servicebus/java/bicep +``` + +Make the script executable: + +```bash +chmod +x deploy.sh +``` + +Run the deployment script: + +```bash +./deploy.sh +``` + +## Validation + +Once the deployment completes, run the [validate.sh](../scripts/validate.sh) script to confirm that all resources were provisioned and configured as expected: + +```bash +#!/bin/bash + +# Variables +PREFIX='local' +SUFFIX='test' +RESOURCE_GROUP_NAME="${PREFIX}-rg" +SERVICEBUS_NAMESPACE_NAME="${PREFIX}-sb-ns-${SUFFIX}" +SERVICEBUS_QUEUE_NAME="myqueue" +ENVIRONMENT=$(az account show --query environmentName --output tsv) + +# Choose the appropriate CLI based on the environment +if [[ $ENVIRONMENT == "LocalStack" ]]; then + echo "Using azlocal for LocalStack emulator environment." + AZ="azlocal" +else + echo "Using standard az for AzureCloud environment." + AZ="az" +fi + +# Check resource group +echo -e "[$RESOURCE_GROUP_NAME] resource group:\n" +$AZ group show \ + --name "$RESOURCE_GROUP_NAME" \ + --output table \ + --only-show-errors + +# Check Service Bus namespace +echo -e "\n[$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace:\n" +$AZ servicebus namespace show \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --name "$SERVICEBUS_NAMESPACE_NAME" \ + --query "{name:name, location:location, serviceBusEndpoint:serviceBusEndpoint, status:provisioningState}" \ + --output table \ + --only-show-errors + +# Check Service Bus queue +echo -e "\n[$SERVICEBUS_QUEUE_NAME] Service Bus queue:\n" +$AZ servicebus queue show \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --namespace-name "$SERVICEBUS_NAMESPACE_NAME" \ + --name "$SERVICEBUS_QUEUE_NAME" \ + --query "{name:name, messageCount:messageCount, sizeInBytes:sizeInBytes}" \ + --output table \ + --only-show-errors +``` + +## Cleanup + +To destroy all created resources: + +```bash +#!/bin/bash + +# Variables +PREFIX='local' +SUFFIX='test' +RESOURCE_GROUP_NAME="${PREFIX}-rg" + +# Delete resource group and all contained resources +az group delete --name $RESOURCE_GROUP_NAME --yes --no-wait + +# Verify deletion +az group list --output table +``` + +This will remove all Azure resources created by the Bicep deployment script. + +## Related Documentation + +- [Azure Bicep Documentation](https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/) +- [Bicep Language Reference](https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions) +- [LocalStack for Azure Documentation](https://azure.localstack.cloud/) \ No newline at end of file diff --git a/samples/servicebus/java/bicep/deploy.sh b/samples/servicebus/java/bicep/deploy.sh new file mode 100755 index 0000000..e25dfa7 --- /dev/null +++ b/samples/servicebus/java/bicep/deploy.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +# Variables +PREFIX='local' +SUFFIX='test' +TEMPLATE="main.bicep" +PARAMETERS="main.bicepparam" +RESOURCE_GROUP_NAME="${PREFIX}-rg" +SERVICEBUS_QUEUE_NAME="myqueue" # Queue name is hardcoded in the application properties +LOCATION="westeurope" +VALIDATE_TEMPLATE=1 +USE_WHAT_IF=0 +SUBSCRIPTION_NAME=$(az account show --query name --output tsv) +CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" +ENVIRONMENT=$(az account show --query environmentName --output tsv) + +# Change the current directory to the script's directory +cd "$CURRENT_DIR" || exit + +# Choose the appropriate CLI based on the environment +if [[ $ENVIRONMENT == "LocalStack" ]]; then + echo "Using azlocal for LocalStack emulator environment." + AZ="azlocal" +else + echo "Using standard az for AzureCloud environment." + AZ="az" +fi + +# Validates if the resource group exists in the subscription, if not creates it +echo "Checking if resource group [$RESOURCE_GROUP_NAME] exists in the subscription [$SUBSCRIPTION_NAME]..." +$AZ group show --name $RESOURCE_GROUP_NAME &>/dev/null + +if [[ $? != 0 ]]; then + echo "No resource group [$RESOURCE_GROUP_NAME] exists in the subscription [$SUBSCRIPTION_NAME]" + echo "Creating resource group [$RESOURCE_GROUP_NAME] in the subscription [$SUBSCRIPTION_NAME]..." + + # Create the resource group + $AZ group create \ + --name $RESOURCE_GROUP_NAME \ + --location $LOCATION \ + --only-show-errors 1> /dev/null + + if [[ $? == 0 ]]; then + echo "Resource group [$RESOURCE_GROUP_NAME] successfully created in the subscription [$SUBSCRIPTION_NAME]" + else + echo "Failed to create resource group [$RESOURCE_GROUP_NAME] in the subscription [$SUBSCRIPTION_NAME]" + exit + fi +else + echo "Resource group [$RESOURCE_GROUP_NAME] already exists in the subscription [$SUBSCRIPTION_NAME]" +fi + +# Validates the Bicep template +if [[ $VALIDATE_TEMPLATE == 1 ]]; then + if [[ $USE_WHAT_IF == 1 ]]; then + # Execute a deployment What-If operation at resource group scope. + echo "Previewing changes deployed by Bicep template [$TEMPLATE]..." + $AZ deployment group what-if \ + --resource-group $RESOURCE_GROUP_NAME \ + --template-file $TEMPLATE \ + --parameters $PARAMETERS \ + --parameters location=$LOCATION \ + prefix=$PREFIX \ + suffix=$SUFFIX \ + queueName=$SERVICEBUS_QUEUE_NAME \ + --only-show-errors + + if [[ $? == 0 ]]; then + echo "Bicep template [$TEMPLATE] validation succeeded" + else + echo "Failed to validate Bicep template [$TEMPLATE]" + exit + fi + else + # Validate the Bicep template + echo "Validating Bicep template [$TEMPLATE]..." + output=$($AZ deployment group validate \ + --resource-group $RESOURCE_GROUP_NAME \ + --template-file $TEMPLATE \ + --parameters $PARAMETERS \ + --parameters location=$LOCATION \ + prefix=$PREFIX \ + suffix=$SUFFIX \ + queueName=$SERVICEBUS_QUEUE_NAME \ + --only-show-errors) + + if [[ $? == 0 ]]; then + echo "Bicep template [$TEMPLATE] validation succeeded" + else + echo "Failed to validate Bicep template [$TEMPLATE]" + echo "$output" + exit + fi + fi +fi + +# Deploy the Bicep template +echo "Deploying Bicep template [$TEMPLATE]..." +if DEPLOYMENT_OUTPUTS=$($AZ deployment group create \ + --resource-group $RESOURCE_GROUP_NAME \ + --only-show-errors \ + --template-file $TEMPLATE \ + --parameters $PARAMETERS \ + --parameters location=$LOCATION \ + prefix=$PREFIX \ + suffix=$SUFFIX \ + queueName=$SERVICEBUS_QUEUE_NAME \ + --output json \ + --query 'properties.outputs'); then + # Extract only the JSON portion (everything from first { to the end) + DEPLOYMENT_JSON=$(echo "$DEPLOYMENT_OUTPUTS" | sed -n '/{/,$ p') + echo "Bicep template [$TEMPLATE] deployed successfully. Outputs:" + echo "$DEPLOYMENT_JSON" | jq . + SERVICEBUS_NAMESPACE_NAME=$(echo "$DEPLOYMENT_JSON" | jq -r '.name.value') + echo "Deployment details:" + echo "Service Bus Namespace Name: $SERVICEBUS_NAMESPACE_NAME" +else + echo "Failed to deploy Bicep template [$TEMPLATE]" + exit 1 +fi + +if [[ -z "$SERVICEBUS_NAMESPACE_NAME" ]]; then + echo "Service Bus Namespace Name is empty. Exiting." + exit 1 +fi + +# Retrieve the connection string for the Service Bus namespace +echo "Retrieving connection string for [$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace..." +AZURE_SERVICEBUS_CONNECTION_STRING=$($AZ servicebus namespace authorization-rule keys list \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --namespace-name "$SERVICEBUS_NAMESPACE_NAME" \ + --name RootManageSharedAccessKey \ + --query primaryConnectionString \ + --output tsv) + +if [[ $? -eq 0 ]] && [[ -n "$AZURE_SERVICEBUS_CONNECTION_STRING" ]]; then + export AZURE_SERVICEBUS_CONNECTION_STRING + echo "Connection string retrieved successfully." +else + echo "Failed to retrieve connection string." + exit 1 +fi + +# Start the Java application +echo "Starting Java application..." +cd "$CURRENT_DIR/../app" && mvn clean spring-boot:run + +# Optionally tear down all resources (uncomment to enable) +# echo "Deleting resource group [$RESOURCE_GROUP_NAME]..." +# $AZ group delete --name "$RESOURCE_GROUP_NAME" --yes diff --git a/samples/servicebus/java/bicep/main.bicep b/samples/servicebus/java/bicep/main.bicep new file mode 100644 index 0000000..6cc84a9 --- /dev/null +++ b/samples/servicebus/java/bicep/main.bicep @@ -0,0 +1,108 @@ +//******************************************** +// Parameters +//******************************************** +@description('Specifies the prefix for the name of the Azure resources.') +@minLength(2) +param prefix string = take(uniqueString(resourceGroup().id), 4) + +@description('Specifies the suffix for the name of the Azure resources.') +@minLength(2) +param suffix string = take(uniqueString(resourceGroup().id), 4) + +@description('Enabling this property creates a Premium Service Bus Namespace in regions supported availability zones.') +param zoneRedundant bool = true + +@description('Specifies the name of Service Bus namespace SKU.') +@allowed([ + 'Basic' + 'Premium' + 'Standard' +]) +param skuName string = 'Standard' + +@description('Specifies the messaging units for the Service Bus namespace. For Premium tier, capacity are 1,2 and 4.') +param capacity int = 1 + +@description('Specifies the name of the Service Bus queue.') +param queueName string + +@description('Specifies the lock duration of the queue.') +param lockDuration string = 'PT5M' + +@description('Specifies the maximum number of deliveries for a message.') +param maxDeliveryCount int = 10 + +@description('ISO 8601 timeSpan idle interval after which the topic is automatically deleted. The minimum duration is 5 minutes.') +param autoDeleteOnIdle string = 'P10675199DT2H48M5.4775807S' // Default to max value as per Azure docs + +@description('ISO 8601 default message time to live value. This is the duration after which the message expires, starting from when the message is sent to Service Bus. This is the default value used unless DefaultMessageTimeToLive is explicitly set on a message.') +param defaultMessageTimeToLive string = 'P10675199DT2H48M5.4775807S' // Default to max value + +@description('ISO 8601 duration of the duplicate detection history. The default value is 10 minutes.') +param duplicateDetectionHistoryTimeWindow string = 'PT10M' + +@description('Value that indicates whether operations for the topic are batched.') +param enableBatchedOperations bool = true + +@description('A value that indicates whether the topic has dead-lettering on message expiration. If true, messages that expire will be moved to the dead-letter sub-queue.') +param deadLetteringOnMessageExpiration bool = false + +@description('The maximum size of the topic in megabytes, which is the size of memory allocated for the topic. Default is 1024 MB (1 GB).') +param maxSizeInMegabytes int = 1024 + +@description('A value indicating if this topic requires duplicate detection. If value is true, then DuplicateDetectionHistoryTimeWindow will be required.') +param requiresDuplicateDetection bool = false + +@description('Specifies the location.') +param location string = resourceGroup().location + +@description('Specifies the resource tags.') +param tags object = {} + +//******************************************** +// Variables +//******************************************** +var namespaceName = '${prefix}-sb-ns-${suffix}' + +//******************************************** +// Resources +//******************************************** +resource namespace 'Microsoft.ServiceBus/namespaces@2024-01-01' = { + name: namespaceName + location: location + tags: tags + sku: { + name: skuName + capacity: capacity + } + properties: { + zoneRedundant: zoneRedundant + } +} + +resource queue 'Microsoft.ServiceBus/namespaces/queues@2024-01-01' = { + parent: namespace + name: queueName + properties: { + enableBatchedOperations: enableBatchedOperations + requiresDuplicateDetection: requiresDuplicateDetection + requiresSession: false + defaultMessageTimeToLive: defaultMessageTimeToLive + deadLetteringOnMessageExpiration: deadLetteringOnMessageExpiration + duplicateDetectionHistoryTimeWindow: duplicateDetectionHistoryTimeWindow + maxDeliveryCount: maxDeliveryCount + lockDuration: lockDuration + maxSizeInMegabytes: maxSizeInMegabytes + autoDeleteOnIdle: autoDeleteOnIdle + enablePartitioning: false + enableExpress: false + } +} + +//******************************************** +// Outputs +//******************************************** +output namespaceId string = namespace.id +output name string = namespace.name +output queueId string = queue.id +output queueName string = queue.name diff --git a/samples/servicebus/java/bicep/main.bicepparam b/samples/servicebus/java/bicep/main.bicepparam new file mode 100644 index 0000000..7aface7 --- /dev/null +++ b/samples/servicebus/java/bicep/main.bicepparam @@ -0,0 +1,8 @@ +using 'main.bicep' + +param queueName = 'myqueue' +param zoneRedundant = false +param tags = { + environment: 'test' + iac: 'bicep' +} diff --git a/samples/servicebus/java/scripts/README.md b/samples/servicebus/java/scripts/README.md new file mode 100644 index 0000000..25d6e85 --- /dev/null +++ b/samples/servicebus/java/scripts/README.md @@ -0,0 +1,158 @@ +# Azure CLI Deployment + +This directory contains Azure CLI scripts and a deployment script for provisioning Azure services in LocalStack for Azure. For further details about the sample application, refer to the [Azure Service Bus with Spring Boot](../../README.md). + +## Prerequisites + +Before deploying this solution, ensure you have the following tools installed: + +- [LocalStack for Azure](https://azure.localstack.cloud/): Local Azure cloud emulator for development and testing +- [Visual Studio Code](https://code.visualstudio.com/): Code editor installed on one of the [supported platforms](https://code.visualstudio.com/docs/supporting/requirements#_platforms) +- [Docker](https://docs.docker.com/get-docker/): Container runtime required for LocalStack +- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli): Azure command-line interface +- [Azlocal CLI](https://azure.localstack.cloud/user-guides/sdks/az/): LocalStack Azure CLI wrapper +- [Java 21+](https://learn.microsoft.com/en-us/java/openjdk/download): Java runtime for compiling and running the sample application +- [Maven 3.8+](https://maven.apache.org/download.cgi): Build tool for managing Java project dependencies and compilation +- [jq](https://jqlang.org/): JSON processor for scripting and parsing command outputs + +### Installing azlocal CLI + +The [deploy.sh](deploy.sh) Bash script uses the `azlocal` CLI instead of the standard Azure CLI to work with LocalStack. Install it using: + +```bash +pip install azlocal +``` + +For more information, see [Get started with the az tool on LocalStack](https://azure.localstack.cloud/user-guides/sdks/az/). + +## Architecture Overview + +The Azure CLI scripts create the following Azure resources: + +1. [Azure Resource Group](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-cli): A logical container scoping all resources in this sample. +2. [Azure Service Bus Namespace](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-overview): The messaging namespace that hosts the queue used by the application. +3. [Azure Service Bus Queue](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-queues-topics-subscriptions#queues): The `myqueue` queue used to send and receive messages. + +The Spring Boot sample application connects to the Service Bus namespace, sends a test message to the sample queue, receives it back, and exits. For more information on the sample application, see [Azure Service Bus with Spring Boot](../../README.md). + +## Provisioning Scripts + +You can use the [deploy.sh](deploy.sh) script to automate the deployment of all Azure resources and the sample application in a single step, streamlining setup and reducing manual configuration. The script executes the following steps: + +- Detects the environment (LocalStack vs Azure Cloud) and selects the appropriate CLI. +- Checks whether each resource (resource group, namespace, queue) already exists before creating it. +- Creates the Azure Resource Group in the specified location. +- Creates the Service Bus Namespace within the resource group. +- Creates the Service Bus Queue within the namespace. +- Retrieves the connection string for the Service Bus namespace. +- Compiles the Spring Boot project and runs the app on the host machine. + +## Deployment + +You can set up the Azure emulator by utilizing LocalStack for Azure Docker image. Before starting, ensure you have a valid `LOCALSTACK_AUTH_TOKEN` to access the Azure emulator. Refer to the [Auth Token guide](https://docs.localstack.cloud/getting-started/auth-token/) to obtain your Auth Token and specify it in the `LOCALSTACK_AUTH_TOKEN` environment variable. The Azure Docker image is available on the [LocalStack Docker Hub](https://hub.docker.com/r/localstack/localstack-azure-alpha). To pull the Azure Docker image, execute the following command: + +```bash +docker pull localstack/localstack-azure-alpha +``` + +Start the LocalStack Azure emulator using the localstack CLI, execute the following command: + +```bash +export LOCALSTACK_AUTH_TOKEN= +IMAGE_NAME=localstack/localstack-azure-alpha localstack start +``` + +Navigate to the `scripts` folder: + +```bash +cd samples/servicebus/java/scripts +``` + +Make the script executable: + +```bash +chmod +x deploy.sh +``` + +Run the deployment script: + +```bash +./deploy.sh +``` + +## Validation + +Once the deployment completes, run the [validate.sh](validate.sh) script to confirm that all resources were provisioned and configured as expected: + +```bash +#!/bin/bash + +# Variables +PREFIX='local' +SUFFIX='test' +RESOURCE_GROUP_NAME="${PREFIX}-rg" +SERVICEBUS_NAMESPACE_NAME="${PREFIX}-sb-ns-${SUFFIX}" +SERVICEBUS_QUEUE_NAME="myqueue" +ENVIRONMENT=$(az account show --query environmentName --output tsv) + +# Choose the appropriate CLI based on the environment +if [[ $ENVIRONMENT == "LocalStack" ]]; then + echo "Using azlocal for LocalStack emulator environment." + AZ="azlocal" +else + echo "Using standard az for AzureCloud environment." + AZ="az" +fi + +# Check resource group +echo -e "[$RESOURCE_GROUP_NAME] resource group:\n" +$AZ group show \ + --name "$RESOURCE_GROUP_NAME" \ + --output table \ + --only-show-errors + +# Check Service Bus namespace +echo -e "\n[$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace:\n" +$AZ servicebus namespace show \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --name "$SERVICEBUS_NAMESPACE_NAME" \ + --query "{name:name, location:location, serviceBusEndpoint:serviceBusEndpoint, status:provisioningState}" \ + --output table \ + --only-show-errors + +# Check Service Bus queue +echo -e "\n[$SERVICEBUS_QUEUE_NAME] Service Bus queue:\n" +$AZ servicebus queue show \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --namespace-name "$SERVICEBUS_NAMESPACE_NAME" \ + --name "$SERVICEBUS_QUEUE_NAME" \ + --query "{name:name, messageCount:messageCount, sizeInBytes:sizeInBytes}" \ + --output table \ + --only-show-errors +``` + +## Cleanup + +To destroy all created resources: + +```bash +#!/bin/bash + +# Variables +PREFIX='local' +SUFFIX='test' +RESOURCE_GROUP_NAME="${PREFIX}-rg" + +# Delete resource group and all contained resources +az group delete --name $RESOURCE_GROUP_NAME --yes --no-wait + +# Verify deletion +az group list --output table +``` + +This will remove all Azure resources created by the CLI deployment script. + +## Related Documentation + +- [Azure CLI Documentation](https://docs.microsoft.com/en-us/cli/azure/) +- [LocalStack for Azure Documentation](https://azure.localstack.cloud/) \ No newline at end of file diff --git a/samples/servicebus/java/scripts/deploy.sh b/samples/servicebus/java/scripts/deploy.sh index 9dbc3c5..db1847c 100755 --- a/samples/servicebus/java/scripts/deploy.sh +++ b/samples/servicebus/java/scripts/deploy.sh @@ -1,50 +1,122 @@ #!/bin/bash # Variables +PREFIX='local' +SUFFIX='test' LOCATION='westeurope' -RESOURCE_GROUP_NAME="local-rg-$RANDOM" +RESOURCE_GROUP_NAME="${PREFIX}-rg" +SERVICEBUS_NAMESPACE_NAME="${PREFIX}-sb-ns-${SUFFIX}" +SERVICEBUS_QUEUE_NAME="myqueue" # Queue name is hardcoded in the application properties CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" -SERVICEBUS_NAMESPACE_NAME="ls-sb-ns-$RANDOM" -# Queue name is hardcoded in application properties -SERVICEBUS_QUEUE_NAME="myqueue" - +ENVIRONMENT=$(az account show --query environmentName --output tsv) # Change the current directory to the script's directory cd "$CURRENT_DIR" || exit -# Redirect AZ calls to LocalStack -azlocal start-interception +# Choose the appropriate CLI based on the environment +if [[ $ENVIRONMENT == "LocalStack" ]]; then + echo "Using azlocal for LocalStack emulator environment." + AZ="azlocal" +else + echo "Using standard az for AzureCloud environment." + AZ="az" +fi + +# Check if the resource group already exists +echo "Checking for resource group [$RESOURCE_GROUP_NAME]..." +$AZ group show \ + --name "$RESOURCE_GROUP_NAME" \ + --only-show-errors &>/dev/null + +if [[ $? != 0 ]]; then + echo "Resource group [$RESOURCE_GROUP_NAME] not found. Creating..." + $AZ group create \ + --name "$RESOURCE_GROUP_NAME" \ + --location "$LOCATION" \ + --only-show-errors 1>/dev/null + + if [[ $? -eq 0 ]]; then + echo "Resource group [$RESOURCE_GROUP_NAME] created successfully." + else + echo "Failed to create resource group [$RESOURCE_GROUP_NAME]." + exit 1 + fi +else + echo "Resource group [$RESOURCE_GROUP_NAME] already exists." +fi + +# Check if the Service Bus namespace already exists +echo "Checking if [$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace already exists in the [$RESOURCE_GROUP_NAME] resource group..." +$AZ servicebus namespace show \ + --name "$SERVICEBUS_NAMESPACE_NAME" \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --only-show-errors &>/dev/null -# Create a resource group -echo "Creating resource group [$RESOURCE_GROUP_NAME]..." -az group create \ - --name $RESOURCE_GROUP_NAME \ - --location $LOCATION \ - --only-show-errors 1>/dev/null +if [[ $? != 0 ]]; then + echo "No [$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace found. Creating..." + $AZ servicebus namespace create \ + --name "$SERVICEBUS_NAMESPACE_NAME" \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --location "$LOCATION" \ + --only-show-errors 1>/dev/null -# Create a ServiceBus Queue -az servicebus namespace create \ - --name $SERVICEBUS_NAMESPACE_NAME \ - --resource-group $RESOURCE_GROUP_NAME + if [[ $? -eq 0 ]]; then + echo "[$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace created successfully." + else + echo "Failed to create [$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace." + exit 1 + fi +else + echo "[$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace already exists." +fi -queue_id=$(az servicebus queue create --resource-group $RESOURCE_GROUP_NAME --namespace-name $SERVICEBUS_NAMESPACE_NAME --name $SERVICEBUS_QUEUE_NAME --query 'id' --output tsv) +# Check if the Service Bus queue already exists +echo "Checking if [$SERVICEBUS_QUEUE_NAME] queue already exists in the [$SERVICEBUS_NAMESPACE_NAME] namespace..." +$AZ servicebus queue show \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --namespace-name "$SERVICEBUS_NAMESPACE_NAME" \ + --name "$SERVICEBUS_QUEUE_NAME" \ + --only-show-errors &>/dev/null -# Register connection string to use with our Application -export AZURE_SERVICEBUS_CONNECTION_STRING=$(az servicebus namespace authorization-rule keys list \ - --resource-group $RESOURCE_GROUP_NAME \ - --namespace-name $SERVICEBUS_NAMESPACE_NAME \ - --name RootManageSharedAccessKey \ - --query primaryConnectionString \ - --output tsv) +if [[ $? != 0 ]]; then + echo "No [$SERVICEBUS_QUEUE_NAME] queue found. Creating..." + $AZ servicebus queue create \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --namespace-name "$SERVICEBUS_NAMESPACE_NAME" \ + --name "$SERVICEBUS_QUEUE_NAME" \ + --only-show-errors 1>/dev/null -# START JAVA APP -cd ../app && mvn clean spring-boot:run + if [[ $? -eq 0 ]]; then + echo "[$SERVICEBUS_QUEUE_NAME] queue created successfully." + else + echo "Failed to create [$SERVICEBUS_QUEUE_NAME] queue." + exit 1 + fi +else + echo "[$SERVICEBUS_QUEUE_NAME] queue already exists." +fi -# Tear down all resources -az group delete \ - --name $RESOURCE_GROUP_NAME \ - --yes +# Retrieve the connection string for the Service Bus namespace +echo "Retrieving connection string for [$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace..." +AZURE_SERVICEBUS_CONNECTION_STRING=$($AZ servicebus namespace authorization-rule keys list \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --namespace-name "$SERVICEBUS_NAMESPACE_NAME" \ + --name RootManageSharedAccessKey \ + --query primaryConnectionString \ + --output tsv) -azlocal stop-interception +if [[ $? -eq 0 ]] && [[ -n "$AZURE_SERVICEBUS_CONNECTION_STRING" ]]; then + export AZURE_SERVICEBUS_CONNECTION_STRING + echo "Connection string retrieved successfully." +else + echo "Failed to retrieve connection string." + exit 1 +fi +# Start the Java application +echo "Starting Java application..." +cd "$CURRENT_DIR/../app" && mvn clean spring-boot:run +# Optionally tear down all resources (uncomment to enable) +# echo "Deleting resource group [$RESOURCE_GROUP_NAME]..." +# $AZ group delete --name "$RESOURCE_GROUP_NAME" --yes diff --git a/samples/servicebus/java/scripts/validate.sh b/samples/servicebus/java/scripts/validate.sh new file mode 100755 index 0000000..5d93e13 --- /dev/null +++ b/samples/servicebus/java/scripts/validate.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Variables +PREFIX='local' +SUFFIX='test' +RESOURCE_GROUP_NAME="${PREFIX}-rg" +SERVICEBUS_NAMESPACE_NAME="${PREFIX}-sb-ns-${SUFFIX}" +SERVICEBUS_QUEUE_NAME="myqueue" +ENVIRONMENT=$(az account show --query environmentName --output tsv) + +# Choose the appropriate CLI based on the environment +if [[ $ENVIRONMENT == "LocalStack" ]]; then + echo "Using azlocal for LocalStack emulator environment." + AZ="azlocal" +else + echo "Using standard az for AzureCloud environment." + AZ="az" +fi + +# Check resource group +echo -e "[$RESOURCE_GROUP_NAME] resource group:\n" +$AZ group show \ + --name "$RESOURCE_GROUP_NAME" \ + --output table \ + --only-show-errors + +# Check Service Bus namespace +echo -e "\n[$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace:\n" +$AZ servicebus namespace show \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --name "$SERVICEBUS_NAMESPACE_NAME" \ + --query "{name:name, location:location, serviceBusEndpoint:serviceBusEndpoint, status:provisioningState}" \ + --output table \ + --only-show-errors + +# Check Service Bus queue +echo -e "\n[$SERVICEBUS_QUEUE_NAME] Service Bus queue:\n" +$AZ servicebus queue show \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --namespace-name "$SERVICEBUS_NAMESPACE_NAME" \ + --name "$SERVICEBUS_QUEUE_NAME" \ + --query "{name:name, messageCount:messageCount, sizeInBytes:sizeInBytes}" \ + --output table \ + --only-show-errors \ No newline at end of file diff --git a/samples/servicebus/java/terraform/README.md b/samples/servicebus/java/terraform/README.md new file mode 100644 index 0000000..4afa8ae --- /dev/null +++ b/samples/servicebus/java/terraform/README.md @@ -0,0 +1,181 @@ +# Terraform Deployment + +This directory contains Terraform modules and a deployment script for provisioning Azure services in LocalStack for Azure. For further details about the sample application, refer to the [Azure Service Bus with Spring Boot](../README.md). + +## Prerequisites + +Before deploying this solution, ensure you have the following tools installed: + +- [LocalStack for Azure](https://azure.localstack.cloud/): Local Azure cloud emulator for development and testing +- [Visual Studio Code](https://code.visualstudio.com/): Code editor installed on one of the [supported platforms](https://code.visualstudio.com/docs/supporting/requirements#_platforms) +- [Terraform](https://developer.hashicorp.com/terraform/downloads): Infrastructure as Code tool for provisioning Azure resources +- [Docker](https://docs.docker.com/get-docker/): Container runtime required for LocalStack +- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli): Azure command-line interface +- [Azlocal CLI](https://azure.localstack.cloud/user-guides/sdks/az/): LocalStack Azure CLI wrapper +- [Java 21+](https://learn.microsoft.com/en-us/java/openjdk/download): Java runtime for compiling and running the sample application +- [Maven 3.8+](https://maven.apache.org/download.cgi): Build tool for managing Java project dependencies and compilation +- [jq](https://jqlang.org/): JSON processor for scripting and parsing command outputs + +### Installing azlocal CLI + +The [deploy.sh](deploy.sh) Bash script uses the `azlocal` CLI instead of the standard Azure CLI to work with LocalStack. Install it using: + +```bash +pip install azlocal +``` + +For more information, see [Get started with the az tool on LocalStack](https://azure.localstack.cloud/user-guides/sdks/az/). + +## Architecture Overview + +The Terraform modules create the following Azure resources: + +1. [Azure Resource Group](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-cli): A logical container scoping all resources in this sample. +2. [Azure Service Bus Namespace](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-overview): The messaging namespace that hosts the queue used by the application. +3. [Azure Service Bus Queue](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-queues-topics-subscriptions#queues): The `myqueue` queue used to send and receive messages. + +The Spring Boot sample application connects to the Service Bus namespace, sends a test message to the sample queue, receives it back, and exits. For more information on the sample application, see [Azure Service Bus with Spring Boot](../README.md). + +## Provisioning Scripts + +You can use the [deploy.sh](deploy.sh) script to automate the deployment of all Azure resources and the sample application in a single step, streamlining setup and reducing manual configuration. The script executes the following steps: + +- Cleans up any previous Terraform state and plan files to ensure a fresh deployment. +- Initializes the Terraform working directory and downloads required plugins. +- Creates and validates a Terraform execution plan for the Azure infrastructure. +- Applies the Terraform plan to provision all necessary Azure resources. +- Extracts resource names and outputs from the Terraform deployment. +- Compiles the Spring Boot project and runs the app on the host machine. + +## Configuration + +When using LocalStack for Azure, configure the `metadata_host` and `subscription_id` settings in the [Azure Provider for Terraform](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs) to ensure proper connectivity: + + +```hcl +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + } + + # Set the hostname of the Azure Metadata Service (for example management.azure.com) + # used to obtain the Cloud Environment when using LocalStack's Azure emulator. + # This allows the provider to correctly identify the environment and avoid making calls to the real Azure endpoints. + metadata_host="localhost.localstack.cloud:4566" + + # Set the subscription ID to a dummy value when using LocalStack's Azure emulator. + subscription_id = "00000000-0000-0000-0000-000000000000" +} +``` + +## Deployment + +You can set up the Azure emulator by utilizing LocalStack for Azure Docker image. Before starting, ensure you have a valid `LOCALSTACK_AUTH_TOKEN` to access the Azure emulator. Refer to the [Auth Token guide](https://docs.localstack.cloud/getting-started/auth-token/?__hstc=108988063.8aad2b1a7229945859f4d9b9bb71e05d.1743148429561.1758793541854.1758810151462.32&__hssc=108988063.3.1758810151462&__hsfp=3945774529) to obtain your Auth Token and specify it in the `LOCALSTACK_AUTH_TOKEN` environment variable. The Azure Docker image is available on the [LocalStack Docker Hub](https://hub.docker.com/r/localstack/localstack-azure-alpha). To pull the Azure Docker image, execute the following command: + +```bash +docker pull localstack/localstack-azure-alpha +``` + +Start the LocalStack Azure emulator using the localstack CLI, execute the following command: + +```bash +export LOCALSTACK_AUTH_TOKEN= +IMAGE_NAME=localstack/localstack-azure-alpha localstack start +``` + +Navigate to the `terraform` folder: + +```bash +cd samples/servicebus/java/terraform +``` + +Make the script executable: + +```bash +chmod +x deploy.sh +``` + +Run the deployment script: + +```bash +./deploy.sh +``` + +## Validation + +Once the deployment completes, run the [validate.sh](../scripts/validate.sh) script to confirm that all resources were provisioned and configured as expected: + +```bash +#!/bin/bash + +# Variables +PREFIX='local' +SUFFIX='test' +RESOURCE_GROUP_NAME="${PREFIX}-rg" +SERVICEBUS_NAMESPACE_NAME="${PREFIX}-sb-ns-${SUFFIX}" +SERVICEBUS_QUEUE_NAME="myqueue" +ENVIRONMENT=$(az account show --query environmentName --output tsv) + +# Choose the appropriate CLI based on the environment +if [[ $ENVIRONMENT == "LocalStack" ]]; then + echo "Using azlocal for LocalStack emulator environment." + AZ="azlocal" +else + echo "Using standard az for AzureCloud environment." + AZ="az" +fi + +# Check resource group +echo -e "[$RESOURCE_GROUP_NAME] resource group:\n" +$AZ group show \ + --name "$RESOURCE_GROUP_NAME" \ + --output table \ + --only-show-errors + +# Check Service Bus namespace +echo -e "\n[$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace:\n" +$AZ servicebus namespace show \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --name "$SERVICEBUS_NAMESPACE_NAME" \ + --query "{name:name, location:location, serviceBusEndpoint:serviceBusEndpoint, status:provisioningState}" \ + --output table \ + --only-show-errors + +# Check Service Bus queue +echo -e "\n[$SERVICEBUS_QUEUE_NAME] Service Bus queue:\n" +$AZ servicebus queue show \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --namespace-name "$SERVICEBUS_NAMESPACE_NAME" \ + --name "$SERVICEBUS_QUEUE_NAME" \ + --query "{name:name, messageCount:messageCount, sizeInBytes:sizeInBytes}" \ + --output table \ + --only-show-errors +``` + +## Cleanup + +To destroy all created resources: + +```bash +#!/bin/bash + +# Variables +PREFIX='local' +SUFFIX='test' +RESOURCE_GROUP_NAME="${PREFIX}-rg" + +# Delete resource group and all contained resources +az group delete --name $RESOURCE_GROUP_NAME --yes --no-wait + +# Verify deletion +az group list --output table +``` + +This will remove all Azure resources created by the CLI deployment script. + +## Related Documentation + +- [Terraform Azure Provider](https://registry.terraform.io/providers/hashicorp/azurerm/latest) +- [LocalStack for Azure Documentation](https://azure.localstack.cloud/) \ No newline at end of file diff --git a/samples/servicebus/java/terraform/deploy.sh b/samples/servicebus/java/terraform/deploy.sh new file mode 100755 index 0000000..62fd851 --- /dev/null +++ b/samples/servicebus/java/terraform/deploy.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# Variables +PREFIX='local' +SUFFIX='test' +LOCATION='westeurope' +SERVICEBUS_QUEUE_NAME="myqueue" # Queue name is hardcoded in the application properties +CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" +ZIPFILE="planner_website.zip" +ENVIRONMENT=$(az account show --query environmentName --output tsv) + +# Change the current directory to the script's directory +cd "$CURRENT_DIR" || exit + +# Run terraform init and apply +if [[ $ENVIRONMENT == "LocalStack" ]]; then + echo "Using azlocal for LocalStack emulator environment." + AZ="azlocal" +else + echo "Using standard terraform and az for AzureCloud environment." + AZ="az" +fi + +# Intialize Terraform +echo "Initializing Terraform..." +terraform init -upgrade + +# Run terraform plan and check for errors +echo "Planning Terraform deployment..." +terraform plan -out=tfplan \ + -var "prefix=$PREFIX" \ + -var "suffix=$SUFFIX" \ + -var "location=$LOCATION" \ + -var "queue_name=$SERVICEBUS_QUEUE_NAME" + +if [[ $? != 0 ]]; then + echo "Terraform plan failed. Exiting." + exit 1 +fi + +# Apply the Terraform configuration +echo "Applying Terraform configuration..." +terraform apply -auto-approve tfplan + +if [[ $? != 0 ]]; then + echo "Terraform apply failed. Exiting." + exit 1 +fi + +# Get the output values +RESOURCE_GROUP_NAME=$(terraform output -raw resource_group_name) +SERVICEBUS_NAMESPACE_NAME=$(terraform output -raw namespace_name) + + +if [[ -z "$SERVICEBUS_NAMESPACE_NAME" ]]; then + echo "Service Bus Namespace Name is empty. Exiting." + exit 1 +fi + +# Retrieve the connection string for the Service Bus namespace +echo "Retrieving connection string for [$SERVICEBUS_NAMESPACE_NAME] Service Bus namespace..." +AZURE_SERVICEBUS_CONNECTION_STRING=$($AZ servicebus namespace authorization-rule keys list \ + --resource-group "$RESOURCE_GROUP_NAME" \ + --namespace-name "$SERVICEBUS_NAMESPACE_NAME" \ + --name RootManageSharedAccessKey \ + --query primaryConnectionString \ + --output tsv) + +if [[ $? -eq 0 ]] && [[ -n "$AZURE_SERVICEBUS_CONNECTION_STRING" ]]; then + export AZURE_SERVICEBUS_CONNECTION_STRING + echo "Connection string retrieved successfully." +else + echo "Failed to retrieve connection string." + exit 1 +fi + +# Start the Java application +echo "Starting Java application..." +cd "$CURRENT_DIR/../app" && mvn clean spring-boot:run + +# Optionally tear down all resources (uncomment to enable) +# echo "Deleting resource group [$RESOURCE_GROUP_NAME]..." +# $AZ group delete --name "$RESOURCE_GROUP_NAME" --yes diff --git a/samples/servicebus/java/terraform/main.tf b/samples/servicebus/java/terraform/main.tf new file mode 100644 index 0000000..d5c8208 --- /dev/null +++ b/samples/servicebus/java/terraform/main.tf @@ -0,0 +1,69 @@ +# Local Variables +locals { + prefix = lower(var.prefix) + suffix = lower(var.suffix) + resource_group_name = "${local.prefix}-rg" + servicebus_namespace_name = "${local.prefix}-sb-ns-${local.suffix}" + servicebus_namespace_authorization_rule_name = "${local.prefix}-sb-ns-auth-rule-${local.suffix}" + servicebus_queue_authorization_rule_name = "${local.prefix}-sb-queue-auth-rule-${local.suffix}" +} + +# Create a resource group +resource "azurerm_resource_group" "example" { + name = local.resource_group_name + location = var.location + tags = var.tags +} +resource "azurerm_servicebus_namespace" "example" { + name = local.servicebus_namespace_name + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + sku = var.servicebus_namespace_sku + capacity = var.servicebus_namespace_capacity + premium_messaging_partitions = var.servicebus_namespace_premium_messaging_partitions + local_auth_enabled = var.servicebus_namespace_local_auth_enabled + public_network_access_enabled = var.servicebus_namespace_public_network_access_enabled + minimum_tls_version = var.servicebus_namespace_minimum_tls_version + tags = var.tags +} + +resource "azurerm_servicebus_namespace_authorization_rule" "example" { + name = local.servicebus_namespace_authorization_rule_name + namespace_id = azurerm_servicebus_namespace.example.id + + listen = true + send = true + manage = false +} + +resource "azurerm_servicebus_queue" "example" { + name = var.queue_name + namespace_id = azurerm_servicebus_namespace.example.id + + lock_duration = var.servicebus_queue_lock_duration + max_message_size_in_kilobytes = var.servicebus_queue_max_message_size_in_kilobytes + max_size_in_megabytes = var.servicebus_queue_max_size_in_megabytes + requires_duplicate_detection = var.servicebus_queue_requires_duplicate_detection + requires_session = var.servicebus_queue_requires_session + default_message_ttl = var.servicebus_queue_default_message_ttl + dead_lettering_on_message_expiration = var.servicebus_queue_dead_lettering_on_message_expiration + duplicate_detection_history_time_window = var.servicebus_queue_duplicate_detection_history_time_window + max_delivery_count = var.servicebus_queue_max_delivery_count + status = var.servicebus_queue_status + batched_operations_enabled = var.servicebus_queue_batched_operations_enabled + auto_delete_on_idle = var.servicebus_queue_auto_delete_on_idle + partitioning_enabled = var.servicebus_queue_partitioning_enabled + express_enabled = var.servicebus_queue_express_enabled + forward_to = var.servicebus_queue_forward_to + forward_dead_lettered_messages_to = var.servicebus_queue_forward_dead_lettered_messages_to +} + + +resource "azurerm_servicebus_queue_authorization_rule" "example" { + name = local.servicebus_queue_authorization_rule_name + queue_id = azurerm_servicebus_queue.example.id + + listen = true + send = true + manage = false +} \ No newline at end of file diff --git a/samples/servicebus/java/terraform/outputs.tf b/samples/servicebus/java/terraform/outputs.tf new file mode 100644 index 0000000..e8280b8 --- /dev/null +++ b/samples/servicebus/java/terraform/outputs.tf @@ -0,0 +1,16 @@ +output "resource_group_name" { + description = "The name of the Resource Group." + value = azurerm_resource_group.example.name +} + +output "namespace_name" { + description = "The name of the Service Bus Namespace." + value = azurerm_servicebus_namespace.example.name +} + +output "queue_name" { + description = "The name of the Service Bus Queue." + value = azurerm_servicebus_queue.example.name +} + + diff --git a/samples/servicebus/java/terraform/providers.tf b/samples/servicebus/java/terraform/providers.tf new file mode 100644 index 0000000..3e04500 --- /dev/null +++ b/samples/servicebus/java/terraform/providers.tf @@ -0,0 +1,24 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "=4.60.0" + } + } +} + +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + } + + # Set the hostname of the Azure Metadata Service (for example management.azure.com) + # used to obtain the Cloud Environment when using LocalStack's Azure emulator. + # This allows the provider to correctly identify the environment and avoid making calls to the real Azure endpoints. + metadata_host = "localhost.localstack.cloud:4566" + + # Set the subscription ID to a dummy value when using LocalStack's Azure emulator. + subscription_id = "00000000-0000-0000-0000-000000000000" +} \ No newline at end of file diff --git a/samples/servicebus/java/terraform/variables.tf b/samples/servicebus/java/terraform/variables.tf new file mode 100644 index 0000000..f52b32a --- /dev/null +++ b/samples/servicebus/java/terraform/variables.tf @@ -0,0 +1,188 @@ +variable "prefix" { + description = "(Optional) Specifies the prefix for the name of the Azure resources." + type = string + default = "local" + + validation { + condition = var.prefix == null || length(var.prefix) >= 2 + error_message = "The prefix must be at least 2 characters long." + } +} + +variable "suffix" { + description = "(Optional) Specifies the suffix for the name of the Azure resources." + type = string + default = "test" + + validation { + condition = var.suffix == null || length(var.suffix) >= 2 + error_message = "The suffix must be at least 2 characters long." + } +} + +variable "location" { + description = "(Required) Specifies the location for all resources." + type = string + default = "westeurope" +} + +variable "tags" { + description = "(Optional) Specifies the tags to be applied to the resources." + type = map(string) + default = { + environment = "test" + iac = "terraform" + } +} + +variable "queue_name" { + description = "(Optional) Specifies the name of the Service Bus Queue." + type = string + default = "myqueue" +} + +# Service Bus Namespace Variables + +variable "servicebus_namespace_sku" { + description = "(Required) Specifies the SKU tier for the Service Bus Namespace. Options are Basic, Standard, or Premium." + type = string + default = "Standard" + + validation { + condition = contains(["Basic", "Standard", "Premium"], var.servicebus_namespace_sku) + error_message = "The SKU must be one of Basic, Standard, or Premium." + } +} + +variable "servicebus_namespace_capacity" { + description = "(Optional) Specifies the capacity for the Service Bus Namespace. When SKU is Premium, capacity can be 1, 2, 4, 8, or 16. When SKU is Basic or Standard, capacity can be 0 only." + type = number + default = 0 +} + +variable "servicebus_namespace_premium_messaging_partitions" { + description = "(Optional) Specifies the number of messaging partitions. Only valid when SKU is Premium. Possible values include 0, 1, 2, and 4. Defaults to 0." + type = number + default = 0 +} + +variable "servicebus_namespace_local_auth_enabled" { + description = "(Optional) Specifies whether SAS authentication is enabled for the Service Bus Namespace. Defaults to true." + type = bool + default = true +} + +variable "servicebus_namespace_public_network_access_enabled" { + description = "(Optional) Specifies whether public network access is enabled for the Service Bus Namespace. Defaults to true." + type = bool + default = true +} + +variable "servicebus_namespace_minimum_tls_version" { + description = "(Optional) Specifies the minimum supported TLS version for the Service Bus Namespace. Valid values are 1.0, 1.1, and 1.2. Defaults to 1.2." + type = string + default = "1.2" + + validation { + condition = contains(["1.0", "1.1", "1.2"], var.servicebus_namespace_minimum_tls_version) + error_message = "The minimum TLS version must be one of 1.0, 1.1, or 1.2." + } +} + +# Service Bus Queue Variables + +variable "servicebus_queue_lock_duration" { + description = "(Optional) Specifies the ISO 8601 timespan duration of a peek-lock. Maximum value is 5 minutes. Defaults to PT1M." + type = string + default = "PT4M" +} + +variable "servicebus_queue_max_message_size_in_kilobytes" { + description = "(Optional) Specifies the maximum size of a message allowed on the queue in kilobytes. Only applicable for Premium SKU." + type = number + default = null +} + +variable "servicebus_queue_max_size_in_megabytes" { + description = "(Optional) Specifies the size of memory allocated for the queue in megabytes." + type = number + default = null +} + +variable "servicebus_queue_requires_duplicate_detection" { + description = "(Optional) Specifies whether the queue requires duplicate detection. Changing this forces a new resource to be created. Defaults to false." + type = bool + default = true +} + +variable "servicebus_queue_requires_session" { + description = "(Optional) Specifies whether the queue requires sessions for ordered handling of unbounded sequences of related messages. Changing this forces a new resource to be created. Defaults to false." + type = bool + default = true +} + +variable "servicebus_queue_default_message_ttl" { + description = "(Optional) Specifies the ISO 8601 timespan duration of the TTL of messages sent to this queue." + type = string + default = "PT12S" +} + +variable "servicebus_queue_dead_lettering_on_message_expiration" { + description = "(Optional) Specifies whether the queue has dead letter support when a message expires. Defaults to false." + type = bool + default = true +} + +variable "servicebus_queue_duplicate_detection_history_time_window" { + description = "(Optional) Specifies the ISO 8601 timespan duration during which duplicates can be detected. Defaults to PT10M." + type = string + default = "PT10M" +} + +variable "servicebus_queue_max_delivery_count" { + description = "(Optional) Specifies the maximum number of deliveries before a message is automatically dead lettered. Defaults to 10." + type = number + default = 5 +} + +variable "servicebus_queue_status" { + description = "(Optional) Specifies the status of the queue. Possible values are Active, Creating, Deleting, Disabled, ReceiveDisabled, Renaming, SendDisabled, Unknown. Defaults to Active." + type = string + default = "Active" +} + +variable "servicebus_queue_batched_operations_enabled" { + description = "(Optional) Specifies whether server-side batched operations are enabled. Defaults to true." + type = bool + default = true +} + +variable "servicebus_queue_auto_delete_on_idle" { + description = "(Optional) Specifies the ISO 8601 timespan duration of the idle interval after which the queue is automatically deleted. Minimum of 5 minutes." + type = string + default = null +} + +variable "servicebus_queue_partitioning_enabled" { + description = "(Optional) Specifies whether the queue is partitioned across multiple message brokers. Changing this forces a new resource to be created. Defaults to false." + type = bool + default = true +} + +variable "servicebus_queue_express_enabled" { + description = "(Optional) Specifies whether Express Entities are enabled. An express queue holds a message in memory temporarily before writing it to persistent storage. Defaults to false." + type = bool + default = true +} + +variable "servicebus_queue_forward_to" { + description = "(Optional) Specifies the name of a queue or topic to automatically forward messages to." + type = string + default = null +} + +variable "servicebus_queue_forward_dead_lettered_messages_to" { + description = "(Optional) Specifies the name of a queue or topic to automatically forward dead lettered messages to." + type = string + default = null +} \ No newline at end of file diff --git a/samples/web-app-cosmosdb-mongodb-api/python/bicep/deploy.sh b/samples/web-app-cosmosdb-mongodb-api/python/bicep/deploy.sh index b119d5a..70d17f1 100755 --- a/samples/web-app-cosmosdb-mongodb-api/python/bicep/deploy.sh +++ b/samples/web-app-cosmosdb-mongodb-api/python/bicep/deploy.sh @@ -1,7 +1,7 @@ #!/bin/bash # Variables -PREFIX='bicep' +PREFIX='local' SUFFIX='test' TEMPLATE="main.bicep" PARAMETERS="main.bicepparam" diff --git a/samples/web-app-cosmosdb-mongodb-api/python/terraform/deploy.sh b/samples/web-app-cosmosdb-mongodb-api/python/terraform/deploy.sh index 3dc11b3..ed36a5b 100755 --- a/samples/web-app-cosmosdb-mongodb-api/python/terraform/deploy.sh +++ b/samples/web-app-cosmosdb-mongodb-api/python/terraform/deploy.sh @@ -1,7 +1,7 @@ #!/bin/bash # Variables -PREFIX='ciao' +PREFIX='local' SUFFIX='test' LOCATION='westeurope' CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)"