Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
mauriciolauffer
Product and Topic Expert
Product and Topic Expert
At the beginning of the year, I wrote a couple of blogs on using Terraform to automate and manage SAP BTP. The first one (Infrastructure as Code for Cloud Foundry and Kyma Environments) explained what is Terraform and Infrastructure-as-Code + a demo on how to manage Cloud Foundry ORGs, spaces and services with a Terraform provider for Cloud Foundry. The second (A workaround for Global Account, Directories and Subaccounts with btp CLI), was exploring a workaround to manage SAP BTP accounts on a higher level because there was no Terraform provider for SAP BTP. There was... SAP has just released its official Terraform Provider for SAP BTP! Hooray!!!

As announced, these are the operations supported by the new SAP BTP Terraform Provider:

  • Create, read, and delete subaccounts

  • Create, read, and delete directories

  • Assign, read, and delete labels (directories and subaccounts)

  • Create, read, and delete roles

  • Create, read, and delete role collections

  • Assign, read, and delete assignment of users to role collections

  • Create, read, and delete service entitlements

  • Assign custom identity providers (global account and subaccount)


You already know the concepts and probably played a bit with Terraform. So, here we go exploring what we can do now. I’ll use my SAP BTP trial account for that. Most people that spoke to me about Terraform were using the trial account, I guess nobody wants to destroy production by mistake...

Because we have a proper Terraform provider, we start setting it up. It’s straightforward, no secrets. Define the required provider and set the details to connect to your SAP BTP Global Account. You’ll need to get the Global Account subdomain, you find it in SAP BTP cockpit:


SAP BTP cockpit



SAP BTP Global Account subdomain


Create your Terraform script file, eg main.tf, and run $ terraform init
terraform {
required_providers {
btp = {
source = "sap/btp"
version = "0.1.0-beta1"
}
}
}

provider "btp" {
globalaccount = "globa_account_subdomain"
username = "btp_username"
password = "btp_password"
}

 

Then, we add a resource btp_subaccount to manage SAP BTP Subaccounts. We’ll create a Subaccount called dev.
resource "btp_subaccount" "dev" {
name = "subaccount_name"
subdomain = "subaccount_domain_must_be_unique"
region = "us10"
description = "Hey, this subaccount is managed by Terraform!"
}

 

Whoever creates the subaccount will have the role collection “Subaccount Administrator” assigned to them. But, let’s say you want to assign any role collection to any user. We use the resource btp_subaccount_role_collection_assignment.
resource "btp_subaccount_role_collection_assignment" "subaccount-viewer" {
subaccount_id = btp_subaccount.dev.id
role_collection_name = "Subaccount Viewer"
user_name = "btp_username"
}

 

Some services are included in the subaccount entitlement set by default. Everything else, you must configure it. Let’s add some services to the subaccount entitlement using the resource btp_subaccount_entitlement.
resource "btp_subaccount_entitlement" "bas" {
subaccount_id = btp_subaccount.dev.id
service_name = "sapappstudiotrial"
plan_name = "trial"
}


resource "btp_subaccount_entitlement" "alert" {
subaccount_id = btp_subaccount.dev.id
service_name = "alert-notification"
plan_name = "standard"
}

 

For the last step, we want to activate Cloud Foundry environment in the subaccount. The resource btp_subaccount_environment_instance is responsible for managing all environments: Cloud Foundry, Kyma and ABAP. However, just activating Cloud Foundry won’t be enough, as it won’t have entitlement for memory quota to run your apps. Therefore, we need to configure the entitlement as well.
resource "btp_subaccount_entitlement" "cloudfoundry" {
subaccount_id = btp_subaccount.dev.id
service_name = "APPLICATION_RUNTIME"
plan_name = "MEMORY"
amount = 1
}


resource "btp_subaccount_environment_instance" "cloudfoundry" {
subaccount_id = btp_subaccount.dev.id
name = "my-cf-environment"
environment_type = "cloudfoundry"
service_name = "cloudfoundry"
plan_name = "standard"


parameters = jsonencode({
instance_name = "my-cf-org-name"
})
}

 

All set! Let’s run it and see what is the plan, this won’t do anything on SAP BTP yet, it’ll just show will change if you apply it: $ terraform plan


$ terraform plan


 

Alright, let’s apply the proposed plan and check the results on SAP BTP. Run $ terraform apply.


$ terraform apply --auto-approve


 


Subaccount has been created by Terraform SAP BTP provider


 

