+## Requirements
+
+| Name | Version |
+|------|---------|
+| [btp](#requirement\_btp) | ~> 1.8.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [btp_subaccount.subaccount](https://registry.terraform.io/providers/SAP/btp/latest/docs/resources/subaccount) | resource |
+| [btp_subaccount_role_collection_assignment.subaccount_admin](https://registry.terraform.io/providers/SAP/btp/latest/docs/resources/subaccount_role_collection_assignment) | resource |
+| [btp_subaccount_role_collection_assignment.subaccount_service_admininstrator](https://registry.terraform.io/providers/SAP/btp/latest/docs/resources/subaccount_role_collection_assignment) | resource |
+| [btp_subaccount_role_collection_assignment.subaccount_viewer](https://registry.terraform.io/providers/SAP/btp/latest/docs/resources/subaccount_role_collection_assignment) | resource |
+| [btp_directories.all](https://registry.terraform.io/providers/SAP/btp/latest/docs/data-sources/directories) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [globalaccount](#input\_globalaccount) | The subdomain of the global account in which you want to manage resources. | `string` | n/a | yes |
+| [parent\_id](#input\_parent\_id) | The parent directory ID for the subaccount. Use this when importing existing subaccounts. Mutually exclusive with subfolder. | `string` | `""` | no |
+| [project\_identifier](#input\_project\_identifier) | The meshStack project identifier. | `string` | n/a | yes |
+| [region](#input\_region) | The region of the subaccount. | `string` | `"eu10"` | no |
+| [subfolder](#input\_subfolder) | The subfolder name to use for the SAP BTP resources. This is used to create a folder structure in the SAP BTP cockpit. Mutually exclusive with parent\_id. | `string` | `""` | no |
+| [users](#input\_users) | Users and their roles provided by meshStack | list(object(
{
meshIdentifier = string
username = string
firstName = string
lastName = string
email = string
euid = string
roles = list(string)
}
))
| `[]` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [subaccount\_id](#output\_subaccount\_id) | The ID of the created subaccount |
+| [subaccount\_login\_link](#output\_subaccount\_login\_link) | Link to the subaccount in the SAP BTP cockpit |
+| [subaccount\_name](#output\_subaccount\_name) | The name of the subaccount |
+| [subaccount\_region](#output\_subaccount\_region) | The region of the subaccount |
+| [subaccount\_subdomain](#output\_subaccount\_subdomain) | The subdomain of the subaccount |
+
\ No newline at end of file
diff --git a/modules/sapbtp/subaccount/buildingblock/definition/definition.json b/modules/sapbtp/subaccount/buildingblock/definition/definition.json
new file mode 100644
index 0000000..1e75abb
--- /dev/null
+++ b/modules/sapbtp/subaccount/buildingblock/definition/definition.json
@@ -0,0 +1,94 @@
+{
+ "name": "SAP BTP Subaccount",
+ "displayName": "SAP BTP Subaccount",
+ "description": "Creates and manages SAP BTP subaccounts with user role assignments. Foundation for all other BTP building blocks.",
+ "category": "Infrastructure",
+ "platform": "sapbtp",
+ "tags": ["subaccount", "btp", "foundation", "core"],
+ "version": "3.0.0",
+ "supportedPlatforms": ["sapbtp"],
+ "schema": {
+ "inputs": {
+ "subfolder": {
+ "type": "string",
+ "description": "BTP directory/folder name",
+ "required": false,
+ "default": ""
+ },
+ "region": {
+ "type": "string",
+ "description": "BTP region for the subaccount",
+ "required": true,
+ "default": "eu10",
+ "options": ["us10", "eu10", "eu20", "ap21", "jp20", "us20", "us21", "eu11", "ap10", "ap11"]
+ },
+ "users": {
+ "type": "array",
+ "description": "List of users from authoritative system with roles",
+ "required": false,
+ "default": [],
+ "items": {
+ "type": "object",
+ "properties": {
+ "meshIdentifier": {
+ "type": "string"
+ },
+ "username": {
+ "type": "string"
+ },
+ "firstName": {
+ "type": "string"
+ },
+ "lastName": {
+ "type": "string"
+ },
+ "email": {
+ "type": "string"
+ },
+ "euid": {
+ "type": "string"
+ },
+ "roles": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["admin", "user", "reader"]
+ }
+ }
+ }
+ }
+ },
+ "project_identifier": {
+ "type": "string",
+ "description": "Project identifier from meshStack",
+ "required": true
+ }
+ },
+ "outputs": {
+ "subaccount_id": {
+ "type": "string",
+ "description": "BTP Subaccount ID"
+ },
+ "subaccount_name": {
+ "type": "string",
+ "description": "BTP Subaccount name"
+ },
+ "subaccount_subdomain": {
+ "type": "string",
+ "description": "BTP Subaccount subdomain"
+ },
+ "subaccount_region": {
+ "type": "string",
+ "description": "BTP Subaccount region"
+ },
+ "subaccount_login_link": {
+ "type": "string",
+ "description": "Link to BTP Cockpit for this subaccount"
+ }
+ }
+ },
+ "documentation": {
+ "readme": "README.md",
+ "userGuide": "APP_TEAM_README.md"
+ }
+}
diff --git a/modules/sapbtp/subaccount/buildingblock/import-resources-README.md b/modules/sapbtp/subaccount/buildingblock/import-resources-README.md
new file mode 100644
index 0000000..c459c68
--- /dev/null
+++ b/modules/sapbtp/subaccount/buildingblock/import-resources-README.md
@@ -0,0 +1,306 @@
+# Dynamic SAP BTP Resource Importer
+
+## Overview
+
+Automatically imports existing SAP BTP resources into OpenTofu state by reading configuration from `terraform.tfvars` and discovering resource IDs from state.
+
+Available for both **Bash** (Linux/macOS) and **PowerShell** (Windows).
+
+## Features
+
+✅ **Fully Automatic** - No manual resource ID lookup required
+✅ **Idempotent** - Safe to run multiple times
+✅ **Smart Discovery** - Reads `terraform.tfvars` to know what to import
+✅ **Error Handling** - Tracks successful and failed imports
+✅ **Skip Already Imported** - Detects and skips resources already in state
+
+## Usage
+
+**Bash (Linux/macOS):**
+```bash
+./import-resources.sh
+```
+
+**PowerShell (Windows):**
+```powershell
+./import-resources.ps1
+```
+
+That's it! The script does everything automatically.
+
+## What It Does
+
+### 1. Reads Configuration
+From `terraform.tfvars`:
+- `project_identifier` - Subaccount name
+- `enable_cloudfoundry` - Whether CF environment exists
+- `cf_services` - Comma-separated list of CF service instances (e.g., "destination.lite,xsuaa.application")
+
+### 2. Discovers Resource IDs
+From OpenTofu state (or manual input if empty):
+- `btp_subaccount_id` - Subaccount ID
+- `cloudfoundry_instance_id` - CF environment ID
+- `cloudfoundry_services` - All CF service instance IDs
+
+### 3. Imports Resources
+- Subaccount
+- Cloud Foundry environment (if enabled)
+- All CF service instances (from `cf_services` variable)
+
+### 4. Skips Non-Importable Resources
+- Role assignments (not supported by provider)
+- Entitlements (managed declaratively)
+
+## Example Output
+
+```
+=== SAP BTP Dynamic Resource Import Script ===
+
+Reading configuration from terraform.tfvars...
+ Project Identifier: testsubaccount
+ Cloud Foundry Enabled: true
+ CF Services: destination.lite,xsuaa.application
+
+Discovering resource IDs...
+ Subaccount ID: af3b4e1c-b28d-4c6d-9e4a-3e7ffa725ed3
+ CF Environment ID: 8EE92B2C-120D-4988-931A-598EC72E5273
+
+Starting imports...
+
+Importing: BTP Subaccount
+ Resource: btp_subaccount.subaccount
+ ID: af3b4e1c-b28d-4c6d-9e4a-3e7ffa725ed3
+ ✓ SUCCESS
+
+Importing: Cloud Foundry Environment Instance
+ Resource: btp_subaccount_environment_instance.cloudfoundry[0]
+ ID: af3b4e1c-b28d-4c6d-9e4a-3e7ffa725ed3,8EE92B2C-120D-4988-931A-598EC72E5273
+ ✓ SUCCESS
+
+Importing: CF Service: destination.lite
+ Resource: btp_subaccount_service_instance.cf_service["destination-lite-lite"]
+ ID: af3b4e1c-b28d-4c6d-9e4a-3e7ffa725ed3,040e5544-2923-4ef5-a00b-99afdb7b4005
+ ✓ SUCCESS
+
+=== Import Summary ===
+
+Successful imports (4):
+ ✓ BTP Subaccount
+ ✓ Cloud Foundry Environment Instance
+ ✓ CF Service: destination.lite
+ ✓ CF Service: xsuaa.application
+
+Next steps:
+ 1. Run 'tofu plan' to verify the state
+ 2. Run 'tofu apply' to create any remaining resources
+```
+
+## Requirements
+
+**Common (All Platforms):**
+- `tofu` (OpenTofu) installed and configured
+- Valid BTP provider credentials (set via environment variables)
+- Existing `terraform.tfvars` with configuration
+- BTP CLI (`btp`) for manual ID lookup (if starting from empty state)
+- Cloud Foundry CLI (`cf`) for service instance GUIDs (if importing CF services)
+
+**Bash Script (Linux/macOS):**
+- Bash 3.2+ (macOS default) or higher
+- `jq` for JSON parsing (version 1.6+)
+
+**PowerShell Script (Windows):**
+- PowerShell 5.1+ or PowerShell Core 7+
+
+## Workflow
+
+### Initial Import (No State)
+
+**Bash:**
+```bash
+# 1. Ensure terraform.tfvars exists with correct configuration
+cat terraform.tfvars
+
+# 2. Run the import script
+./import-resources.sh
+
+# 3. Verify the imported resources
+tofu state list
+
+# 4. Check what still needs to be created
+tofu plan
+
+# 5. Apply remaining resources (entitlements, role assignments)
+tofu apply
+```
+
+**PowerShell:**
+```powershell
+# 1. Ensure terraform.tfvars exists with correct configuration
+Get-Content terraform.tfvars
+
+# 2. Run the import script
+./import-resources.ps1
+
+# 3. Verify the imported resources
+tofu state list
+
+# 4. Check what still needs to be created
+tofu plan
+
+# 5. Apply remaining resources (entitlements, role assignments)
+tofu apply
+```
+
+### Re-running (State Exists)
+
+**Bash:**
+```bash
+./import-resources.sh
+```
+
+**PowerShell:**
+```powershell
+./import-resources.ps1
+```
+
+Output:
+```
+⊙ ALREADY IMPORTED (skipping)
+```
+
+Both scripts are idempotent and safe to run multiple times.
+
+## Configuration Examples
+
+### Minimal Configuration
+```hcl
+# terraform.tfvars
+globalaccount = "myaccount"
+project_identifier = "myproject"
+region = "eu10"
+```
+
+Imports: Subaccount only
+
+### With Cloud Foundry
+```hcl
+# terraform.tfvars
+globalaccount = "myaccount"
+project_identifier = "myproject"
+region = "eu10"
+enable_cloudfoundry = true
+cloudfoundry_plan = "standard"
+```
+
+Imports: Subaccount + CF environment
+
+### With CF Services
+```hcl
+# terraform.tfvars
+globalaccount = "myaccount"
+project_identifier = "myproject"
+region = "eu10"
+enable_cloudfoundry = true
+cf_services = "destination.lite,xsuaa.application,postgresql.small"
+```
+
+Imports: Subaccount + CF environment + 3 service instances
+
+## Troubleshooting
+
+### "Could not discover subaccount ID"
+When state is empty, the script will prompt for manual input.
+
+Find your IDs using the BTP CLI:
+```bash
+# Subaccount ID
+btp list accounts/subaccount
+
+# CF Environment ID (8EE92... format)
+btp list accounts/environment-instance --subaccount
+
+# CF Service Instance IDs (040e5... format)
+cf services
+cf service --guid
+```
+
+### "Service instance not found"
+The service instance might not exist yet or the name doesn't match.
+
+Check outputs:
+```bash
+tofu output -json | jq '.cloudfoundry_services.value'
+```
+
+### Import fails with "already managed by Terraform"
+Resource is already in state. The script should detect this, but if not:
+```bash
+tofu state list | grep
+```
+
+## Technical Details
+
+### Resource Discovery Logic
+
+**Bash Script:**
+1. **State (Primary)**
+ ```bash
+ tofu show -json | jq -r '.values.root_module.resources[]...'
+ ```
+
+2. **Manual Input (If State Empty)**
+ ```bash
+ read -p "Enter Subaccount ID: "
+ ```
+
+**PowerShell Script:**
+1. **State (Primary)**
+ ```powershell
+ tofu show -json | ConvertFrom-Json
+ $stateJson.values.root_module.resources | Where-Object {...}
+ ```
+
+2. **Manual Input (If State Empty)**
+ ```powershell
+ Read-Host "Enter Subaccount ID"
+ ```
+
+### Resource Naming Pattern
+
+CF services follow this pattern from `locals.tf`:
+```
+cf_services = "destination.lite,xsuaa.application"
+ ↓
+Resource key: "destination-lite-lite"
+Instance name: "destination-lite"
+```
+
+## Limitations
+
+- **Role assignments** cannot be imported (SAP BTP provider limitation)
+- **Entitlements** don't need import (managed declaratively)
+- Bash script requires **jq** for JSON parsing
+- PowerShell script requires **PowerShell 5.1+**
+
+## Exit Codes
+
+- `0` - All imports successful
+- `1` - One or more imports failed
+
+## Platform Notes
+
+### macOS/Linux
+- Uses bash 3.2+ compatible syntax (macOS default shell)
+- Requires `jq` for JSON parsing: `brew install jq`
+
+### Windows
+- PowerShell 5.1+ included by default in Windows 10+
+- PowerShell Core 7+ recommended for cross-platform consistency
+- Native JSON parsing with `ConvertFrom-Json`
+
+## See Also
+
+- [import-resources.sh](./import-resources.sh) - Bash script for Linux/macOS
+- [import-resources.ps1](./import-resources.ps1) - PowerShell script for Windows
+- [terraform.tfvars](./terraform.tfvars) - Configuration file
+- [main.tf](./main.tf) - Resource definitions
diff --git a/modules/sapbtp/subaccount/buildingblock/import-resources.ps1 b/modules/sapbtp/subaccount/buildingblock/import-resources.ps1
new file mode 100644
index 0000000..b0c05bb
--- /dev/null
+++ b/modules/sapbtp/subaccount/buildingblock/import-resources.ps1
@@ -0,0 +1,246 @@
+#!/usr/bin/env pwsh
+# SAP BTP Dynamic Resource Import Script for PowerShell
+# Automatically discovers and imports existing SAP BTP resources into OpenTofu state
+
+$ErrorActionPreference = "Stop"
+
+Write-Host "=== SAP BTP Dynamic Resource Import Script ===" -ForegroundColor Cyan
+Write-Host ""
+Write-Host "This script will automatically discover and import ALL existing SAP BTP resources."
+Write-Host ""
+
+# Check if terraform.tfvars exists
+if (-not (Test-Path "terraform.tfvars")) {
+ Write-Host "Error: terraform.tfvars not found in current directory" -ForegroundColor Red
+ exit 1
+}
+
+# Extract values from terraform.tfvars using tofu console
+Write-Host "Reading configuration from terraform.tfvars..."
+
+$PROJECT_ID = (Write-Output 'var.project_identifier' | tofu console 2>$null) -replace '"', ''
+$ENABLE_CF = (Write-Output 'var.enable_cloudfoundry' | tofu console 2>$null) -replace '"', ''
+$CF_SERVICES = (Write-Output 'var.cf_services' | tofu console 2>$null) -replace '"', ''
+$ENTITLEMENTS = (Write-Output 'var.entitlements' | tofu console 2>$null) -replace '"', ''
+$USERS = Write-Output 'var.users' | tofu console 2>$null
+
+Write-Host " Project Identifier: $PROJECT_ID"
+Write-Host " Cloud Foundry Enabled: $ENABLE_CF"
+Write-Host " CF Services: $CF_SERVICES"
+Write-Host " Entitlements: $ENTITLEMENTS"
+Write-Host ""
+
+# Get resource IDs - try state first, then prompt for manual input
+Write-Host "Discovering resource IDs..."
+
+# Get subaccount ID
+$stateJson = tofu show -json 2>$null | ConvertFrom-Json -ErrorAction SilentlyContinue
+$SUBACCOUNT_ID = ($stateJson.values.root_module.resources | Where-Object { $_.type -eq "btp_subaccount" -and $_.name -eq "subaccount" } | Select-Object -First 1).values.id
+
+if (-not $SUBACCOUNT_ID) {
+ Write-Host ""
+ Write-Host "Subaccount ID not found in state."
+ $SUBACCOUNT_ID = Read-Host "Enter Subaccount ID"
+}
+
+Write-Host " Subaccount ID: $SUBACCOUNT_ID"
+
+# Get CF environment ID if enabled
+$CF_ENV_ID = ""
+if ($ENABLE_CF -eq "true") {
+ $CF_ENV_ID = ($stateJson.values.root_module.resources | Where-Object { $_.type -eq "btp_subaccount_environment_instance" -and $_.name -eq "cloudfoundry" } | Select-Object -First 1).values.id
+
+ if (-not $CF_ENV_ID) {
+ Write-Host ""
+ Write-Host "Cloud Foundry Environment ID not found in state."
+ Write-Host "You can find it with: btp list accounts/environment-instance --subaccount $SUBACCOUNT_ID"
+ $CF_ENV_ID = Read-Host "Enter CF Environment ID"
+ }
+
+ Write-Host " CF Environment ID: $CF_ENV_ID"
+}
+
+# Get CF service instance IDs - build hashtable for lookup
+Write-Host " Discovering CF service instances..."
+$CF_SERVICE_IDS = @{}
+
+$cfServiceResources = $stateJson.values.root_module.resources | Where-Object { $_.type -eq "btp_subaccount_service_instance" -and $_.name -eq "cf_service" }
+foreach ($resource in $cfServiceResources) {
+ if ($resource.index -and $resource.values.id) {
+ $CF_SERVICE_IDS[$resource.index] = $resource.values.id
+ }
+}
+
+# Check if we found any in state
+if ($CF_SERVICE_IDS.Count -eq 0 -and $CF_SERVICES -and $CF_SERVICES -ne '""') {
+ Write-Host ""
+ Write-Host "No CF service instances found in state."
+ Write-Host "You can find them with: btp list services/instance --subaccount $SUBACCOUNT_ID"
+ Write-Host ""
+ Write-Host "Please enter service instance IDs for each service:"
+
+ # Parse services and prompt for each ID
+ $serviceArray = $CF_SERVICES -split ',' | ForEach-Object { $_.Trim() }
+
+ foreach ($serviceEntry in $serviceArray) {
+ if ($serviceEntry) {
+ $serviceName, $planName = $serviceEntry -split '\.'
+ $instanceName = "$serviceName-$planName"
+ $resourceKey = "$instanceName-$planName"
+
+ $serviceId = Read-Host " Enter ID for $serviceName.$planName (name: $instanceName)"
+ $CF_SERVICE_IDS[$resourceKey] = $serviceId
+ }
+ }
+}
+
+Write-Host ""
+
+$FAILED_IMPORTS = @()
+$SUCCESSFUL_IMPORTS = @()
+
+function Import-Resource {
+ param(
+ [string]$ResourceAddress,
+ [string]$ResourceId,
+ [string]$Description
+ )
+
+ Write-Host "Importing: $Description"
+ Write-Host " Resource: $ResourceAddress"
+ Write-Host " ID: $ResourceId"
+
+ # Check if already imported
+ $stateCheck = tofu state show $ResourceAddress 2>$null
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host " ⊙ ALREADY IMPORTED (skipping)" -ForegroundColor Yellow
+ $script:SUCCESSFUL_IMPORTS += "$Description (already imported)"
+ Write-Host ""
+ return $true
+ }
+
+ $importResult = tofu import $ResourceAddress $ResourceId 2>$null
+ if ($LASTEXITCODE -eq 0) {
+ $script:SUCCESSFUL_IMPORTS += $Description
+ Write-Host " ✓ SUCCESS" -ForegroundColor Green
+ } else {
+ $script:FAILED_IMPORTS += $Description
+ Write-Host " ✗ FAILED" -ForegroundColor Red
+ }
+ Write-Host ""
+ return ($LASTEXITCODE -eq 0)
+}
+
+Write-Host "Starting imports..."
+Write-Host ""
+
+# Import subaccount
+Import-Resource -ResourceAddress "btp_subaccount.subaccount" -ResourceId $SUBACCOUNT_ID -Description "BTP Subaccount"
+
+# Import entitlements
+if ($ENTITLEMENTS -and $ENTITLEMENTS -ne '""' -and $ENTITLEMENTS -ne "") {
+ Write-Host "Importing entitlements..."
+
+ # Parse entitlements (format: service.plan,service.plan)
+ $entitlementArray = $ENTITLEMENTS -split ',' | ForEach-Object { $_.Trim() }
+
+ foreach ($entitlementEntry in $entitlementArray) {
+ if ($entitlementEntry) {
+ $serviceName, $planName = $entitlementEntry -split '\.'
+ $resourceKey = "$serviceName-$planName"
+
+ # Entitlement import ID format: subaccount_id,service_name,plan_name
+ Import-Resource `
+ -ResourceAddress "btp_subaccount_entitlement.entitlement_without_quota[`"$resourceKey`"]" `
+ -ResourceId "$SUBACCOUNT_ID,$serviceName,$planName" `
+ -Description "Entitlement: $serviceName.$planName"
+ }
+ }
+}
+
+# Import Cloud Foundry environment if enabled
+if ($ENABLE_CF -eq "true" -and $CF_ENV_ID) {
+ Import-Resource `
+ -ResourceAddress "btp_subaccount_environment_instance.cloudfoundry[0]" `
+ -ResourceId "$SUBACCOUNT_ID,$CF_ENV_ID" `
+ -Description "Cloud Foundry Environment Instance"
+}
+
+# Import CF service instances
+if ($CF_SERVICES -and $CF_SERVICES -ne '""' -and $CF_SERVICES -ne "") {
+ Write-Host "Importing CF service instances..."
+
+ # Parse services from cf_services variable (format: service.plan,service.plan)
+ $serviceArray = $CF_SERVICES -split ',' | ForEach-Object { $_.Trim() }
+
+ foreach ($serviceEntry in $serviceArray) {
+ if ($serviceEntry) {
+ # service.plan -> name-plan format (e.g., destination.lite -> destination-lite)
+ $serviceName, $planName = $serviceEntry -split '\.'
+ $instanceName = "$serviceName-$planName"
+
+ # The resource key is name-plan-plan (e.g., destination-lite-lite)
+ $resourceKey = "$instanceName-$planName"
+
+ # Get instance ID from hashtable
+ $instanceId = $CF_SERVICE_IDS[$resourceKey]
+
+ if ($instanceId) {
+ Import-Resource `
+ -ResourceAddress "btp_subaccount_service_instance.cf_service[`"$resourceKey`"]" `
+ -ResourceId "$SUBACCOUNT_ID,$instanceId" `
+ -Description "CF Service: $serviceName.$planName"
+ } else {
+ Write-Host " ⚠ CF Service instance $resourceKey not found in state (may need to be created)" -ForegroundColor Yellow
+ }
+ }
+ }
+}
+
+# Note about resources that cannot be imported
+Write-Host "=== Resources that cannot be imported ===" -ForegroundColor Cyan
+Write-Host ""
+Write-Host "The following resources cannot be imported per SAP BTP provider design:"
+Write-Host " • Role collection assignments (will be managed on next apply)"
+Write-Host ""
+
+# Show which role assignments will be created
+if ($USERS -and $USERS -ne "[]") {
+ Write-Host "Role assignments to be created:"
+ try {
+ $usersObj = $USERS | ConvertFrom-Json
+ foreach ($user in $usersObj) {
+ $roles = $user.roles -join ", "
+ Write-Host " • $($user.euid) - role: $roles"
+ }
+ } catch {
+ Write-Host " (Cannot parse users)"
+ }
+}
+
+Write-Host ""
+
+Write-Host "=== Import Summary ===" -ForegroundColor Cyan
+Write-Host ""
+Write-Host "Successful imports ($($SUCCESSFUL_IMPORTS.Count)):" -ForegroundColor Green
+foreach ($item in $SUCCESSFUL_IMPORTS) {
+ Write-Host " ✓ $item"
+}
+Write-Host ""
+
+if ($FAILED_IMPORTS.Count -gt 0) {
+ Write-Host "Failed imports ($($FAILED_IMPORTS.Count)):" -ForegroundColor Red
+ foreach ($item in $FAILED_IMPORTS) {
+ Write-Host " ✗ $item"
+ }
+ Write-Host ""
+}
+
+Write-Host "Next steps:"
+Write-Host " 1. Run 'tofu plan' to verify the state"
+Write-Host " 2. Run 'tofu apply' to create role collection assignments"
+Write-Host ""
+
+if ($FAILED_IMPORTS.Count -gt 0) {
+ exit 1
+}
diff --git a/modules/sapbtp/subaccount/buildingblock/import-resources.sh b/modules/sapbtp/subaccount/buildingblock/import-resources.sh
new file mode 100755
index 0000000..67725c1
--- /dev/null
+++ b/modules/sapbtp/subaccount/buildingblock/import-resources.sh
@@ -0,0 +1,109 @@
+#!/usr/bin/env bash
+
+set -e
+
+echo "=== SAP BTP Subaccount Import Script ==="
+echo ""
+echo "This script imports an existing SAP BTP subaccount and user role assignments."
+echo ""
+
+if [ ! -f "terraform.tfvars" ]; then
+ echo "Error: terraform.tfvars not found in current directory"
+ exit 1
+fi
+
+echo "Reading configuration from terraform.tfvars..."
+
+PROJECT_ID=$(tofu console <<< 'var.project_identifier' 2>/dev/null | tr -d '"')
+USERS=$(tofu console <<< 'var.users' 2>/dev/null)
+
+echo " Project Identifier: $PROJECT_ID"
+echo ""
+
+echo "Discovering resource IDs..."
+
+SUBACCOUNT_ID=$(tofu show -json 2>/dev/null | jq -r '.values.root_module.resources[] | select(.type == "btp_subaccount" and .name == "subaccount") | .values.id' 2>/dev/null || echo "")
+
+if [ -z "$SUBACCOUNT_ID" ]; then
+ echo ""
+ echo "Subaccount ID not found in state."
+ read -p "Enter Subaccount ID: " SUBACCOUNT_ID
+fi
+
+echo " Subaccount ID: $SUBACCOUNT_ID"
+echo ""
+
+FAILED_IMPORTS=()
+SUCCESSFUL_IMPORTS=()
+
+import_resource() {
+ local resource_address="$1"
+ local resource_id="$2"
+ local description="$3"
+
+ echo "Importing: $description"
+ echo " Resource: $resource_address"
+ echo " ID: $resource_id"
+
+ if tofu state show "$resource_address" >/dev/null 2>&1; then
+ echo " ⊙ ALREADY IMPORTED (skipping)"
+ SUCCESSFUL_IMPORTS+=("$description (already imported)")
+ echo ""
+ return 0
+ fi
+
+ if tofu import "$resource_address" "$resource_id" >/dev/null 2>&1; then
+ SUCCESSFUL_IMPORTS+=("$description")
+ echo " ✓ SUCCESS"
+ else
+ FAILED_IMPORTS+=("$description")
+ echo " ✗ FAILED"
+ fi
+ echo ""
+}
+
+echo "Starting imports..."
+echo ""
+
+import_resource \
+ "btp_subaccount.subaccount" \
+ "$SUBACCOUNT_ID" \
+ "BTP Subaccount"
+
+echo "=== Resources that cannot be imported ==="
+echo ""
+echo "The following resources cannot be imported per SAP BTP provider design:"
+echo " • Role collection assignments (will be managed on next apply)"
+echo ""
+
+if [ -n "$USERS" ] && [ "$USERS" != "[]" ]; then
+ echo "Role assignments to be created:"
+ echo "$USERS" | jq -r '.[] | " • \(.euid) - role: \(.roles | join(", "))"' 2>/dev/null || echo " (Cannot parse users)"
+fi
+
+echo ""
+
+echo "=== Import Summary ==="
+echo ""
+echo "Successful imports (${#SUCCESSFUL_IMPORTS[@]}):"
+for item in "${SUCCESSFUL_IMPORTS[@]}"; do
+ echo " ✓ $item"
+done
+echo ""
+
+if [ ${#FAILED_IMPORTS[@]} -gt 0 ]; then
+ echo "Failed imports (${#FAILED_IMPORTS[@]}):"
+ for item in "${FAILED_IMPORTS[@]}"; do
+ echo " ✗ $item"
+ done
+ echo ""
+fi
+
+echo "Next steps:"
+echo " 1. Run 'tofu plan' to verify the state"
+echo " 2. Run 'tofu apply' to create role collection assignments"
+echo ""
+
+if [ ${#FAILED_IMPORTS[@]} -gt 0 ]; then
+ exit 1
+fi
diff --git a/modules/sapbtp/subaccount/buildingblock/locals.tf b/modules/sapbtp/subaccount/buildingblock/locals.tf
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/modules/sapbtp/subaccount/buildingblock/locals.tf
@@ -0,0 +1 @@
+
diff --git a/modules/sapbtp/subaccounts/buildingblock/main.tf b/modules/sapbtp/subaccount/buildingblock/main.tf
similarity index 84%
rename from modules/sapbtp/subaccounts/buildingblock/main.tf
rename to modules/sapbtp/subaccount/buildingblock/main.tf
index 9bcc1ca..37e5606 100644
--- a/modules/sapbtp/subaccounts/buildingblock/main.tf
+++ b/modules/sapbtp/subaccount/buildingblock/main.tf
@@ -1,12 +1,10 @@
data "btp_directories" "all" {}
-# iterate through the list of users and redue to a map of user with only their euid
locals {
reader = { for user in var.users : user.euid => user if contains(user.roles, "reader") }
admin = { for user in var.users : user.euid => user if contains(user.roles, "admin") }
user = { for user in var.users : user.euid => user if contains(user.roles, "user") }
-
subfolders = [
for dir in data.btp_directories.all.values : {
id = dir.id
@@ -14,7 +12,8 @@ locals {
}
]
- selected_subfolder_id = try(
+ # Support both subfolder name (meshStack pattern) and parent_id (import pattern)
+ selected_subfolder_id = var.parent_id != "" ? var.parent_id : try(
one([
for sf in local.subfolders : sf.id
if sf.name == var.subfolder
@@ -37,7 +36,6 @@ resource "btp_subaccount_role_collection_assignment" "subaccount_admin" {
user_name = each.key
}
-# btp_subaccount_role_collection_assignment.subaccount_admin_sysuser will be created
resource "btp_subaccount_role_collection_assignment" "subaccount_service_admininstrator" {
for_each = local.user
role_collection_name = "Subaccount Service Administrator"
@@ -45,7 +43,6 @@ resource "btp_subaccount_role_collection_assignment" "subaccount_service_adminin
user_name = each.key
}
-# btp_subaccount_role_collection_assignment.subaccount_viewer will be created
resource "btp_subaccount_role_collection_assignment" "subaccount_viewer" {
for_each = local.reader
role_collection_name = "Subaccount Viewer"
diff --git a/modules/sapbtp/subaccount/buildingblock/outputs.tf b/modules/sapbtp/subaccount/buildingblock/outputs.tf
new file mode 100644
index 0000000..ac94fe7
--- /dev/null
+++ b/modules/sapbtp/subaccount/buildingblock/outputs.tf
@@ -0,0 +1,24 @@
+output "subaccount_id" {
+ description = "The ID of the created subaccount"
+ value = btp_subaccount.subaccount.id
+}
+
+output "subaccount_region" {
+ description = "The region of the subaccount"
+ value = btp_subaccount.subaccount.region
+}
+
+output "subaccount_name" {
+ description = "The name of the subaccount"
+ value = btp_subaccount.subaccount.name
+}
+
+output "subaccount_subdomain" {
+ description = "The subdomain of the subaccount"
+ value = btp_subaccount.subaccount.subdomain
+}
+
+output "subaccount_login_link" {
+ description = "Link to the subaccount in the SAP BTP cockpit"
+ value = "https://emea.cockpit.btp.cloud.sap/cockpit#/globalaccount/${btp_subaccount.subaccount.parent_id}/subaccount/${btp_subaccount.subaccount.id}"
+}
diff --git a/modules/sapbtp/subaccount/buildingblock/subaccounts.tftest.hcl b/modules/sapbtp/subaccount/buildingblock/subaccounts.tftest.hcl
new file mode 100644
index 0000000..a304b4a
--- /dev/null
+++ b/modules/sapbtp/subaccount/buildingblock/subaccounts.tftest.hcl
@@ -0,0 +1,205 @@
+run "verify_basic_subaccount" {
+ variables {
+ globalaccount = "meshcloudgmbh"
+ project_identifier = "testsubaccount-basic"
+ subfolder = "test"
+ region = "eu10"
+ users = [
+ {
+ meshIdentifier = "likvid-tom-user"
+ username = "likvid-tom@meshcloud.io"
+ firstName = "Tom"
+ lastName = "Livkid"
+ email = "likvid-tom@meshcloud.io"
+ euid = "likvid-tom@meshcloud.io"
+ roles = ["admin", "Workspace Owner"]
+ },
+ {
+ meshIdentifier = "likvid-daniela-user"
+ username = "likvid-daniela@meshcloud.io"
+ firstName = "Daniela"
+ lastName = "Livkid"
+ email = "likvid-daniela@meshcloud.io"
+ euid = "likvid-daniela@meshcloud.io"
+ roles = ["user", "Workspace Manager"]
+ },
+ {
+ meshIdentifier = "likvid-anna-user"
+ username = "likvid-anna@meshcloud.io"
+ firstName = "Anna"
+ lastName = "Livkid"
+ email = "likvid-anna@meshcloud.io"
+ euid = "likvid-anna@meshcloud.io"
+ roles = ["reader", "Workspace Member"]
+ }
+ ]
+ }
+
+ assert {
+ condition = length(var.users) == 3
+ error_message = "Should have 3 users configured"
+ }
+
+ assert {
+ condition = btp_subaccount.subaccount.name == "testsubaccount-basic"
+ error_message = "Subaccount name should match project_identifier"
+ }
+
+ assert {
+ condition = btp_subaccount.subaccount.region == "eu10"
+ error_message = "Subaccount region should be eu10"
+ }
+}
+
+run "verify_role_assignments" {
+ variables {
+ globalaccount = "meshcloudgmbh"
+ project_identifier = "testsubaccount-roles"
+ users = [
+ {
+ meshIdentifier = "admin-user"
+ username = "admin@meshcloud.io"
+ firstName = "Admin"
+ lastName = "User"
+ email = "admin@meshcloud.io"
+ euid = "admin@meshcloud.io"
+ roles = ["admin"]
+ },
+ {
+ meshIdentifier = "service-admin-user"
+ username = "service@meshcloud.io"
+ firstName = "Service"
+ lastName = "User"
+ email = "service@meshcloud.io"
+ euid = "service@meshcloud.io"
+ roles = ["user"]
+ },
+ {
+ meshIdentifier = "viewer-user"
+ username = "viewer@meshcloud.io"
+ firstName = "Viewer"
+ lastName = "User"
+ email = "viewer@meshcloud.io"
+ euid = "viewer@meshcloud.io"
+ roles = ["reader"]
+ }
+ ]
+ }
+
+ assert {
+ condition = length(btp_subaccount_role_collection_assignment.subaccount_admin) == 1
+ error_message = "Should have 1 Subaccount Administrator assignment"
+ }
+
+ assert {
+ condition = length(btp_subaccount_role_collection_assignment.subaccount_service_admininstrator) == 1
+ error_message = "Should have 1 Subaccount Service Administrator assignment"
+ }
+
+ assert {
+ condition = length(btp_subaccount_role_collection_assignment.subaccount_viewer) == 1
+ error_message = "Should have 1 Subaccount Viewer assignment"
+ }
+}
+
+run "verify_minimal_configuration" {
+ variables {
+ globalaccount = "meshcloudgmbh"
+ project_identifier = "testsubaccount-minimal"
+ }
+
+ assert {
+ condition = btp_subaccount.subaccount.name == "testsubaccount-minimal"
+ error_message = "Subaccount should be created even with minimal config"
+ }
+
+ assert {
+ condition = var.region == "eu10"
+ error_message = "Should use default region eu10"
+ }
+
+ assert {
+ condition = var.subfolder == ""
+ error_message = "Should use empty subfolder by default"
+ }
+
+ assert {
+ condition = length(var.users) == 0
+ error_message = "Should have no users by default"
+ }
+}
+
+run "verify_subfolder_selection" {
+ variables {
+ globalaccount = "meshcloudgmbh"
+ project_identifier = "testsubaccount-folder"
+ subfolder = "Development"
+ region = "us10"
+ }
+
+ assert {
+ condition = var.subfolder == "Development"
+ error_message = "Subfolder should be set to Development"
+ }
+
+ assert {
+ condition = btp_subaccount.subaccount.region == "us10"
+ error_message = "Subaccount region should be us10"
+ }
+}
+
+run "verify_outputs" {
+ variables {
+ globalaccount = "meshcloudgmbh"
+ project_identifier = "testsubaccount-outputs"
+ }
+
+ assert {
+ condition = output.subaccount_id != ""
+ error_message = "Should output subaccount_id"
+ }
+
+ assert {
+ condition = output.subaccount_name == "testsubaccount-outputs"
+ error_message = "Should output correct subaccount_name"
+ }
+
+ assert {
+ condition = output.subaccount_subdomain == "testsubaccount-outputs"
+ error_message = "Should output correct subaccount_subdomain"
+ }
+
+ assert {
+ condition = output.subaccount_region != ""
+ error_message = "Should output subaccount_region"
+ }
+
+ assert {
+ condition = can(regex("^https://.*", output.subaccount_login_link))
+ error_message = "Should output valid login link URL"
+ }
+}
+
+run "verify_parent_id_import_pattern" {
+ variables {
+ globalaccount = "meshcloudgmbh"
+ project_identifier = "testsubaccount-import"
+ parent_id = "9b8960a6-b80a-4096-80e5-a61bea98ac48"
+ region = "eu30"
+ }
+
+ assert {
+ condition = var.parent_id != ""
+ error_message = "Parent ID should be set for import pattern"
+ }
+
+ assert {
+ condition = btp_subaccount.subaccount.parent_id == "9b8960a6-b80a-4096-80e5-a61bea98ac48"
+ error_message = "Subaccount should use parent_id directly when provided"
+ }
+
+ assert {
+ condition = var.subfolder == ""
+ error_message = "Subfolder should be empty when using parent_id"
+ }
+}
diff --git a/modules/sapbtp/subaccounts/buildingblock/variables.tf b/modules/sapbtp/subaccount/buildingblock/variables.tf
similarity index 61%
rename from modules/sapbtp/subaccounts/buildingblock/variables.tf
rename to modules/sapbtp/subaccount/buildingblock/variables.tf
index 38a97af..519e597 100644
--- a/modules/sapbtp/subaccounts/buildingblock/variables.tf
+++ b/modules/sapbtp/subaccount/buildingblock/variables.tf
@@ -5,15 +5,10 @@ variable "globalaccount" {
variable "region" {
type = string
- default = "eu30"
+ default = "eu10"
description = "The region of the subaccount."
}
-variable "workspace_identifier" {
- type = string
- description = "The meshStack workspace identifier."
-}
-
variable "project_identifier" {
type = string
description = "The meshStack project identifier."
@@ -21,12 +16,16 @@ variable "project_identifier" {
variable "subfolder" {
type = string
- description = "The subfolder to use for the SAP BTP resources. This is used to create a folder structure in the SAP BTP cockpit."
+ default = ""
+ description = "The subfolder name to use for the SAP BTP resources. This is used to create a folder structure in the SAP BTP cockpit. Mutually exclusive with parent_id."
+}
+
+variable "parent_id" {
+ type = string
+ default = ""
+ description = "The parent directory ID for the subaccount. Use this when importing existing subaccounts. Mutually exclusive with subfolder."
}
-# note: these permissions are passed in from meshStack and automatically updated whenever something changes
-# atm. we are not using them inside this building block implementation, but they give us a trigger to often reconcile
-# the permissions
variable "users" {
type = list(object(
{
diff --git a/modules/sapbtp/subaccount/buildingblock_import/outputs.tf b/modules/sapbtp/subaccount/buildingblock_import/outputs.tf
new file mode 100644
index 0000000..73b633b
--- /dev/null
+++ b/modules/sapbtp/subaccount/buildingblock_import/outputs.tf
@@ -0,0 +1,45 @@
+output "btp_subaccount_id" {
+ value = "initial state creation"
+}
+
+output "btp_subaccount_region" {
+ value = "initial state creation"
+}
+
+output "btp_subaccount_name" {
+ value = "initial state creation"
+}
+
+output "btp_subaccount_login_link" {
+ value = "initial state creation"
+}
+
+output "entitlements" {
+ description = "Map of entitlements created for this subaccount"
+ value = "initial state creation"
+}
+
+output "subscriptions" {
+ description = "Map of application subscriptions created in this subaccount"
+ value = "initial state creation"
+}
+
+output "cloudfoundry_instance_id" {
+ description = "ID of the Cloud Foundry environment instance (if created)"
+ value = "initial state creation"
+}
+
+output "cloudfoundry_instance_state" {
+ description = "State of the Cloud Foundry environment instance (if created)"
+ value = "initial state creation"
+}
+
+output "trust_configuration_origin" {
+ description = "Origin key of the configured trust configuration (if configured)"
+ value = "initial state creation"
+}
+
+output "cloudfoundry_services" {
+ description = "Map of Cloud Foundry service instances created in this subaccount"
+ value = "initial state creation"
+}
diff --git a/modules/sapbtp/subaccount/buildingblock_import/variables.tf b/modules/sapbtp/subaccount/buildingblock_import/variables.tf
new file mode 100644
index 0000000..b6421c2
--- /dev/null
+++ b/modules/sapbtp/subaccount/buildingblock_import/variables.tf
@@ -0,0 +1,79 @@
+variable "globalaccount" {
+ type = string
+ description = "The subdomain of the global account in which you want to manage resources."
+}
+
+variable "region" {
+ type = string
+ default = "eu10"
+ description = "The region of the subaccount."
+}
+
+variable "project_identifier" {
+ type = string
+ description = "The meshStack project identifier."
+}
+
+variable "subfolder" {
+ type = string
+ default = ""
+ description = "The subfolder to use for the SAP BTP resources. This is used to create a folder structure in the SAP BTP cockpit."
+}
+
+variable "users" {
+ type = list(object(
+ {
+ meshIdentifier = string
+ username = string
+ firstName = string
+ lastName = string
+ email = string
+ euid = string
+ roles = list(string)
+ }
+ ))
+ description = "Users and their roles provided by meshStack"
+ default = []
+}
+
+variable "entitlements" {
+ type = string
+ default = ""
+ description = "Comma-separated list of service entitlements in format: service.plan (e.g., 'postgresql-db.trial,destination.lite,xsuaa.application')"
+}
+
+variable "subscriptions" {
+ type = string
+ default = ""
+ description = "Comma-separated list of application subscriptions in format: app.plan (e.g., 'build-workzone.standard,integrationsuite.enterprise_agreement')"
+}
+
+variable "enable_cloudfoundry" {
+ type = bool
+ default = false
+ description = "Enable Cloud Foundry environment in the subaccount"
+}
+
+variable "cloudfoundry_plan" {
+ type = string
+ default = "standard"
+ description = "Cloud Foundry environment plan (standard or trial)"
+}
+
+variable "cloudfoundry_space_name" {
+ type = string
+ default = "dev"
+ description = "Name for the Cloud Foundry space"
+}
+
+variable "cf_services" {
+ type = string
+ default = ""
+ description = "Comma-separated list of Cloud Foundry service instances in format: service.plan (e.g., 'postgresql.small,destination.lite,redis.medium')"
+}
+
+variable "identity_provider" {
+ type = string
+ default = ""
+ description = "Custom identity provider origin (e.g., mytenant.accounts.ondemand.com). Leave empty to skip trust configuration."
+}
diff --git a/modules/sapbtp/subaccounts/buildingblock/APP_TEAM_README.md b/modules/sapbtp/subaccounts/buildingblock/APP_TEAM_README.md
deleted file mode 100644
index a727907..0000000
--- a/modules/sapbtp/subaccounts/buildingblock/APP_TEAM_README.md
+++ /dev/null
@@ -1,20 +0,0 @@
-This building block provisions a SAP BTP subaccount. It provides a self-service way for application teams to create isolated environments within SAP BTP.
-
-**Usage Motivation**
-
-This building block is designed for application teams that need to develop and deploy applications on the SAP Business Technology Platform (BTP). It allows developers to quickly provision and manage their own isolated BTP subaccounts without requiring manual intervention from the platform team. Use this building block when you need a new, isolated environment for your SAP BTP project.
-
-**Usage Examples**
-
-* A developer wants to create a new subaccount for a proof-of-concept project. They use this building block to quickly provision a subaccount named "poc-project".
-* An application team wants to create a dedicated subaccount for their production application. They use this building block to provision a subaccount with a specific name that aligns with their project.
-
-**Shared Responsibility**
-
-| Responsibility | Platform Team ✅/❌ | Application Team ✅/❌ |
-| ------------------------- | ------------------- | ---------------------- |
-| Subaccount Creation | ✅ | ❌ |
-| Using the subaccount | ❌ | ✅ |
-| Cost Management | ❌ | ✅ |
-| Security | ❌ | ✅ |
-| Updates | ❌ | ✅ |
diff --git a/modules/sapbtp/subaccounts/buildingblock/README.md b/modules/sapbtp/subaccounts/buildingblock/README.md
deleted file mode 100644
index 88d01b5..0000000
--- a/modules/sapbtp/subaccounts/buildingblock/README.md
+++ /dev/null
@@ -1,66 +0,0 @@
----
-name: SAP BTP subaccount
-supportedPlatforms:
- - sapbtp
-description: |
- This building block Creates a subaccount in SAP BTP.
----
-
-# SAP BTP subaccount with environment configuration
-
-This Terraform module provisions a subaccount in SAP Business Technology Platform (BTP).
-
-## Providers
-
-```hcl
-terraform {
- required_providers {
- btp = {
- source = "SAP/btp"
- version = "~> 1.8.0"
- }
- }
-}
-```
-
-
-## Requirements
-
-| Name | Version |
-|------|---------|
-| [btp](#requirement\_btp) | ~> 1.8.0 |
-
-## Modules
-
-No modules.
-
-## Resources
-
-| Name | Type |
-|------|------|
-| [btp_subaccount.subaccount](https://registry.terraform.io/providers/SAP/btp/latest/docs/resources/subaccount) | resource |
-| [btp_subaccount_role_collection_assignment.subaccount_admin](https://registry.terraform.io/providers/SAP/btp/latest/docs/resources/subaccount_role_collection_assignment) | resource |
-| [btp_subaccount_role_collection_assignment.subaccount_service_admininstrator](https://registry.terraform.io/providers/SAP/btp/latest/docs/resources/subaccount_role_collection_assignment) | resource |
-| [btp_subaccount_role_collection_assignment.subaccount_viewer](https://registry.terraform.io/providers/SAP/btp/latest/docs/resources/subaccount_role_collection_assignment) | resource |
-| [btp_directories.all](https://registry.terraform.io/providers/SAP/btp/latest/docs/data-sources/directories) | data source |
-
-## Inputs
-
-| Name | Description | Type | Default | Required |
-|------|-------------|------|---------|:--------:|
-| [globalaccount](#input\_globalaccount) | The subdomain of the global account in which you want to manage resources. | `string` | n/a | yes |
-| [project\_identifier](#input\_project\_identifier) | The meshStack project identifier. | `string` | n/a | yes |
-| [region](#input\_region) | The region of the subaccount. | `string` | `"eu30"` | no |
-| [subfolder](#input\_subfolder) | The subfolder to use for the SAP BTP resources. This is used to create a folder structure in the SAP BTP cockpit. | `string` | n/a | yes |
-| [users](#input\_users) | Users and their roles provided by meshStack | list(object(
{
meshIdentifier = string
username = string
firstName = string
lastName = string
email = string
euid = string
roles = list(string)
}
))
| `[]` | no |
-| [workspace\_identifier](#input\_workspace\_identifier) | The meshStack workspace identifier. | `string` | n/a | yes |
-
-## Outputs
-
-| Name | Description |
-|------|-------------|
-| [btp\_subaccount\_id](#output\_btp\_subaccount\_id) | n/a |
-| [btp\_subaccount\_login\_link](#output\_btp\_subaccount\_login\_link) | n/a |
-| [btp\_subaccount\_name](#output\_btp\_subaccount\_name) | n/a |
-| [btp\_subaccount\_region](#output\_btp\_subaccount\_region) | n/a |
-
diff --git a/modules/sapbtp/subaccounts/buildingblock/logo.png b/modules/sapbtp/subaccounts/buildingblock/logo.png
deleted file mode 100644
index 663d059..0000000
Binary files a/modules/sapbtp/subaccounts/buildingblock/logo.png and /dev/null differ
diff --git a/modules/sapbtp/subaccounts/buildingblock/outputs.tf b/modules/sapbtp/subaccounts/buildingblock/outputs.tf
deleted file mode 100644
index 6153f83..0000000
--- a/modules/sapbtp/subaccounts/buildingblock/outputs.tf
+++ /dev/null
@@ -1,15 +0,0 @@
-output "btp_subaccount_id" {
- value = btp_subaccount.subaccount.id
-}
-
-output "btp_subaccount_region" {
- value = btp_subaccount.subaccount.region
-}
-
-output "btp_subaccount_name" {
- value = btp_subaccount.subaccount.name
-}
-
-output "btp_subaccount_login_link" {
- value = "https://emea.cockpit.btp.cloud.sap/cockpit#/globalaccount/${btp_subaccount.subaccount.parent_id}/subaccount/${btp_subaccount.subaccount.id}"
-}
diff --git a/modules/sapbtp/subaccounts/buildingblock/provider.tf b/modules/sapbtp/subaccounts/buildingblock/provider.tf
deleted file mode 100644
index 855b309..0000000
--- a/modules/sapbtp/subaccounts/buildingblock/provider.tf
+++ /dev/null
@@ -1,4 +0,0 @@
-provider "btp" {
- globalaccount = var.globalaccount
- # using ENV vars in meshStack for username and password
-}
diff --git a/modules/sapbtp/subaccounts/buildingblock/subaccounts.tftest.hcl b/modules/sapbtp/subaccounts/buildingblock/subaccounts.tftest.hcl
deleted file mode 100644
index 0d26707..0000000
--- a/modules/sapbtp/subaccounts/buildingblock/subaccounts.tftest.hcl
+++ /dev/null
@@ -1,36 +0,0 @@
-run "verify" {
- variables {
-
- aws_account_id = "490004649140"
- parent_id = "99f8ad5f-255e-46a5-a72d-f6d652c90525"
- globalaccount = "meshcloudgmbh"
- workspace_identifier = "sapbtp"
- project_identifier = "testsubaccount"
- users = [
- {
- meshIdentifier = "identifier1"
- username = "testuser1@likvid.io"
- firstName = "test"
- lastName = "user"
- email = "testuser1@likvid.io"
- euid = "testuser1@likvid.io"
- roles = ["admin", "user"]
- },
-
- {
- meshIdentifier = "identifier2"
- username = "testuser2@likvid.io"
- firstName = "test"
- lastName = "user"
- email = "testuser2@likvid.io"
- euid = "testuser2@likvid.io"
- roles = ["admin"]
- }
- ]
- }
-
- assert {
- condition = length(var.users) > 0
- error_message = "No users provided"
- }
-}
diff --git a/modules/sapbtp/subscriptions/buildingblock/APP_TEAM_README.md b/modules/sapbtp/subscriptions/buildingblock/APP_TEAM_README.md
new file mode 100644
index 0000000..51362f8
--- /dev/null
+++ b/modules/sapbtp/subscriptions/buildingblock/APP_TEAM_README.md
@@ -0,0 +1,113 @@
+# SAP BTP Subscriptions - User Guide
+
+## 🎯 What This Building Block Does
+
+Subscribes your subaccount to SaaS applications available in the SAP BTP marketplace. These are turnkey applications you can start using immediately.
+
+## 🚀 Quick Start
+
+Add subscriptions by specifying apps in the format `app.plan`:
+
+```
+subscriptions = "build-workzone.standard,sapappstudio.standard-edition"
+```
+
+## 📋 Popular Applications
+
+### Development Tools
+- `sapappstudio.standard-edition` - Full-featured cloud IDE for SAP development
+- `sap-build-apps.standard` - No-code/low-code app builder
+- `cicd-service.default` - Continuous integration and deployment
+
+### Integration & APIs
+- `integrationsuite.enterprise_agreement` - API management, process integration, events
+- `mobile-services.standard` - Mobile app development and management
+
+### Collaboration
+- `build-workzone.standard` - Digital workplace with sites, workspaces, and content
+
+### Business Tools
+- `business-rules.standard` - Business rules management
+- `workflow.standard` - Workflow and process automation
+- `web-analytics.standard` - Web analytics for apps
+
+## 🔄 Shared Responsibility Matrix
+
+| Responsibility | meshStack | App Team |
+|---------------|-----------|----------|
+| Select applications | | ✅ |
+| Provision subscriptions | ✅ | |
+| Configure applications | | ✅ |
+| Manage application users | | ✅ |
+| Application updates | SAP BTP | |
+| Application support | SAP BTP | |
+
+## 💡 Best Practices
+
+### Check Entitlements First
+Every subscription needs a matching entitlement. Use the **entitlements building block** first:
+```
+# Add entitlement first
+entitlements = "sapappstudio.standard-edition"
+
+# Then subscribe
+subscriptions = "sapappstudio.standard-edition"
+```
+
+### Wait for Provisioning
+Subscriptions can take 5-15 minutes to become ready. Check the BTP Cockpit for status.
+
+### Start with Free/Trial Plans
+Many apps offer free or trial plans:
+- `build-workzone.free` - Free tier
+- `sap-build-apps.free` - Free tier
+- `cicd-service.trial` - Trial version
+
+### Understand the Difference
+- **Subscriptions** = SaaS apps (like Office 365)
+- **Service Instances** = Platform services (like databases) → Use Cloud Foundry building block
+
+## 🔍 Finding Available Apps
+
+1. **BTP Cockpit**: Navigate to Subaccount → Services → Service Marketplace
+2. **Filter by "Subscriptions"**: Shows only subscribable apps
+3. **Check Prerequisites**: Some apps require other services
+
+## ⚠️ Common Issues
+
+### "Entitlement missing"
+Add the required entitlement first using the entitlements building block.
+
+### "Subscription takes forever"
+Some subscriptions provision slowly. Wait 10-15 minutes, then check BTP Cockpit → Instances and Subscriptions.
+
+### "Can't access the application"
+After subscription:
+1. Assign users to the application's role collections in BTP Cockpit
+2. Access the app via the subscription URL (shown in BTP Cockpit)
+
+### "Wrong plan selected"
+Subscriptions can be changed by:
+1. Updating the plan in your config
+2. Running `tofu apply`
+Note: Some plan changes may cause service interruption.
+
+## 📞 Accessing Your Applications
+
+After subscription:
+1. Go to BTP Cockpit → Subaccount → Instances and Subscriptions
+2. Find your application
+3. Click "Go to Application" link
+4. Log in with your SAP BTP user credentials
+
+## 🎓 Next Steps
+
+1. **Assign Users**: Configure role collections for your team
+2. **Configure Apps**: Follow app-specific configuration guides
+3. **Integrate**: Connect apps to your development workflow
+
+## 📞 Getting Help
+
+- Check SAP BTP Service Marketplace for app details
+- Review app documentation in SAP Help Portal
+- Contact platform team for subscription issues
diff --git a/modules/sapbtp/subscriptions/buildingblock/README.md b/modules/sapbtp/subscriptions/buildingblock/README.md
new file mode 100644
index 0000000..63d3979
--- /dev/null
+++ b/modules/sapbtp/subscriptions/buildingblock/README.md
@@ -0,0 +1,137 @@
+---
+name: SAP BTP Subscriptions
+supportedPlatforms:
+ - btp
+description: Manages application subscriptions in an SAP BTP subaccount, enabling access to SaaS applications like SAP Build Work Zone, Integration Suite, and more.
+---
+
+# SAP BTP Subscriptions Building Block
+
+This building block manages application subscriptions for an SAP BTP subaccount. Subscriptions enable access to SaaS applications available in the SAP BTP marketplace.
+
+## Prerequisites
+
+- An existing SAP BTP subaccount
+- Required entitlements for the applications you want to subscribe to
+- SAP BTP authentication configured via environment variables
+
+## Usage
+
+### Creating New Subscriptions
+
+```hcl
+globalaccount = "my-global-account"
+subaccount_id = "ab5dcd3d-c824-4470-a2f6-758d37da52ea"
+subscriptions = "build-workzone.standard,sapappstudio.standard-edition"
+```
+
+### Importing Existing Subscriptions
+
+1. Create a `terraform.tfvars` file with your configuration
+2. Run the import script:
+ ```bash
+ ./import-resources.sh
+ ```
+3. Verify with `tofu plan`
+
+## Common Applications
+
+### Development & Integration
+- `sapappstudio.standard-edition` - SAP Business Application Studio IDE
+- `sap-build-apps.standard` - Low-code development platform
+- `integrationsuite.enterprise_agreement` - Integration Suite
+- `cicd-service.default` - CI/CD service
+
+### Productivity & Collaboration
+- `build-workzone.standard` - SAP Build Work Zone
+- `mobile-services.standard` - Mobile services
+
+### Business Applications
+- `business-rules.standard` - Business rules management
+- `workflow.standard` - Workflow automation
+
+## Important Notes
+
+### Entitlement Dependency
+Subscriptions **require corresponding entitlements**. For example:
+- `sapappstudio.standard-edition` subscription needs `sapappstudio.standard-edition` entitlement
+- Always add entitlements before subscriptions
+
+### Provisioning Time
+Some subscriptions take time to provision (5-15 minutes). The `state` output shows provisioning status.
+
+### Subscription vs Service Instance
+- **Subscription**: SaaS application (like SAP Business Application Studio)
+- **Service Instance**: Platform service (like PostgreSQL) - managed in Cloud Foundry building block
+
+## Variables
+
+| Name | Description | Required |
+|------|-------------|----------|
+| `globalaccount` | Global account subdomain | Yes |
+| `subaccount_id` | Target subaccount ID | Yes |
+| `subscriptions` | Comma-separated list in format `app.plan` | No |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| `subscriptions` | Map of created subscriptions with app names, plans, and states |
+| `subaccount_id` | Passthrough of subaccount ID for dependency chaining |
+
+## Dependency Chain
+
+This building block depends on:
+- **subaccount** - Must have a subaccount ID
+- **entitlements** - Required entitlements must exist
+
+## Common Scenarios
+
+### Full Development Environment
+```
+subscriptions = "sapappstudio.standard-edition,build-workzone.standard,cicd-service.default"
+```
+
+### Integration Platform
+```
+subscriptions = "integrationsuite.enterprise_agreement,mobile-services.standard"
+```
+
+### Low-Code Platform
+```
+subscriptions = "sap-build-apps.standard,build-workzone.standard"
+```
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.3.0 |
+| [btp](#requirement\_btp) | ~> 1.8.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [btp_subaccount_subscription.subscription](https://registry.terraform.io/providers/sap/btp/latest/docs/resources/subaccount_subscription) | resource |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [globalaccount](#input\_globalaccount) | The subdomain of the global account in which you want to manage resources. | `string` | n/a | yes |
+| [subaccount\_id](#input\_subaccount\_id) | The ID of the subaccount where subscriptions should be added. | `string` | n/a | yes |
+| [subscriptions](#input\_subscriptions) | Comma-separated list of application subscriptions in format: app.plan (e.g., 'build-workzone.standard,integrationsuite.enterprise\_agreement') | `string` | `""` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [subaccount\_id](#output\_subaccount\_id) | The subaccount ID (passthrough for dependency chaining) |
+| [subscriptions](#output\_subscriptions) | Map of application subscriptions created in this subaccount |
+
\ No newline at end of file
diff --git a/modules/sapbtp/subscriptions/buildingblock/definition/definition.json b/modules/sapbtp/subscriptions/buildingblock/definition/definition.json
new file mode 100644
index 0000000..45c1efc
--- /dev/null
+++ b/modules/sapbtp/subscriptions/buildingblock/definition/definition.json
@@ -0,0 +1,60 @@
+{
+ "name": "SAP BTP Subscriptions",
+ "displayName": "SAP BTP Subscriptions",
+ "description": "Manages application subscriptions in an SAP BTP subaccount, enabling access to SaaS applications",
+ "category": "Platform Services",
+ "platform": "sapbtp",
+ "tags": ["subscriptions", "btp", "saas", "applications"],
+ "version": "1.0.0",
+ "supportedPlatforms": ["sapbtp"],
+ "schema": {
+ "inputs": {
+ "globalaccount": {
+ "type": "string",
+ "description": "Global account subdomain",
+ "required": true
+ },
+ "subaccount_id": {
+ "type": "string",
+ "description": "BTP Subaccount ID (from dependency or manual input)",
+ "required": true
+ },
+ "subscriptions": {
+ "type": "string",
+ "description": "Select application subscriptions (meshStack will join as comma-separated: app.plan)",
+ "required": false,
+ "default": "",
+ "selectableValues": [
+ "build-workzone.standard",
+ "build-workzone.free",
+ "integrationsuite.enterprise_agreement",
+ "integrationsuite.trial",
+ "sap-build-apps.standard",
+ "sap-build-apps.free",
+ "sapappstudio.standard-edition",
+ "cicd-service.default",
+ "cicd-service.trial",
+ "business-rules.standard",
+ "mobile-services.standard",
+ "web-analytics.standard",
+ "workflow.standard",
+ "auditlog-viewer.free"
+ ]
+ }
+ },
+ "outputs": {
+ "subaccount_id": {
+ "type": "string",
+ "description": "BTP Subaccount ID (passthrough)"
+ },
+ "subscriptions": {
+ "type": "object",
+ "description": "Map of created subscriptions"
+ }
+ }
+ },
+ "documentation": {
+ "readme": "README.md",
+ "userGuide": "APP_TEAM_README.md"
+ }
+}
diff --git a/modules/sapbtp/subscriptions/buildingblock/import-resources.sh b/modules/sapbtp/subscriptions/buildingblock/import-resources.sh
new file mode 100755
index 0000000..18e8ade
--- /dev/null
+++ b/modules/sapbtp/subscriptions/buildingblock/import-resources.sh
@@ -0,0 +1,105 @@
+#!/usr/bin/env bash
+
+set -e
+
+echo "=== SAP BTP Subscriptions Import Script ==="
+echo ""
+echo "This script will import existing application subscriptions for a subaccount."
+echo ""
+
+if [ ! -f "terraform.tfvars" ]; then
+ echo "Error: terraform.tfvars not found in current directory"
+ exit 1
+fi
+
+echo "Reading configuration from terraform.tfvars..."
+
+SUBACCOUNT_ID=$(tofu console <<< 'var.subaccount_id' 2>/dev/null | tr -d '"')
+SUBSCRIPTIONS=$(tofu console <<< 'var.subscriptions' 2>/dev/null | tr -d '"')
+
+echo " Subaccount ID: $SUBACCOUNT_ID"
+echo " Subscriptions: $SUBSCRIPTIONS"
+echo ""
+
+if [ -z "$SUBACCOUNT_ID" ]; then
+ echo "Error: subaccount_id is required"
+ exit 1
+fi
+
+FAILED_IMPORTS=()
+SUCCESSFUL_IMPORTS=()
+
+import_resource() {
+ local resource_address="$1"
+ local resource_id="$2"
+ local description="$3"
+
+ echo "Importing: $description"
+ echo " Resource: $resource_address"
+ echo " ID: $resource_id"
+
+ if tofu state show "$resource_address" >/dev/null 2>&1; then
+ echo " ⊙ ALREADY IMPORTED (skipping)"
+ SUCCESSFUL_IMPORTS+=("$description (already imported)")
+ echo ""
+ return 0
+ fi
+
+ if tofu import "$resource_address" "$resource_id" >/dev/null 2>&1; then
+ SUCCESSFUL_IMPORTS+=("$description")
+ echo " ✓ SUCCESS"
+ else
+ FAILED_IMPORTS+=("$description")
+ echo " ✗ FAILED"
+ fi
+ echo ""
+}
+
+echo "Starting imports..."
+echo ""
+
+if [ -n "$SUBSCRIPTIONS" ] && [ "$SUBSCRIPTIONS" != '""' ] && [ "$SUBSCRIPTIONS" != "" ]; then
+ echo "Importing subscriptions..."
+
+ IFS=',' read -ra SUBSCRIPTION_ARRAY <<< "$SUBSCRIPTIONS"
+
+ for subscription_entry in "${SUBSCRIPTION_ARRAY[@]}"; do
+ subscription_entry=$(echo "$subscription_entry" | xargs)
+ if [ -n "$subscription_entry" ]; then
+ app_name=$(echo "$subscription_entry" | cut -d'.' -f1)
+ plan_name=$(echo "$subscription_entry" | cut -d'.' -f2)
+ resource_key="${app_name}-${plan_name}"
+
+ import_resource \
+ "btp_subaccount_subscription.subscription[\"$resource_key\"]" \
+ "$SUBACCOUNT_ID,$app_name,$plan_name" \
+ "Subscription: $app_name.$plan_name"
+ fi
+ done
+fi
+
+echo ""
+echo "=== Import Summary ==="
+echo ""
+echo "Successful imports (${#SUCCESSFUL_IMPORTS[@]}):"
+for item in "${SUCCESSFUL_IMPORTS[@]}"; do
+ echo " ✓ $item"
+done
+echo ""
+
+if [ ${#FAILED_IMPORTS[@]} -gt 0 ]; then
+ echo "Failed imports (${#FAILED_IMPORTS[@]}):"
+ for item in "${FAILED_IMPORTS[@]}"; do
+ echo " ✗ $item"
+ done
+ echo ""
+fi
+
+echo "Next steps:"
+echo " 1. Run 'tofu plan' to verify the state"
+echo " 2. Run 'tofu apply' if any changes are needed"
+echo ""
+
+if [ ${#FAILED_IMPORTS[@]} -gt 0 ]; then
+ exit 1
+fi
diff --git a/modules/sapbtp/subscriptions/buildingblock/locals.tf b/modules/sapbtp/subscriptions/buildingblock/locals.tf
new file mode 100644
index 0000000..2b8a12d
--- /dev/null
+++ b/modules/sapbtp/subscriptions/buildingblock/locals.tf
@@ -0,0 +1,20 @@
+locals {
+ raw_subscriptions = var.subscriptions != "" ? (
+ can(jsondecode(var.subscriptions)) ? jsondecode(var.subscriptions) : split(",", var.subscriptions)
+ ) : []
+
+ parsed_subscriptions = [
+ for s in local.raw_subscriptions :
+ {
+ app_name = split(".", trimspace(s))[0]
+ plan_name = split(".", trimspace(s))[1]
+ parameters = {}
+ }
+ if trimspace(s) != ""
+ ]
+
+ subscriptions_map = {
+ for idx, subscription in local.parsed_subscriptions :
+ "${subscription.app_name}-${subscription.plan_name}" => subscription
+ }
+}
diff --git a/modules/sapbtp/subscriptions/buildingblock/main.tf b/modules/sapbtp/subscriptions/buildingblock/main.tf
new file mode 100644
index 0000000..7af85b1
--- /dev/null
+++ b/modules/sapbtp/subscriptions/buildingblock/main.tf
@@ -0,0 +1,8 @@
+resource "btp_subaccount_subscription" "subscription" {
+ for_each = local.subscriptions_map
+
+ subaccount_id = var.subaccount_id
+ app_name = each.value.app_name
+ plan_name = each.value.plan_name
+ parameters = jsonencode(each.value.parameters)
+}
diff --git a/modules/sapbtp/subscriptions/buildingblock/outputs.tf b/modules/sapbtp/subscriptions/buildingblock/outputs.tf
new file mode 100644
index 0000000..663dbf5
--- /dev/null
+++ b/modules/sapbtp/subscriptions/buildingblock/outputs.tf
@@ -0,0 +1,16 @@
+output "subscriptions" {
+ description = "Map of application subscriptions created in this subaccount"
+ value = {
+ for k, v in btp_subaccount_subscription.subscription :
+ k => {
+ app_name = v.app_name
+ plan_name = v.plan_name
+ state = v.state
+ }
+ }
+}
+
+output "subaccount_id" {
+ description = "The subaccount ID (passthrough for dependency chaining)"
+ value = var.subaccount_id
+}
diff --git a/modules/sapbtp/subscriptions/buildingblock/provider.tf b/modules/sapbtp/subscriptions/buildingblock/provider.tf
new file mode 100644
index 0000000..c8ed1d3
--- /dev/null
+++ b/modules/sapbtp/subscriptions/buildingblock/provider.tf
@@ -0,0 +1,3 @@
+provider "btp" {
+ globalaccount = var.globalaccount
+}
diff --git a/modules/sapbtp/subscriptions/buildingblock/variables.tf b/modules/sapbtp/subscriptions/buildingblock/variables.tf
new file mode 100644
index 0000000..c176d55
--- /dev/null
+++ b/modules/sapbtp/subscriptions/buildingblock/variables.tf
@@ -0,0 +1,15 @@
+variable "globalaccount" {
+ type = string
+ description = "The subdomain of the global account in which you want to manage resources."
+}
+
+variable "subaccount_id" {
+ type = string
+ description = "The ID of the subaccount where subscriptions should be added."
+}
+
+variable "subscriptions" {
+ type = string
+ default = ""
+ description = "Comma-separated list of application subscriptions in format: app.plan (e.g., 'build-workzone.standard,integrationsuite.enterprise_agreement')"
+}
diff --git a/modules/sapbtp/subscriptions/buildingblock/versions.tf b/modules/sapbtp/subscriptions/buildingblock/versions.tf
new file mode 100644
index 0000000..e1d38eb
--- /dev/null
+++ b/modules/sapbtp/subscriptions/buildingblock/versions.tf
@@ -0,0 +1,9 @@
+terraform {
+ required_version = ">= 1.3.0"
+ required_providers {
+ btp = {
+ source = "sap/btp"
+ version = "~> 1.8.0"
+ }
+ }
+}
diff --git a/modules/sapbtp/trust-configuration/buildingblock/import-resources.sh b/modules/sapbtp/trust-configuration/buildingblock/import-resources.sh
new file mode 100755
index 0000000..cbb2c68
--- /dev/null
+++ b/modules/sapbtp/trust-configuration/buildingblock/import-resources.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env bash
+
+set -e
+
+echo "=== SAP BTP Trust Configuration Import Script ==="
+echo ""
+echo "This script will import an existing custom identity provider trust configuration."
+echo ""
+
+if [ ! -f "terraform.tfvars" ]; then
+ echo "Error: terraform.tfvars not found in current directory"
+ exit 1
+fi
+
+echo "Reading configuration from terraform.tfvars..."
+
+SUBACCOUNT_ID=$(tofu console <<< 'var.subaccount_id' 2>/dev/null | tr -d '"')
+IDENTITY_PROVIDER=$(tofu console <<< 'var.identity_provider' 2>/dev/null | tr -d '"')
+
+echo " Subaccount ID: $SUBACCOUNT_ID"
+echo " Identity Provider: $IDENTITY_PROVIDER"
+echo ""
+
+if [ -z "$SUBACCOUNT_ID" ]; then
+ echo "Error: subaccount_id is required"
+ exit 1
+fi
+
+if [ -z "$IDENTITY_PROVIDER" ] || [ "$IDENTITY_PROVIDER" = '""' ]; then
+ echo "No custom identity provider configured. Nothing to import."
+ exit 0
+fi
+
+FAILED_IMPORTS=()
+SUCCESSFUL_IMPORTS=()
+
+import_resource() {
+ local resource_address="$1"
+ local resource_id="$2"
+ local description="$3"
+
+ echo "Importing: $description"
+ echo " Resource: $resource_address"
+ echo " ID: $resource_id"
+
+ if tofu state show "$resource_address" >/dev/null 2>&1; then
+ echo " ⊙ ALREADY IMPORTED (skipping)"
+ SUCCESSFUL_IMPORTS+=("$description (already imported)")
+ echo ""
+ return 0
+ fi
+
+ if tofu import "$resource_address" "$resource_id" >/dev/null 2>&1; then
+ SUCCESSFUL_IMPORTS+=("$description")
+ echo " ✓ SUCCESS"
+ else
+ FAILED_IMPORTS+=("$description")
+ echo " ✗ FAILED"
+ fi
+ echo ""
+}
+
+echo "Starting imports..."
+echo ""
+
+import_resource \
+ "btp_subaccount_trust_configuration.custom_idp[0]" \
+ "$SUBACCOUNT_ID,$IDENTITY_PROVIDER" \
+ "Trust Configuration: $IDENTITY_PROVIDER"
+
+echo ""
+echo "=== Import Summary ==="
+echo ""
+echo "Successful imports (${#SUCCESSFUL_IMPORTS[@]}):"
+for item in "${SUCCESSFUL_IMPORTS[@]}"; do
+ echo " ✓ $item"
+done
+echo ""
+
+if [ ${#FAILED_IMPORTS[@]} -gt 0 ]; then
+ echo "Failed imports (${#FAILED_IMPORTS[@]}):"
+ for item in "${FAILED_IMPORTS[@]}"; do
+ echo " ✗ $item"
+ done
+ echo ""
+fi
+
+echo "Next steps:"
+echo " 1. Run 'tofu plan' to verify the state"
+echo " 2. Run 'tofu apply' if any changes are needed"
+echo ""
+
+if [ ${#FAILED_IMPORTS[@]} -gt 0 ]; then
+ exit 1
+fi
diff --git a/modules/sapbtp/trust-configuration/buildingblock/locals.tf b/modules/sapbtp/trust-configuration/buildingblock/locals.tf
new file mode 100644
index 0000000..559c0be
--- /dev/null
+++ b/modules/sapbtp/trust-configuration/buildingblock/locals.tf
@@ -0,0 +1,5 @@
+locals {
+ trust_configuration = var.identity_provider != "" ? {
+ identity_provider = var.identity_provider
+ } : null
+}
diff --git a/modules/sapbtp/trust-configuration/buildingblock/main.tf b/modules/sapbtp/trust-configuration/buildingblock/main.tf
new file mode 100644
index 0000000..8fd0917
--- /dev/null
+++ b/modules/sapbtp/trust-configuration/buildingblock/main.tf
@@ -0,0 +1,6 @@
+resource "btp_subaccount_trust_configuration" "custom_idp" {
+ count = local.trust_configuration != null ? 1 : 0
+
+ subaccount_id = var.subaccount_id
+ identity_provider = local.trust_configuration.identity_provider
+}
diff --git a/modules/sapbtp/trust-configuration/buildingblock/outputs.tf b/modules/sapbtp/trust-configuration/buildingblock/outputs.tf
new file mode 100644
index 0000000..43c5769
--- /dev/null
+++ b/modules/sapbtp/trust-configuration/buildingblock/outputs.tf
@@ -0,0 +1,14 @@
+output "trust_configuration_origin" {
+ description = "Origin key of the configured trust configuration (if configured)"
+ value = local.trust_configuration != null ? btp_subaccount_trust_configuration.custom_idp[0].origin : null
+}
+
+output "trust_configuration_status" {
+ description = "Status of the trust configuration (if configured)"
+ value = local.trust_configuration != null ? btp_subaccount_trust_configuration.custom_idp[0].status : null
+}
+
+output "subaccount_id" {
+ description = "The subaccount ID (passthrough for dependency chaining)"
+ value = var.subaccount_id
+}
diff --git a/modules/sapbtp/trust-configuration/buildingblock/provider.tf b/modules/sapbtp/trust-configuration/buildingblock/provider.tf
new file mode 100644
index 0000000..c8ed1d3
--- /dev/null
+++ b/modules/sapbtp/trust-configuration/buildingblock/provider.tf
@@ -0,0 +1,3 @@
+provider "btp" {
+ globalaccount = var.globalaccount
+}
diff --git a/modules/sapbtp/trust-configuration/buildingblock/variables.tf b/modules/sapbtp/trust-configuration/buildingblock/variables.tf
new file mode 100644
index 0000000..d8ebb77
--- /dev/null
+++ b/modules/sapbtp/trust-configuration/buildingblock/variables.tf
@@ -0,0 +1,15 @@
+variable "globalaccount" {
+ type = string
+ description = "The subdomain of the global account in which you want to manage resources."
+}
+
+variable "subaccount_id" {
+ type = string
+ description = "The ID of the subaccount where trust configuration should be added."
+}
+
+variable "identity_provider" {
+ type = string
+ default = ""
+ description = "Custom identity provider origin (e.g., mytenant.accounts.ondemand.com). Leave empty to skip trust configuration."
+}
diff --git a/modules/sapbtp/trust-configuration/buildingblock/versions.tf b/modules/sapbtp/trust-configuration/buildingblock/versions.tf
new file mode 100644
index 0000000..e1d38eb
--- /dev/null
+++ b/modules/sapbtp/trust-configuration/buildingblock/versions.tf
@@ -0,0 +1,9 @@
+terraform {
+ required_version = ">= 1.3.0"
+ required_providers {
+ btp = {
+ source = "sap/btp"
+ version = "~> 1.8.0"
+ }
+ }
+}