diff --git a/mi-azuresql-spring/README.md b/mi-azuresql-spring/README.md new file mode 100644 index 0000000..bd62546 --- /dev/null +++ b/mi-azuresql-spring/README.md @@ -0,0 +1,77 @@ +# Azure SQL with Managed Identity - Spring Boot Demo + +This project demonstrates how to connect to Azure SQL Database using Azure Managed Identity in a Spring Boot application. + +## Features + +- Spring Boot application with Azure SQL Database connectivity +- Azure Managed Identity authentication (no passwords required) +- REST API endpoints for testing database connectivity +- Proper Spring Cloud Azure integration + +## Configuration + +The application is configured to use Azure Managed Identity for authentication with Azure SQL Database. + +### Required Environment Variables + +- `AZ_DATABASE_SERVER_NAME`: Your Azure SQL Server name (without .database.windows.net) +- `AZURE_CLIENT_ID`: The client ID of your User-Assigned Managed Identity + +### Application Properties + +The `application.properties` file contains: + +```properties +# Azure SQL Database Configuration with Managed Identity +spring.datasource.url=jdbc:sqlserver://${AZ_DATABASE_SERVER_NAME}.database.windows.net:1433;database=demo;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryMSI + +# Enable Azure managed identity for Spring Cloud Azure +spring.cloud.azure.credential.managed-identity-enabled=true +spring.cloud.azure.credential.client-id=${AZURE_CLIENT_ID} +``` + +## Dependencies + +Key dependencies used in this project: + +- `spring-boot-starter-web`: Web layer and REST controllers +- `spring-boot-starter-jdbc`: JDBC support +- `spring-cloud-azure-starter`: Azure integration for Spring Boot +- `mssql-jdbc`: Microsoft SQL Server JDBC driver + +## API Endpoints + +Once running, the application exposes the following endpoints: + +- `GET /api/database/health`: Check if the application is running +- `GET /api/database/test-connection`: Test database connectivity +- `GET /api/database/version`: Get database version information +- `GET /api/database/test-query`: Execute a simple test query + +## Building and Running + +```bash +# Build the project +mvn clean compile + +# Run the application (requires Azure environment with configured managed identity) +mvn spring-boot:run +``` + +## Prerequisites + +1. Azure SQL Database instance +2. User-Assigned Managed Identity configured in Azure +3. Proper access permissions granted to the managed identity on the SQL database +4. Application running in an Azure environment that supports managed identity (e.g., Azure App Service, Azure VM, Azure Container Instances) + +## Azure Setup + +1. Create an Azure SQL Database +2. Create a User-Assigned Managed Identity +3. Grant the managed identity access to the SQL database +4. Configure the application environment variables +5. Deploy to an Azure service that supports managed identity + +For more information about setting up managed identity with Azure SQL, refer to the [Azure documentation](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview). \ No newline at end of file diff --git a/mi-azuresql-spring/pom.xml b/mi-azuresql-spring/pom.xml new file mode 100644 index 0000000..bab37c0 --- /dev/null +++ b/mi-azuresql-spring/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + + com.example + mi-azuresql-spring + 1.0-SNAPSHOT + jar + + mi-azuresql-spring + Azure SQL with Managed Identity Spring Boot Demo + + + 17 + 17 + 17 + 3.1.0 + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-parent + 3.1.0 + + + + + + + + com.azure.spring + spring-cloud-azure-dependencies + 5.22.0 + pom + import + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + + + com.microsoft.sqlserver + mssql-jdbc + 12.4.2.jre11 + + + + + com.azure.spring + spring-cloud-azure-starter + + + + + com.h2database + h2 + test + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/mi-azuresql-spring/src/main/java/com/example/azuresql/AzureSqlManagedIdentityApplication.java b/mi-azuresql-spring/src/main/java/com/example/azuresql/AzureSqlManagedIdentityApplication.java new file mode 100644 index 0000000..57cafdf --- /dev/null +++ b/mi-azuresql-spring/src/main/java/com/example/azuresql/AzureSqlManagedIdentityApplication.java @@ -0,0 +1,12 @@ +package com.example.azuresql; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AzureSqlManagedIdentityApplication { + + public static void main(String[] args) { + SpringApplication.run(AzureSqlManagedIdentityApplication.class, args); + } +} \ No newline at end of file diff --git a/mi-azuresql-spring/src/main/java/com/example/azuresql/controller/DatabaseController.java b/mi-azuresql-spring/src/main/java/com/example/azuresql/controller/DatabaseController.java new file mode 100644 index 0000000..a89e750 --- /dev/null +++ b/mi-azuresql-spring/src/main/java/com/example/azuresql/controller/DatabaseController.java @@ -0,0 +1,35 @@ +package com.example.azuresql.controller; + +import com.example.azuresql.service.DatabaseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/database") +public class DatabaseController { + + @Autowired + private DatabaseService databaseService; + + @GetMapping("/test-connection") + public String testConnection() { + return databaseService.testConnection(); + } + + @GetMapping("/version") + public String getDatabaseVersion() { + return databaseService.getDatabaseVersion(); + } + + @GetMapping("/test-query") + public String testQuery() { + return databaseService.testQuery(); + } + + @GetMapping("/health") + public String health() { + return "Azure SQL Managed Identity Spring application is running!"; + } +} \ No newline at end of file diff --git a/mi-azuresql-spring/src/main/java/com/example/azuresql/service/DatabaseService.java b/mi-azuresql-spring/src/main/java/com/example/azuresql/service/DatabaseService.java new file mode 100644 index 0000000..7ab7c70 --- /dev/null +++ b/mi-azuresql-spring/src/main/java/com/example/azuresql/service/DatabaseService.java @@ -0,0 +1,53 @@ +package com.example.azuresql.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +@Service +public class DatabaseService { + + @Autowired + private DataSource dataSource; + + @Autowired + private JdbcTemplate jdbcTemplate; + + /** + * Test database connection using Azure Managed Identity + */ + public String testConnection() { + try (Connection connection = dataSource.getConnection()) { + return "Successfully connected to Azure SQL Database using Managed Identity!"; + } catch (SQLException e) { + return "Failed to connect to database: " + e.getMessage(); + } + } + + /** + * Get database version using JdbcTemplate + */ + public String getDatabaseVersion() { + try { + return jdbcTemplate.queryForObject("SELECT @@VERSION", String.class); + } catch (Exception e) { + return "Failed to get database version: " + e.getMessage(); + } + } + + /** + * Test a simple query to validate the connection + */ + public String testQuery() { + try { + String result = jdbcTemplate.queryForObject("SELECT 'Hello from Azure SQL!' as message", String.class); + return result; + } catch (Exception e) { + return "Failed to execute test query: " + e.getMessage(); + } + } +} \ No newline at end of file diff --git a/mi-azuresql-spring/src/main/resources/application.properties b/mi-azuresql-spring/src/main/resources/application.properties new file mode 100644 index 0000000..e283a30 --- /dev/null +++ b/mi-azuresql-spring/src/main/resources/application.properties @@ -0,0 +1,17 @@ +# Azure SQL Database Configuration with Managed Identity +spring.datasource.url=jdbc:sqlserver://${AZ_DATABASE_SERVER_NAME}.database.windows.net:1433;database=demo;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryMSI + +# Enable Azure managed identity for Spring Cloud Azure +spring.cloud.azure.credential.managed-identity-enabled=true +spring.cloud.azure.credential.client-id=${AZURE_CLIENT_ID} + +# Application Configuration +server.port=8080 +logging.level.com.example=DEBUG +logging.level.com.microsoft.sqlserver=DEBUG + +# Database server name placeholder +AZ_DATABASE_SERVER_NAME= + +# Managed identity client ID placeholder +AZURE_CLIENT_ID= \ No newline at end of file diff --git a/mi-azuresql-spring/src/test/java/com/example/azuresql/AzureSqlManagedIdentityApplicationTests.java b/mi-azuresql-spring/src/test/java/com/example/azuresql/AzureSqlManagedIdentityApplicationTests.java new file mode 100644 index 0000000..2fb88f5 --- /dev/null +++ b/mi-azuresql-spring/src/test/java/com/example/azuresql/AzureSqlManagedIdentityApplicationTests.java @@ -0,0 +1,21 @@ +package com.example.azuresql; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; + +@SpringBootTest +@TestPropertySource(properties = { + "AZ_DATABASE_SERVER_NAME=test-server", + "AZURE_CLIENT_ID=test-client-id", + "spring.datasource.url=jdbc:h2:mem:testdb", + "spring.cloud.azure.credential.managed-identity-enabled=false" +}) +class AzureSqlManagedIdentityApplicationTests { + + @Test + void contextLoads() { + // This test ensures the application context loads successfully + // with mocked properties to avoid actual Azure SQL connection + } +} \ No newline at end of file