Well done! Subaccount and Could Foundry environment have been created, some services added to the subaccount entitlement, and a role collection added to the user. Everything using the brand new Terraform provider for SAP BTP.

What if you also want to add a space to the newly created CF ORG? Easy! Just use the Cloud Foundry provider. You can mix and match multiple Terraform providers in the same script.

Let's add the Terraform provider for Cloud Foundry, the CF provider configuration and the resource cloudfoundry_space to our script. The resource cloudfoundry_space requires the CF ORG id which will come from resource btp_subaccount_environment_instance.platform_id.
terraform {
required_providers {
btp = {
source = "sap/btp"
version = "0.1.0-beta1"
}
cloudfoundry = {
source = "cloudfoundry-community/cloudfoundry"
version = "0.50.8"
}
}
}

### all the stuff we wrote before ###

provider "cloudfoundry" {
api_url = "https://api.cf.us10-001.hana.ondemand.com"
user = "btp_username"
password = "btp_password"
}

resource "cloudfoundry_space" "dev" {
name = "dev"
org = btp_subaccount_environment_instance.cloudfoundry.platform_id
}

Run $ terraform apply again.


$ terraform apply --auto-approve


 

Voilà! You have used two Terraform providers to manage SAP BTP and Cloud Foundry stuff in one go. That's one of the most powerful things in Terraform. You could add the Kyma/Kubernetes provider or anything else to meet your needs. You now have the tools to manage everything in SAP BTP with Terraform. Ok, not really everything yet, but the SAP BTP provider is still in beta, it'll get there. There's still room for improvement, but it's already great! It doesn't do everything, but it's better than nothing.

To finish up, let's destroy everything, let's delete the subaccount and its contents. Run $ terraform destroy --auto-approve. Don't worry, it won't delete the Global Account or anything else that isn't managed by the Terraform script. Other directories and subaccounts are safe.

To make it easier, here's the full Terraform script (with variables for user credentials):
terraform {
required_providers {
btp = {
source = "sap/btp"
version = "0.1.0-beta1"
}
cloudfoundry = {
source = "cloudfoundry-community/cloudfoundry"
version = "0.50.8"
}
}
}

variable "admin_user" {}
variable "admin_password" {}
variable "globalaccount_subdomain" {}

# SAP BTP provider configuration
provider "btp" {
globalaccount = var.globalaccount_subdomain
username = var.admin_user
password = var.admin_password
}

# Manage subaccount (create subaccount at Global Account level)
resource "btp_subaccount" "dev" {
name = "my-terraform-dev"
subdomain = "dev-terraform-ejzl2vx1" # it must be unique in SAP BTP
region = "us10"
description = "This was created by Terraform"
}

# Assign role collection Subaccount Administrator to users
resource "btp_subaccount_role_collection_assignment" "subaccount-viewer" {
subaccount_id = btp_subaccount.dev.id
role_collection_name = "Subaccount Viewer"
user_name = var.admin_user
}

# Configure subaccount entitlement, add service SAP Business Application Studio
resource "btp_subaccount_entitlement" "bas" {
subaccount_id = btp_subaccount.dev.id
service_name = "sapappstudiotrial"
plan_name = "trial"
}

# Configure subaccount entitlement, add service Alert Notification
resource "btp_subaccount_entitlement" "alert" {
subaccount_id = btp_subaccount.dev.id
service_name = "alert-notification"
plan_name = "standard"
}

# Configure subaccount entitlement, add quota to Cloud Foundry Runtime
resource "btp_subaccount_entitlement" "cloudfoundry" {
subaccount_id = btp_subaccount.dev.id
service_name = "APPLICATION_RUNTIME"
plan_name = "MEMORY"
amount = 1 # It allocates 1GB RAM to the subaccount
}

# Manage Cloud Foundry environment (create CF ORG at Subaccount level)
resource "btp_subaccount_environment_instance" "cloudfoundry" {
subaccount_id = btp_subaccount.dev.id
name = "my-cf-environment"
environment_type = "cloudfoundry"
service_name = "cloudfoundry"
plan_name = "standard"

parameters = jsonencode({
instance_name = "my-cf-org-x1" # it must be unique in the region
})
}

# Cloud Foundry provider configuration
provider "cloudfoundry" {
api_url = "https://api.cf.us10-001.hana.ondemand.com"
user = var.admin_user
password = var.admin_password
}

# CF Spaces
resource "cloudfoundry_space" "dev" {
name = "dev"
org = btp_subaccount_environment_instance.cloudfoundry.platform_id
}

 
8 Comments
Ashu1
Participant
0 Kudos
Hi Mauricio,

 

Nice blog ! and could very much see the potential of using terraform on btp. I am intereseted in knowing about assigning/deleting roles/role collections at a scale, is it possible at this stage ? would like to know your thoughts on it.
florian_lusch
Explorer
0 Kudos

Dear mauriciolauffer 

Do you know how to get the right API endpoint in a "dynamic way", after activating Cloud Foundry Environment with Terraform?
In some regions (us10, eu10) there are more than one possible endpoints. I can only get it to work, by hardcoding the API endpoint.

Br,
Florian

0 Kudos

HI mauriciolauffer

I am trying to create a terraform for user automation alone. I am getting the plan as below.

 

Terraform will perform the following actions:

# btp_subaccount_role_collection_assignment.bas_admn["test2@test.com"] will be created
+ resource "btp_subaccount_role_collection_assignment" "bas_admn" {
+ id = (known after apply)
+ origin = "ldap"
+ role_collection_name = "Business_Application_Studio_Administrator"
+ subaccount_id = "5ec57e8d-be52xxx"
+ user_name = "test2@test.com"
}

# btp_subaccount_role_collection_assignment.bas_admn["test3@test.com"] will be created
+ resource "btp_subaccount_role_collection_assignment" "bas_admn" {
+ id = (known after apply)
+ origin = "ldap"
+ role_collection_name = "Business_Application_Studio_Administrator"
+ subaccount_id = "5ec57e8d-bxxx"
+ user_name = "test3@test.com"
}

# btp_subaccount_role_collection_assignment.bas_dev["test1@test.com"] will be created
+ resource "btp_subaccount_role_collection_assignment" "bas_dev" {
+ id = (known after apply)
+ origin = "ldap"
+ role_collection_name = "Business_Application_Studio_Developer"
+ subaccount_id = "5ec57e8d-bexxxx"
+ user_name = "test1@test.com"
}

# btp_subaccount_role_collection_assignment.bas_dev["test2@test.com"] will be created
+ resource "btp_subaccount_role_collection_assignment" "bas_dev" {
+ id = (known after apply)
+ origin = "ldap"
+ role_collection_name = "Business_Application_Studio_Developer"
+ subaccount_id = "5ec57e8d-bexxxx"
+ user_name = "test2@test.com"
}

# time_sleep.wait_a_few_seconds will be created
+ resource "time_sleep" "wait_a_few_seconds" {
+ create_duration = "5s"
+ id = (known after apply)
}

Plan: 5 to add, 0 to change, 0 to destroy.

 

But when I execute the apply only first entries are getting added and remaining all are failing with 500 error. I tried even giving exact value instead of var files. Still the same.

 

Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value: yes

time_sleep.wait_a_few_seconds: Creating...
time_sleep.wait_a_few_seconds: Creation complete after 5s [id=2023-11-06T11:46:25Z]
btp_subaccount_role_collection_assignment.bas_admn["test2@test.com"]: Creating...
btp_subaccount_role_collection_assignment.bas_dev["test1@test.com"]: Creating...
btp_subaccount_role_collection_assignment.bas_admn["test3@test.com"]: Creating...
btp_subaccount_role_collection_assignment.bas_dev["test2@test.com"]: Creating...
btp_subaccount_role_collection_assignment.bas_admn["test2@test.com"]: Creation complete after 2s [id=5ec57e8d-be52-47c3-8274-7cc86ef51f4f,Business_Application_Studio_Administrator,test2@test.com]

│ Error: API Error Creating Resource Role Collection Assignment (Subaccount)

│ with btp_subaccount_role_collection_assignment.bas_dev["test1@test.com"],
│ on main.tf line 28, in resource "btp_subaccount_role_collection_assignment" "bas_dev":
│ 28: resource "btp_subaccount_role_collection_assignment" "bas_dev" {

│ Received response with unexpected status [Status: 500; Correlation ID: dab78805-82d1-2ed5-9d94-e1ba4a91e1e5]


│ Error: API Error Creating Resource Role Collection Assignment (Subaccount)

│ with btp_subaccount_role_collection_assignment.bas_dev["test2@test.com"],
│ on main.tf line 28, in resource "btp_subaccount_role_collection_assignment" "bas_dev":
│ 28: resource "btp_subaccount_role_collection_assignment" "bas_dev" {

│ Received response with unexpected status [Status: 500; Correlation ID: 74a24b54-2ae6-a671-5605-5d7e5208eff1]


│ Error: API Error Creating Resource Role Collection Assignment (Subaccount)

│ with btp_subaccount_role_collection_assignment.bas_admn["test3@test.com"],
│ on main.tf line 36, in resource "btp_subaccount_role_collection_assignment" "bas_admn":
│ 36: resource "btp_subaccount_role_collection_assignment" "bas_admn" {

│ Received response with unexpected status [Status: 500; Correlation ID: b5ca147a-f72c-8982-c134-b888d8847da3]

I am not sure whether this is bug or I am having any library missing. Please help.

Regards

Dinesh

mauriciolauffer
Product and Topic Expert
Product and Topic Expert
0 Kudos
Yes, it's possible. Resource btp_subaccount_role_collection_assignment will help on that.
mauriciolauffer
Product and Topic Expert
Product and Topic Expert
0 Kudos
There's one way. After creating btp_subaccount_environment_instance, the labels property will be a stringified JSON that contains the "API Endpoint". See example:
{
"mode": "managed",
"type": "btp_subaccount_environment_instance",
"name": "cloudfoundry",
"provider": "provider[\"registry.terraform.io/sap/btp\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"broker_id": "985B006B-820E-40BF-AC53-xxxxxx",
"created_date": "2023-07-03T04:53:21Z",
"custom_labels": {},
"dashboard_url": "",
"description": "",
"environment_type": "cloudfoundry",
"id": "B6B10F42-CBD7-409B-801D-xxxxxx",
"labels": "{\"API Endpoint\":\"https://api.cf.ap10.hana.ondemand.com\",\"Org Name\":\"mlauffer-cf-org\",\"Org ID\":\"7003562f-f010-48fa-a06f-xxxxxx\"}",
"landscape_label": "cf-us10-001",
"last_modified": "2023-07-03T04:53:35Z",
"name": "my-cf-environment",
"operation": "provision",
"parameters": "{\"instance_name\":\"mlauffer-cf-org\"}",
"plan_id": "fc5abe63-2a7d-4848-babf-f63a5d316df1",
"plan_name": "standard",
"platform_id": "7003562f-f010-48fa-a06f-f13e8732872e",
"service_id": "fa31b750-375f-4268-bee1-604811a89fd9",
"service_name": "cloudfoundry",
"state": "OK",
"subaccount_id": "651aae2a-660f-4689-93d2-xxxxxx",
"tenant_id": "651aae2a-660f-4689-93d2-xxxxxx",
"type": "Provision"
},
"sensitive_attributes": [],
"dependencies": [
"btp_subaccount.dev"
]
}
]
}

 

However, this won't help if you're trying to use it in the Cloud Foundry provider as the providers are started before anything is executed.
provider "cloudfoundry" {
api_url = "https://api.cf.ap10.hana.ondemand.com"
user = var.admin_user
password = var.admin_password
}
florian_lusch
Explorer
0 Kudos
Thanks for your response.

Exactly, api_url for CF provider is where I would have needed it.

 

Br,

Florian
abhijeet_mukkawar
Active Contributor
0 Kudos
​I have ensured the login id/pwd are correct and the subdomain too. But not able to get past this error.

Error: Unable to create Client



│   with provider["registry.terraform.io/sap/btp"],

│   on main.tf line 10, in provider "btp":

│   10: provider "btp" {



│ Login failed. Check your credentials. [Status: 401; Correlation ID: 265ee572-0ea1-9f46-eaa8-53859d3b6e23]

 

Any resolution?
Ravirajsinh1
Explorer
0 Kudos

Nice Blog!

I have couple of questions..

  1. For Provider we have an optional field cli_server_url. When do we provide value for this?
  2. We have custom IDP enabled(idp). and I am passing the value in provider.
    • But when I do pass I get 400 error
    • When I do not pass, I get 401 error.
    • I believe I might be providing wrong credentials. But I'm able to login with the same credentials to cockpit. Could you please provide more details on which credentials we need to pass?

My provider.tf looks like below:

terraform {
required_providers {
btp = {
source = "SAP/btp"
version = "1.0.0-rc1"
}
}
}

# Please checkout documentation on how best to authenticate
# against SAP BTP via the Terraform provider for SAP BTP
provider "btp" {
globalaccount = "my_subdomain"
cli_server_url = "https://cpcli.cf.eu10.hana.ondemand.com"
idp = "custom_idp"
username = "my_username"
password = "my_password"
}
Labels in this area