<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vipin Vijaykumar</title>
    <description>The latest articles on DEV Community by Vipin Vijaykumar (@vipinvkmenon).</description>
    <link>https://dev.to/vipinvkmenon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F268178%2F3a8d5833-cb9e-4958-b940-39ff5aad5528.jpeg</url>
      <title>DEV Community: Vipin Vijaykumar</title>
      <link>https://dev.to/vipinvkmenon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vipinvkmenon"/>
    <language>en</language>
    <item>
      <title>SAP BTP Multi-Region Setup with Terraform: A Practical Guide to Disaster Recovery Subaccount Pairing</title>
      <dc:creator>Vipin Vijaykumar</dc:creator>
      <pubDate>Sun, 19 Jul 2026 16:16:27 +0000</pubDate>
      <link>https://dev.to/vipinvkmenon/sap-btp-multi-region-setup-with-terraform-a-practical-guide-to-disaster-recovery-subaccount-hd8</link>
      <guid>https://dev.to/vipinvkmenon/sap-btp-multi-region-setup-with-terraform-a-practical-guide-to-disaster-recovery-subaccount-hd8</guid>
      <description>&lt;p&gt;&lt;em&gt;From real-world outages to automated resilience — how to protect your SAP BTP workloads across regions using Terraform&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  When the Cloud Goes Down
&lt;/h2&gt;

&lt;p&gt;In March 2026, physical attacks on data center infrastructure in the Middle East took two of three availability zones in an AWS region completely offline. The damage was not a software bug or a misconfiguration — it was fire and power shutdown from direct physical impact. Engineers could not simply reboot their way out of it. Physical repairs took multiple days.&lt;/p&gt;

&lt;p&gt;The cascading effect went further than the directly hit facilities. Ninety-one AWS services were disrupted across two Middle East regions. The control plane degraded — IAM, CloudFormation, and EC2 APIs all throttled — meaning that organizations whose DR strategy relied on &lt;em&gt;deploying infrastructure during the incident&lt;/em&gt; found their recovery tooling was itself unreliable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.infoq.com/news/2026/03/aws-multiaz-conflict-outage/" rel="noopener noreferrer"&gt;Analyses published after the incident&lt;/a&gt; landed on a blunt conclusion: &lt;strong&gt;Multi-AZ is not disaster recovery.&lt;/strong&gt; One expert put it plainly: &lt;em&gt;"Multi-AZ protects you from hardware failures, not a missile hitting an entire availability zone cluster in the same city."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://medium.com/@ismailkovvuru/aws-uae-drone-strike-outage-disaster-recovery-lessons-every-cloud-architect-needs-now-6b4d9c57b694" rel="noopener noreferrer"&gt;financial analysis of the impact&lt;/a&gt; illustrated the stakes concretely: for an $80K/hour SaaS platform, total costs from such an outage — revenue loss, SLA penalties, customer churn — would reach $2.77 million. Organizations that had pre-provisioned infrastructure in a secondary region and implemented automated, data-plane-based failover recovered in 28 minutes. Those that did not faced days of downtime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.networkworld.com/article/4140145/aws-middle-east-outage-a-reminder-not-to-rely-on-cloud-as-disaster-recovery-plan.html" rel="noopener noreferrer"&gt;Network World's post-incident report&lt;/a&gt; captured the lesson: &lt;em&gt;"The enterprises that will come through events like this aren't the ones with the thickest DR binders, they're the ones who've actually failed over to another continent."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These are not theoretical scenarios. They happened.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Disaster Recovery?
&lt;/h2&gt;

&lt;p&gt;Disaster recovery (DR) is the practice of designing systems so that when a failure occurs — and failure always occurs eventually — your business can continue operating or recover fast enough that the impact is acceptable.&lt;/p&gt;

&lt;p&gt;Two metrics define every DR strategy:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recovery Time Objective (RTO)&lt;/strong&gt; — the maximum acceptable time your system can be down after a failure. This is a business decision, not a technical one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recovery Point Objective (RPO)&lt;/strong&gt; — the maximum acceptable data loss measured in time. Can you tolerate losing the last 15 minutes of transactions? The last 24 hours? Nothing at all?&lt;/p&gt;

&lt;p&gt;These are defined in the &lt;a href="https://csrc.nist.gov/glossary/term/recovery_time_objective" rel="noopener noreferrer"&gt;NIST SP 800-34 Contingency Planning Guide&lt;/a&gt; and the &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/disaster-recovery-dr-objectives.html" rel="noopener noreferrer"&gt;AWS Well-Architected Reliability Pillar&lt;/a&gt;. The lower your RTO and RPO targets, the more investment your DR strategy requires — but according to the &lt;a href="https://uptimeinstitute.com/resources/research-and-reports/annual-outage-analysis-2023" rel="noopener noreferrer"&gt;Uptime Institute's 2023 Annual Outage Analysis&lt;/a&gt;, more than two-thirds of enterprise outages already cost over $100,000. For large enterprises the figure climbs past $1 million per hour. The cost of not investing is often higher than the cost of investing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-AZ: Necessary but Not Sufficient
&lt;/h2&gt;

&lt;p&gt;Before discussing multi-region, it is worth being precise about what multi-AZ actually protects against — because it is commonly misunderstood.&lt;/p&gt;

&lt;p&gt;Cloud providers divide each geographic region into multiple &lt;strong&gt;Availability Zones (AZs)&lt;/strong&gt; — physically separate data centers within the same metropolitan area, connected by low-latency private fiber. Deploying across multiple AZs within the same region protects against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single data center power failure&lt;/li&gt;
&lt;li&gt;A single building fire or hardware fault&lt;/li&gt;
&lt;li&gt;A localized network partition within one facility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What multi-AZ does &lt;strong&gt;not&lt;/strong&gt; protect against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A failure that affects the entire regional network layer (routing misconfiguration, backbone outage)&lt;/li&gt;
&lt;li&gt;A physical event large enough to affect multiple facilities in the same city simultaneously&lt;/li&gt;
&lt;li&gt;A regional control plane failure that takes down APIs across all AZs&lt;/li&gt;
&lt;li&gt;Legal or regulatory action that makes an entire geographic jurisdiction inaccessible&lt;/li&gt;
&lt;li&gt;A submarine cable cut that severs connectivity to the entire region&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The March 2026 Middle East incident demonstrated all of these simultaneously. The AZs were in the same metropolitan area. When two of three were hit, the third experienced cascading degradation from the loss of redundancy. And when the control plane throttled, the customers whose DR plans said "redeploy in a secondary region" discovered they could not reliably execute that plan using the primary region's APIs.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.serverion.com/uncategorized/case-study-multi-region-dr-load-balancing/" rel="noopener noreferrer"&gt;real-world case study of a multi-region DR implementation&lt;/a&gt; shows what proper architecture achieves: after a regional outage caused nearly a full day of downtime with $40,000 in losses, the organization moved to an active/passive architecture across two geographically separate regions. The result was a 2–5 minute RTO and sub-30-second RPO — at 50% of the cost of a full active-active model.&lt;/p&gt;

&lt;p&gt;Multi-region is not a luxury for hyperscalers. It is the minimum viable resilience posture for business-critical workloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Physical Internet Is Also a Factor
&lt;/h2&gt;

&lt;p&gt;The Red Sea submarine cable incidents of early 2024 are a useful reminder that cloud regions are not abstract — they depend on physical cables laid on ocean floors. When four major cables connecting Europe, the Middle East, and Asia were damaged, approximately 25% of traffic on those routes was disrupted. Providers rerouted traffic around the Cape of Good Hope, adding latency and reducing throughput. Cloud services in European regions serving users in Asia experienced degraded performance for weeks while repairs were negotiated.&lt;/p&gt;

&lt;p&gt;Organizations with active workloads on both sides of the disruption — in both an EU region and an Asia-Pacific region — were insulated. Those with all workloads in a single region were not.&lt;/p&gt;

&lt;p&gt;The point is not that these events happen frequently. The point is that they happen unpredictably, they affect entire geographic areas, and multi-AZ provides no protection against them.&lt;/p&gt;




&lt;h2&gt;
  
  
  SAP BTP Multi-Region: The Platform-Level Response
&lt;/h2&gt;

&lt;p&gt;SAP Business Technology Platform is the cloud foundation for modern SAP applications — the runtime for extensions, integrations, and services that connect to core SAP systems. For enterprises running business-critical SAP workloads on BTP, regional resilience is not optional.&lt;/p&gt;

&lt;p&gt;SAP's answer is the &lt;strong&gt;BTP Multi-Region Setup&lt;/strong&gt;, described in the &lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/btp-multi-region-basic-concepts" rel="noopener noreferrer"&gt;SAP BTP Multi-Region Guide&lt;/a&gt;. It provides a native disaster recovery capability at the platform layer, built around the following core ideas:&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Concepts
&lt;/h3&gt;

&lt;p&gt;SAP defines &lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/btp-multi-region-pairs" rel="noopener noreferrer"&gt;specific region pairs&lt;/a&gt; — the secondary region for a given primary is prescribed by SAP, ensuring sufficient geographic distance while keeping data within the same jurisdiction. The pairing is not arbitrary; it reflects deliberate decisions about latency, data residency, and regulatory compliance.&lt;/p&gt;

&lt;p&gt;The BTP Multi-Region architecture involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Region Pairs&lt;/strong&gt; — SAP-defined pairs of regions that are sufficiently distant from each other while remaining within the same country or jurisdiction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subaccount Pairs&lt;/strong&gt; — two customer subaccounts, one in each region of a pair, linked into a DR relationship&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redundant Setup&lt;/strong&gt; — customers deploy equivalent resources in both subaccounts; SAP BTP handles asynchronous replication between them behind the scenes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instance-level Failover&lt;/strong&gt; — controlled failover for individual service instances&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subaccount-level Failover&lt;/strong&gt; — controlled failover of an entire subaccount (currently in development)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regional-level Failover&lt;/strong&gt; — in a catastrophic regional event, SAP can initiate failover after a formal disaster declaration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One important design note from the &lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/btp-multi-region-basic-concepts" rel="noopener noreferrer"&gt;SAP documentation&lt;/a&gt;: customers should segregate DR and non-DR workloads into separate subaccounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Currently Supported Region Pairs
&lt;/h3&gt;

&lt;p&gt;SAP maintains a defined and growing set of DR region pairs. The secondary region for a given primary is prescribed by SAP ensuring sufficient geographic distance while keeping data within the same jurisdiction. For the most current list of supported pairs, refer to the &lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/btp-multi-region-pairs" rel="noopener noreferrer"&gt;SAP BTP Multi-Region Pairs&lt;/a&gt; page.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Set Up Multi-Region on BTP
&lt;/h2&gt;

&lt;p&gt;According to the &lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/how-to-create-multi-region-setup-on-btp" rel="noopener noreferrer"&gt;SAP setup guide&lt;/a&gt;, the process involves:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Pair two subaccounts&lt;/strong&gt; — one in each region of a supported pair. The pairing is symmetric: SAP BTP does not maintain or enforce "primary" and "secondary" roles at the platform level. You define which is primary based on your use case, typically proximity to your users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Configure redundant resources&lt;/strong&gt; — customers are responsible for deploying equivalent resources in both subaccounts. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trust configuration (same Identity Provider in both subaccounts for seamless authentication after failover)&lt;/li&gt;
&lt;li&gt;Entitlements (equivalent service entitlements in both subaccounts)&lt;/li&gt;
&lt;li&gt;Role collections and assignments&lt;/li&gt;
&lt;li&gt;Destination and connectivity settings&lt;/li&gt;
&lt;li&gt;Environment variables and service bindings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Pair service instances&lt;/strong&gt; — for services with built-in multi-region support, create a paired instance using the &lt;code&gt;dr_setup&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cf create-service &amp;lt;service&amp;gt; &amp;lt;plan&amp;gt; &amp;lt;name&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'{"dr_setup": {"op": "pair", "values": ["&amp;lt;primary_instance_id&amp;gt;"]}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After pairing, the primary instance handles traffic while data is asynchronously replicated to the secondary.&lt;/p&gt;

&lt;p&gt;For the full list of services with built-in multi-region support (a growing list), refer to &lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/btp-services-with-built-in-multi-region-support" rel="noopener noreferrer"&gt;SAP BTP Services with Built-in Multi-Region Support&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Triggering a Failover
&lt;/h2&gt;

&lt;p&gt;When a failover is needed, SAP BTP supports &lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/how-to-trigger-failover-to-secondary-btp-region" rel="noopener noreferrer"&gt;two strategies&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Planned failover&lt;/strong&gt; — zero data loss. The primary service instance becomes read-only, the secondary waits for full replication to complete, then promotes itself. If replication cannot complete within a time window, the failover is aborted. This is for scheduled maintenance or proactive region migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forced failover&lt;/strong&gt; — used during regional outages where the primary is unreachable. May incur data loss. The customer is responsible for resolving any data inconsistency afterward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cf update-service &amp;lt;name&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'{"dr_setup": {"op": "promote", "values": ["forced"]}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why Automate This with Terraform?
&lt;/h2&gt;

&lt;p&gt;Subaccount pairing through the BTP cockpit works for one environment. In practice you have multiple subaccounts in  development, staging, and production — each needing its own pairing, each with the same bilateral role assignments, each needing to be documented, audited, and reproducible.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://registry.terraform.io/providers/SAP/btp/latest" rel="noopener noreferrer"&gt;SAP BTP Terraform provider&lt;/a&gt; exposes subaccount pairing as a first-class resource. With it, your DR topology is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auditable&lt;/strong&gt; — every pairing change is a git commit with an author and timestamp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeatable&lt;/strong&gt; — new environment, same topology, &lt;code&gt;terraform apply&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drift-detectable&lt;/strong&gt; — &lt;code&gt;terraform plan&lt;/code&gt; catches manual changes made through the cockpit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitOps-compatible&lt;/strong&gt; — DR changes go through pull request review like any other infra change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Critically: the March 2026 incident showed that relying on deploying infrastructure &lt;em&gt;during&lt;/em&gt; an outage is dangerous — the control plane may be degraded. With Terraform, everything is pre-provisioned and pre-declared. When the incident happens, the infrastructure is already there.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Terraform Resource: &lt;code&gt;btp_disaster_recovery_subaccount_pair&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Refer to the full schema from the &lt;a href="https://registry.terraform.io/providers/SAP/btp/latest/docs/resources/disaster_recovery_subaccount_pair" rel="noopener noreferrer"&gt;Terraform Registry&lt;/a&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  Required Arguments
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Argument&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;subaccount_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;The ID of the first subaccount to pair&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;paired_subaccount_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;The ID of the second subaccount to pair&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Read-Only Attributes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pair_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;Unique identifier of the subaccount pair&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;globalaccount_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;The Global Account owning both subaccounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;created_by&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;The user who created the pairing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;created_date&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;Timestamp of creation in RFC3339 format&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step-by-Step Terraform Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Provider Setup
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;provider.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;btp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SAP/btp"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"btp"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;globalaccount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;globalaccount_subdomain&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Variables
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"globalaccount_subdomain"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Your BTP Global Account subdomain"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"primary_subaccount_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of the subaccount in the primary region (e.g. cf-eu10)"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"secondary_subaccount_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of the subaccount in the paired secondary region (e.g. cf-eu13)"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The DR Pair Resource
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;main.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"btp_disaster_recovery_subaccount_pair"&lt;/span&gt; &lt;span class="s2"&gt;"dr_pair"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subaccount_id&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary_subaccount_id&lt;/span&gt;
  &lt;span class="nx"&gt;paired_subaccount_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secondary_subaccount_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two arguments. The provider handles the API calls to establish the DR relationship. That is the entire configuration needed to pair the subaccounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Outputs
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;outputs.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"dr_pair_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;btp_disaster_recovery_subaccount_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dr_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pair_id&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The ID of the disaster recovery subaccount pair"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"dr_pair_created_by"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;btp_disaster_recovery_subaccount_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dr_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_by&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"dr_pair_created_date"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;btp_disaster_recovery_subaccount_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dr_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_date&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Apply
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
terraform plan
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After apply, the pairing appears in the BTP cockpit under &lt;strong&gt;Multi-Region Setup&lt;/strong&gt; in both subaccounts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Importing an Existing Pair
&lt;/h2&gt;

&lt;p&gt;If the pairing was created manually through the cockpit, import it without destroying and recreating it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLI import:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform import btp_disaster_recovery_subaccount_pair.dr_pair &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'dd005d8b-1fee-4e6b-b6ff-cb9a197b7fe0,2dc1ecf1-786c-4f92-91f2-26650ab3ad28'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Import block (Terraform 1.5+):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;btp_disaster_recovery_subaccount_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dr_pair&lt;/span&gt;
  &lt;span class="nx"&gt;identity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;subaccount_id&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dd005d8b-1fee-4e6b-b6ff-cb9a197b7fe0"&lt;/span&gt;
    &lt;span class="nx"&gt;paired_subaccount_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2dc1ecf1-786c-4f92-91f2-26650ab3ad28"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Important Constraints
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One-to-one only.&lt;/strong&gt; Each subaccount can be paired with exactly one other subaccount.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bilateral role requirement.&lt;/strong&gt; The &lt;code&gt;Central Disaster Recovery Administrator&lt;/code&gt; role must be assigned in &lt;strong&gt;both&lt;/strong&gt; subaccounts. Assigning it only in one is the most common setup mistake.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destroy unpairs, does not delete.&lt;/strong&gt; &lt;code&gt;terraform destroy&lt;/code&gt; removes the pairing relationship — subaccounts and their resources are untouched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subaccount pairing is the foundation.&lt;/strong&gt; It establishes the trust relationship. Individual service instance pairs and subscription pairs must be created on top of it to achieve actual data replication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate DR from non-DR workloads.&lt;/strong&gt; SAP recommends keeping DR-enabled resources in dedicated subaccounts, separate from non-DR workloads.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The events of the past few years — data center disruptions, undersea cable damage, regional network failures — have made one thing clear: multi-AZ resilience is no longer enough for business-critical workloads. The failure modes that actually cause extended outages operate at a level above individual availability zones.&lt;/p&gt;

&lt;p&gt;Multi-region disaster recovery is the architectural response. For SAP BTP workloads, the platform provides this natively through subaccount pairing and service-level replication. Terraform makes the configuration auditable, repeatable, and safe to execute before the incident happens — not during it.&lt;/p&gt;

&lt;p&gt;The full setup is genuinely simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"btp_disaster_recovery_subaccount_pair"&lt;/span&gt; &lt;span class="s2"&gt;"dr_pair"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subaccount_id&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary_subaccount_id&lt;/span&gt;
  &lt;span class="nx"&gt;paired_subaccount_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secondary_subaccount_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Create two subaccounts in a supported SAP BTP region pair&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Assign &lt;code&gt;Central Disaster Recovery Administrator&lt;/code&gt; in both&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Configure the SAP BTP Terraform provider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Declare &lt;code&gt;btp_disaster_recovery_subaccount_pair&lt;/code&gt; and apply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Mirror entitlements, trust config, roles, and destinations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Create service instance pairs for each critical service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Test a planned failover — before you need it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/btp-multi-region-basic-concepts" rel="noopener noreferrer"&gt;SAP BTP Multi-Region — Basic Concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/btp-multi-region-pairs" rel="noopener noreferrer"&gt;SAP BTP Multi-Region Pairs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/how-to-create-multi-region-setup-on-btp" rel="noopener noreferrer"&gt;How to Create Multi-Region Setup on SAP BTP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/how-to-trigger-failover-to-secondary-btp-region" rel="noopener noreferrer"&gt;How to Trigger a Failover to the Secondary SAP BTP Region&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.sap.com/docs/btp/sap-btp-multi-region-guide/btp-services-with-built-in-multi-region-support" rel="noopener noreferrer"&gt;SAP BTP Services with Built-in Multi-Region Support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/SAP/btp/latest/docs/resources/disaster_recovery_subaccount_pair" rel="noopener noreferrer"&gt;btp_disaster_recovery_subaccount_pair — Terraform Registry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.infoq.com/news/2026/03/aws-multiaz-conflict-outage/" rel="noopener noreferrer"&gt;AWS Multi-AZ Conflict Outage — InfoQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@ismailkovvuru/aws-uae-drone-strike-outage-disaster-recovery-lessons-every-cloud-architect-needs-now-6b4d9c57b694" rel="noopener noreferrer"&gt;AWS UAE Outage: DR Lessons — Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.networkworld.com/article/4140145/aws-middle-east-outage-a-reminder-not-to-rely-on-cloud-as-disaster-recovery-plan.html" rel="noopener noreferrer"&gt;AWS Middle East Outage — Network World&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.serverion.com/uncategorized/case-study-multi-region-dr-load-balancing/" rel="noopener noreferrer"&gt;Multi-Region DR Case Study — Serverion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/disaster-recovery-dr-objectives.html" rel="noopener noreferrer"&gt;AWS Well-Architected: DR Objectives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://uptimeinstitute.com/resources/research-and-reports/annual-outage-analysis-2023" rel="noopener noreferrer"&gt;Uptime Institute 2023 Annual Outage Analysis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://csrc.nist.gov/glossary/term/recovery_time_objective" rel="noopener noreferrer"&gt;NIST RTO Definition — SP 800-34&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>btp</category>
      <category>terraform</category>
      <category>tutorial</category>
      <category>sap</category>
    </item>
    <item>
      <title>Bye-Bye Credentials! Automate BTP &amp; Cloud Foundry Setup with Terraform using Github Actions and Github OIDC</title>
      <dc:creator>Vipin Vijaykumar</dc:creator>
      <pubDate>Wed, 13 Aug 2025 15:38:36 +0000</pubDate>
      <link>https://dev.to/vipinvkmenon/bye-bye-credentials-automate-btp-cloud-foundry-setup-with-terraform-using-github-actions-and-3m07</link>
      <guid>https://dev.to/vipinvkmenon/bye-bye-credentials-automate-btp-cloud-foundry-setup-with-terraform-using-github-actions-and-3m07</guid>
      <description>&lt;p&gt;&lt;strong&gt;Imagine this&lt;/strong&gt;: You want to automate setting up SAP BTP Accounts and Cloud Foundry using Terraform, but without the pain of managing and tracking usernames, passwords, or rotating any of those credentials.&lt;/p&gt;

&lt;p&gt;Well, that's no longer a dream! Most common CI/CD Solutions like &lt;a href="https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-cloud-providers" rel="noopener noreferrer"&gt;Github&lt;/a&gt;/&lt;a href="https://docs.gitlab.com/integration/openid_connect_provider/" rel="noopener noreferrer"&gt;Gitlab&lt;/a&gt; actions, &lt;a href="https://plugins.jenkins.io/oidc-provider/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt;, &lt;a href="https://devblogs.microsoft.com/devops/workload-identity-federation-for-azure-deployments-is-now-generally-available/" rel="noopener noreferrer"&gt;Azure&lt;/a&gt; &lt;a href="https://devblogs.microsoft.com/devops/introduction-to-azure-devops-workload-identity-federation-oidc-with-terraform/" rel="noopener noreferrer"&gt;DevOps&lt;/a&gt; etc and OIDC Provider integration, you can wave goodbye to maintaining long-lived credentials. Say hello to &lt;strong&gt;short-lived, secure, just-in-time&lt;/strong&gt; tokens.&lt;/p&gt;

&lt;p&gt;In this blog, we’ll set up GitHub’s OIDC provider to authenticate directly against SAP BTP using &lt;strong&gt;JWT Bearer Assertion flow&lt;/strong&gt;, and then use Terraform to provision your &lt;a href="https://www.sap.com/india/products/technology-platform.html" rel="noopener noreferrer"&gt;BTP&lt;/a&gt; resources, &lt;a href="https://www.cloudfoundry.org/" rel="noopener noreferrer"&gt;Cloud Foundry&lt;/a&gt; spaces, and more. These steps work for both &lt;a href="https://developer.hashicorp.com/terraform" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; and &lt;a href="https://opentofu.org/" rel="noopener noreferrer"&gt;OpenTofu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's a JWT Bearer Assertion Anyway?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before we jump into Terraform and GitHub Actions, let’s talk &lt;a href="https://www.jwt.io/introduction#what-is-json-web-token" rel="noopener noreferrer"&gt;JWT Bearer tokens&lt;/a&gt;. A JWT Bearer Assertion is a way for a client to prove its identity to an authorisation server using a signed JSON Web Token (JWT) instead of a password or traditional credentials. Since the tokens are short-lived and ephemeral, they bring somewhat better security and rotation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short&lt;/strong&gt;: Instead of passing around long-term credentials, GitHub can request a short-lived token for your pipeline, signed and trusted via OpenID Connect. This token is accepted by an &lt;a href="https://help.sap.com/docs/cloud-identity-services/cloud-identity-services/landing-page" rel="noopener noreferrer"&gt;SAP Cloud Identity Service&lt;/a&gt; tenant, which then lets Terraform (on behalf of GitHub Actions) access BTP and Cloud Foundry APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event Sequence&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Github Workflow is triggered&lt;/li&gt;
&lt;li&gt;The Github OIDC Provider is called, and a JWT Token is generated for the workflow. This token is stored in environment variables or github variables&lt;/li&gt;
&lt;li&gt;The JWT Bearer token is used by the BTP &amp;amp; CF terraform provider in the Terraform scripts&lt;/li&gt;
&lt;li&gt;The calls for the respective providers are authenticated in BTP and CF (UAA) via the trusted SAP Cloud Identity Service tenant. The bearer token is forwarded to the SAP Cloud Identity Service&lt;/li&gt;
&lt;li&gt;SAP Cloud Identity services validate the token against the Github OIDC Public Keys(JWKS)&lt;/li&gt;
&lt;li&gt;If the key is successfully validated, the clients are authenticated, and subsequent calls can be made.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let’s dig in!
&lt;/h2&gt;

&lt;p&gt;This tutorial builds upon &lt;a href="https://community.sap.com/t5/technology-blog-posts-by-sap/authenticating-github-actions-workflows-deploying-to-the-sap-btp-cloud/ba-p/14075047" rel="noopener noreferrer"&gt;a community blog&lt;/a&gt; that showcases how the &lt;code&gt;cf&lt;/code&gt; CLI can leverage GitHub OIDC for logging into and managing Cloud Foundry. &lt;/p&gt;

&lt;p&gt;We'll reference the community blog for the initial setup steps as it's well-documented there. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Establish Trust between SAP BTP &amp;amp; SAP Cloud Identity Services
&lt;/h3&gt;

&lt;p&gt;Please refer to the &lt;a href="https://help.sap.com/docs/btp/sap-business-technology-platform/establish-trust-and-federation-of-custom-identity-providers-for-platform-users?locale=en-US#procedure" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; for doing the same&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create the Github Repo
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create the github repo with &lt;code&gt;repository-name&lt;/code&gt; in the &lt;code&gt;Organisation&lt;/code&gt; you need. &lt;strong&gt;This repository and name will be used in the next steps&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3:Configure SAP Cloud Identity Services
&lt;/h3&gt;

&lt;p&gt;In this step, configure the SAP Cloud Identity Services to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the Repo User for Github Action Workflows. 
Note: 

&lt;ul&gt;
&lt;li&gt;Name format: &lt;code&gt;repo/&amp;lt;Organisation&amp;gt;/&amp;lt;repository-name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Email format: &lt;code&gt;repo/&amp;lt;Organisation&amp;gt;/&amp;lt;repository-name&amp;gt;@&amp;lt;github-domain&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Add the Repo User to a group&lt;/li&gt;

&lt;li&gt;Configure Github OIDC as a Corporate IdP&lt;/li&gt;

&lt;li&gt;Map the repository name as &lt;code&gt;NameId&lt;/code&gt; (Enriched Token Claims)&lt;/li&gt;

&lt;li&gt;Configure Identity Federation.&lt;/li&gt;

&lt;li&gt;Set up the BTP Application to use the GitHub IDP&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Please refer to the &lt;a href="https://community.sap.com/t5/technology-blog-posts-by-sap/authenticating-github-actions-workflows-deploying-to-the-sap-btp-cloud/ba-p/14075047" rel="noopener noreferrer"&gt;community blog&lt;/a&gt; mentioned above for the detailed steps with regards to this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Add user to Global Account
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add the &lt;code&gt;repo-user&lt;/code&gt; email, created in SAP Cloud Identity Services in BTP. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjjdd7h3k3pd832fzyt8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjjdd7h3k3pd832fzyt8m.png" alt=" " width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since we will be working with creating a subaccount and managing resources at the Global Account level, assign the &lt;code&gt;Global Account Administrator&lt;/code&gt; Role Collection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fca0cm6om7gyb2ob8mehg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fca0cm6om7gyb2ob8mehg.png" alt=" " width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Add content to the Github Repository
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The following will be the structure of the project and files.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&amp;lt;repository-root-directory&amp;gt;/
├── .github
│   └── workflows
│       └── deploy.yml
├── main.tf
├── provider.tf
├── terraform.tfvars
└── variables.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Here we create a Terraform configuration that uses the Terraform providers for SAP BTP &amp;amp; Cloud Foundry to:-&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the configuration to initialise the &lt;code&gt;provider.tf&lt;/code&gt; file&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;main.tf&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;main.tf&lt;/code&gt; has the definition of the infrastructure that will be created. For this article, we take a simple scenario as:-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Subaccount&lt;/li&gt;
&lt;li&gt;Initialise a Cloud Foundry Runtime Environment in the subaccount&lt;/li&gt;
&lt;li&gt;Create a space within Cloud Foundry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This would, of course vary depending on your setup and scenario. So we won't delve into this section much.&lt;/p&gt;

&lt;p&gt;For details on the different resources &amp;amp; datasources present in the Terraform providers for &lt;a href="https://registry.terraform.io/providers/SAP/btp/latest" rel="noopener noreferrer"&gt;SAP BTP&lt;/a&gt; &amp;amp; &lt;a href="https://registry.terraform.io/providers/cloudfoundry/cloudfoundry/latest" rel="noopener noreferrer"&gt;Cloud Foundry&lt;/a&gt;, please refer to the registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"btp_subaccount"&lt;/span&gt; &lt;span class="s2"&gt;"project"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sample project"&lt;/span&gt;
  &lt;span class="nx"&gt;subdomain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sample.project"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"btp_subaccount_environment_instance"&lt;/span&gt; &lt;span class="s2"&gt;"cloudfoundry"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subaccount_id&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;btp_subaccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sample-cf-instance"&lt;/span&gt;
  &lt;span class="nx"&gt;landscape_label&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cf-ap10"&lt;/span&gt;
  &lt;span class="nx"&gt;environment_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cloudfoundry"&lt;/span&gt;
  &lt;span class="nx"&gt;service_name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cloudfoundry"&lt;/span&gt;
  &lt;span class="nx"&gt;plan_name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"standard"&lt;/span&gt;
  &lt;span class="nx"&gt;parameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;instance_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sample-cf-instance"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"cloudfoundry_space"&lt;/span&gt; &lt;span class="s2"&gt;"space"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev_space"&lt;/span&gt;
  &lt;span class="nx"&gt;org&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;btp_subaccount_environment_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudfoundry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platform_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;variables.tf&lt;/code&gt; file
&lt;/h4&gt;

&lt;p&gt;Here we declare the variables &lt;code&gt;globalaccount&lt;/code&gt; and &lt;code&gt;idp&lt;/code&gt;. You'd have more depending on your script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"globalaccount"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The subdomain of the SAP BTP global account."&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"idp"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Orgin key of Identity Provider"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Configure &lt;code&gt;provider.tf&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;By convention, the &lt;code&gt;provider.tf&lt;/code&gt; file usually represents the configuration to initialise the providers. Here we will configure the SAP BTP and Cloud Foundry providers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;btp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sap/btp"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.15.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;cloudfoundry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cloudfoundry/cloudfoundry"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.8.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"btp"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;globalaccount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;globalaccount&lt;/span&gt;
  &lt;span class="nx"&gt;idp&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idp&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"cloudfoundry"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;api_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://api.cf.${var.region}.hana.ondemand.com"&lt;/span&gt;
  &lt;span class="nx"&gt;origin&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idp&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As can be seen:-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;btp&lt;/code&gt; provider is configured with the &lt;code&gt;global&lt;/code&gt; account and the    &lt;code&gt;idp&lt;/code&gt; (Based on the trust configured in the above steps)&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cloudfoundry&lt;/code&gt; is configured with the &lt;code&gt;cf-api&lt;/code&gt; and the &lt;code&gt;origin&lt;/code&gt;(uaa), which is the same trust configuration as created above&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since the credentials used here is the assertion token, we'll be using the environment variables BTP_ASSERTION and CF_ASSERTION_TOKEN.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;:- &lt;em&gt;CF API depends on the region where the subaccount is created. Please refer to the right API in the&lt;/em&gt; &lt;a href="https://help.sap.com/docs/btp/sap-business-technology-%20platform/regions-and-api-endpoints-available-for-cloud-foundry-%20environment?locale=en-US" rel="noopener noreferrer"&gt;official document&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Add the Github Workflow
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The Github workflow initialises Terraform, generates the token and stores it as Github Outputs. These tokens are then assigned as environment variables to the Terraform Providers. Thereafter, the Terraform executes the scripts just as any typical workflow.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BTP&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Subaccount&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Region'&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;idp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Identity&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Provider'&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;global_account_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Global&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Account&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ID'&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;tenantIssuerUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Issuer&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;URL'&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt; &lt;span class="c1"&gt;# This is required for requesting the JWT&lt;/span&gt;
        &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Terraform&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hashicorp/setup-terraform@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;terraform_wrapper&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;terraform_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install OIDC Client from Core Package&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install @actions/core @actions/http-client&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get Id Token&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/github-script@v7&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;idtoken&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;TENANT_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{inputs.tenantIssuerUri}}&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;const tenanturl = process.env.TENANT_URL;&lt;/span&gt;
            &lt;span class="s"&gt;const coredemo = require('@actions/core')&lt;/span&gt;
            &lt;span class="s"&gt;let id_token = await coredemo.getIDToken(tenanturl)&lt;/span&gt;
            &lt;span class="s"&gt;coredemo.setOutput('githubJwt', id_token)&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apply Terraform configuration&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;export BTP_ASSERTION="${{ steps.idtoken.outputs.githubJwt }}"&lt;/span&gt;
          &lt;span class="s"&gt;export CF_ASSERTION_TOKEN="${{ steps.idtoken.outputs.githubJwt }}"&lt;/span&gt;
          &lt;span class="s"&gt;terraform apply -auto-approve -var "region=${{ inputs.region }}" -var "idp=${{ inputs.idp }}" -var "globalaccount=${{ inputs.global_account_id }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this article, the workflow is triggered &lt;a href="https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#workflow_dispatch" rel="noopener noreferrer"&gt;manually&lt;/a&gt; based on user inputs. It takes the following inputs:-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BTP Subaccount Region&lt;/strong&gt;: Region where the Subaccount will be created&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity Provider&lt;/strong&gt;: The name of the identity provider (Origin Key (Cloud Foundry)) that was configured in the Trust Configuration in BTP from the above step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global Account ID&lt;/strong&gt;: The Global Account Subdomain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issuer URL&lt;/strong&gt;: The URL of the SAP Cloud Identity Service Tenant. 
Github OIDC Provider issues tokens for this tenant as the &lt;a href="https://www.ory.sh/docs/hydra/guides/audiences" rel="noopener noreferrer"&gt;audience&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The token is generated in the step &lt;code&gt;Get Id Token&lt;/code&gt;. In this article, we use the &lt;a href="https://github.com/actions/toolkit/tree/main/packages/core" rel="noopener noreferrer"&gt;core package&lt;/a&gt; for the GH Actions Toolkit. An alternative approach of using &lt;a href="https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-cloud-providers#requesting-the-jwt-using-environment-variables" rel="noopener noreferrer"&gt;curl&lt;/a&gt;. This approach can be seen in the blog referred above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Trigger Workflow
&lt;/h3&gt;

&lt;p&gt;Trigger the workflow from the Github Actions tab. Add the necessary details as seen below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvo1ofmt0dndd4zb2t313.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvo1ofmt0dndd4zb2t313.png" alt=" " width="700" height="1002"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Job should run successfully, whereby you can see that the Token is generated and used by the subsequent steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;By federating GitHub Actions with SAP Cloud Identity and leveraging the JWT Bearer Assertion flow, you can automate provisioning of SAP BTP resources and Cloud Foundry environments without storing or rotating long-lived service credentials. &lt;/p&gt;

&lt;h3&gt;
  
  
  Some Best Practices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use any supported remote, encrypted Terraform/OpenTofu backends&lt;/strong&gt; like &lt;a href="https://developer.hashicorp.com/hcp" rel="noopener noreferrer"&gt;HCP&lt;/a&gt;, &lt;a href="https://www.hashicorp.com/en/products/vault" rel="noopener noreferrer"&gt;Vault&lt;/a&gt;, &lt;a href="https://openbao.org/" rel="noopener noreferrer"&gt;OpenBao&lt;/a&gt;, S3/GCS/Azure Blob, etc, with server-side encryption. &lt;/li&gt;
&lt;li&gt;Work with &lt;strong&gt;least privilege &amp;amp; role collections&lt;/strong&gt;: In BTP, assign the repo user (or group) only the role collections needed for the automation (For e.g Avoid Global Account Admin if the setup is for a specific subaccount).&lt;/li&gt;
&lt;li&gt;Ensure &lt;strong&gt;GitHub Repo is protected&lt;/strong&gt; and &lt;strong&gt;require reviewers&lt;/strong&gt; for workflows that can create or alter infrastructure.&lt;/li&gt;
&lt;li&gt;Test your workflow setup on dev/test environments first.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terraform</category>
      <category>github</category>
      <category>devops</category>
      <category>btp</category>
    </item>
    <item>
      <title>Setting up OTEL Collectors for mTLS</title>
      <dc:creator>Vipin Vijaykumar</dc:creator>
      <pubDate>Mon, 17 Feb 2025 03:27:47 +0000</pubDate>
      <link>https://dev.to/vipinvkmenon/setting-up-otel-collectors-for-mtls-4n4o</link>
      <guid>https://dev.to/vipinvkmenon/setting-up-otel-collectors-for-mtls-4n4o</guid>
      <description>&lt;h1&gt;
  
  
  What's mTLS
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;mTLS&lt;/strong&gt; (Mutual Transport Layer Security) is an enhanced version of &lt;strong&gt;TLS&lt;/strong&gt; (Transport Layer Security) where the client and the server authenticate each other during the connection establishment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A very Simple Flow for mTLS. (Note arrow direction)

+--------+ ---1--&amp;gt; +--------+
| Client | &amp;lt;--2--- | Server |
+--------+ &amp;lt;--3--&amp;gt; +--------+

1a) Client sends request to server along with its certificate.
1b) Server verifies the client certificate using client's public key.(Server has the public Key in its store/DB)
2a) Server sends response along with its certificate.
2b) Client verifies the server certificate with the server public key
3) After mutual authentication, encrypted communication between client and server.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typically, the server and client certificates are signed by a CA. As the server can manage multiple clients, the server usually maintains the public key of the CA that is used to sign all clients.&lt;/p&gt;

&lt;h1&gt;
  
  
  How is this different from regular TLS?
&lt;/h1&gt;

&lt;p&gt;Regular TLS is unidirectional. I.E Only the client verifies the certificates presented by the server. The server does not verify the client's certificates.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using mTLS in OpenTelemetry(OTEL)
&lt;/h1&gt;

&lt;p&gt;To use mTLS between 2 OTEL components, we need client and server certificates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;2 OTEL Collectors. One as client (exporter) and the other as server (receiver). Metrics are sent/forwarded from the client to the server. Since this is a demo setup (or internal) we will be using Self Signed certificates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------+                       +--------------+
| Otel Client |-------GRPC-----------&amp;gt;|  OTEL Server |
| (Exporter)  |                       |  (Receiver)  |
+-------------+                       +--------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll be running this inside a Kubernetes cluster in 2 different namespaces.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Prerequisites&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Kubernetes cluster&lt;/li&gt;
&lt;li&gt;kubectl &amp;amp; helm&lt;/li&gt;
&lt;li&gt;System with OpenSSL client installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1) Generate certificates
&lt;/h3&gt;

&lt;p&gt;a)&lt;u&gt;Generate rootCA certificate.&lt;/u&gt;&lt;br&gt;
Since we are using Self-signed certificates, we will create and use our own rootCA.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:4096 &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s1"&gt;'/O=A Team/CN=root.com'&lt;/span&gt; &lt;span class="nt"&gt;-keyout&lt;/span&gt; rootCA.key &lt;span class="nt"&gt;-out&lt;/span&gt; rootCA.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b)&lt;u&gt;Generate server certificate&lt;/u&gt;&lt;br&gt;
We need to first create a &lt;code&gt;certificate signing request (csr)&lt;/code&gt; for the server certificate: &lt;code&gt;server.csr&lt;/code&gt; and the key &lt;code&gt;server.key&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-out&lt;/span&gt; server.csr &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:4096 &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-keyout&lt;/span&gt; server.key &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=test.com/O=PM"&lt;/span&gt; &lt;span class="nt"&gt;-config&lt;/span&gt; server.cnf &lt;span class="nt"&gt;-extensions&lt;/span&gt; req_ext
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will notice a &lt;code&gt;server.cnf&lt;/code&gt; config file. This file consists of extensions for adding Subject Alternative Names (&lt;code&gt;SAN&lt;/code&gt;), which are alternative host names that can identify the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[req]
default_bits = 4096
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
CN = 'test.com'
[ req_ext ]
subjectAltName = @alt_names
[alt_names]
DNS.1   = 'test.com'
DNS.2   = 'test.server.svc.cluster.local'
DNS.3   = 'localhost'
DNS.4   = 'test'
# add more for other names
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will then use the &lt;code&gt;server.csr&lt;/code&gt; to create a signed server certificate: &lt;code&gt;server.pem&lt;/code&gt;. The signing is done by the &lt;code&gt;rootCA&lt;/code&gt; certificate that was generated in &lt;code&gt;step a&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-CA&lt;/span&gt; rootCA.pem &lt;span class="nt"&gt;-CAkey&lt;/span&gt; rootCA.key &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; server.csr &lt;span class="nt"&gt;-out&lt;/span&gt; server.pem &lt;span class="nt"&gt;-extensions&lt;/span&gt; req_ext &lt;span class="nt"&gt;-extfile&lt;/span&gt; server.cnf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Notice: We use the &lt;code&gt;rootCA&lt;/code&gt; certificate and private key to sign the certificate.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;b)&lt;u&gt;Generate client certificate&lt;/u&gt;&lt;br&gt;
Similarly, we create client certificates &amp;amp; key: &lt;code&gt;client.pem&lt;/code&gt;| &lt;code&gt;client.key&lt;/code&gt;. For the ease of this demo , we use the same &lt;code&gt;rootCA&lt;/code&gt; to sign the client certificates.&lt;/p&gt;

&lt;p&gt;As before we use a &lt;code&gt;client.cnf&lt;/code&gt; also maintained for &lt;code&gt;SAN&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[req]
default_bits = 4096
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
CN = 'localhost'
[ req_ext ]
subjectAltName = @alt_names
[alt_names]
DNS.1   = 'localhost' 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating the &lt;code&gt;csr&lt;/code&gt; and signing it with the &lt;code&gt;rootCA&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-out&lt;/span&gt; client.csr &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:4096 &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-keyout&lt;/span&gt; client.key &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=localhost/O=client"&lt;/span&gt; &lt;span class="nt"&gt;-extensions&lt;/span&gt; req_ext &lt;span class="nt"&gt;-config&lt;/span&gt; client.cnf

openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-CA&lt;/span&gt; rootCA.pem &lt;span class="nt"&gt;-CAkey&lt;/span&gt; rootCA.key &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; client.csr &lt;span class="nt"&gt;-out&lt;/span&gt; client.pem &lt;span class="nt"&gt;-extensions&lt;/span&gt; req_ext &lt;span class="nt"&gt;-extfile&lt;/span&gt; client.cnf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1) Deploy on Kubernetes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prerequisite&lt;/strong&gt;: Please install the &lt;a href="https://opentelemetry.io/docs/platforms/kubernetes/operator/" rel="noopener noreferrer"&gt;opentelemetry-operator&lt;/a&gt; in the cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts

helm &lt;span class="nb"&gt;install &lt;/span&gt;otel-operator open-telemetry/opentelemetry-operator &lt;span class="nt"&gt;--namespace&lt;/span&gt; otel-collector-operator &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="s2"&gt;"manager.collectorImage.repository=otel/opentelemetry-collector-k8s"&lt;/span&gt; &lt;span class="nt"&gt;--set&lt;/span&gt; admissionWebhooks.certManager.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--set&lt;/span&gt; admissionWebhooks.autoGenerateCert.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;a)&lt;u&gt;Create namespaces&lt;/u&gt;&lt;br&gt;
Create the 2 namespaces&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create ns server
kubectl create ns client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b)&lt;u&gt;Create Kubernetes secrets.&lt;/u&gt;&lt;br&gt;
The certificates that were generated in &lt;strong&gt;step 1&lt;/strong&gt; will be used to create the secrets. These secrets will be mounted into the OTEL pods in the next step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;k create secret generic server &lt;span class="nt"&gt;-n&lt;/span&gt; server  &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tls.pem&lt;span class="o"&gt;=&lt;/span&gt;server.pem &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tls.key&lt;span class="o"&gt;=&lt;/span&gt;server.key &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ca.pem&lt;span class="o"&gt;=&lt;/span&gt;rootCA.pem &lt;span class="c"&gt;# server certificates&lt;/span&gt;

k create secret generic client &lt;span class="nt"&gt;-n&lt;/span&gt; client  &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tls.pem&lt;span class="o"&gt;=&lt;/span&gt;client.pem &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tls.key&lt;span class="o"&gt;=&lt;/span&gt;client.key &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ca.pem&lt;span class="o"&gt;=&lt;/span&gt;rootCA.pem &lt;span class="c"&gt;# client certificates&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;c)&lt;u&gt;Deploy the OTEL Receiver (server)&lt;/u&gt;&lt;br&gt;
The OTEL receiver is the server that accepts the metrics. For our demo purpose the OTEL server will receive the metrics (Port 4317) and print it in the logs. The server is deployed in &lt;code&gt;server&lt;/code&gt; namespace and the secrets are mounted as volume to be used by the otel-collector receiver.&lt;/p&gt;

&lt;p&gt;We also create a service named test with port 4317. While this is optional for the demo, its a good practice. Additionally certain meshes..For E.g istio relies on the port &lt;code&gt;name&lt;/code&gt; for the protocol. (&lt;code&gt;grpc&lt;/code&gt; is the name of the port in the service here)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opentelemetry.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OpenTelemetryCollector&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;server&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;otel-collector&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deployment&lt;/span&gt;
  &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;server&lt;/span&gt;
    &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/pki/ca-trust/source/server-ca&lt;/span&gt;
    &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;server&lt;/span&gt;
    &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;server&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;receivers:&lt;/span&gt;
      &lt;span class="s"&gt;otlp:&lt;/span&gt;
        &lt;span class="s"&gt;protocols:&lt;/span&gt;
          &lt;span class="s"&gt;grpc:&lt;/span&gt;
            &lt;span class="s"&gt;#endpoint: test.server.svc.cluster.local:4317&lt;/span&gt;
            &lt;span class="s"&gt;tls:&lt;/span&gt;
              &lt;span class="s"&gt;cert_file: /etc/pki/ca-trust/source/server-ca/tls.pem&lt;/span&gt;
              &lt;span class="s"&gt;key_file: /etc/pki/ca-trust/source/server-ca/tls.key&lt;/span&gt;
              &lt;span class="s"&gt;client_ca_file: /etc/pki/ca-trust/source/server-ca/ca.pem&lt;/span&gt;
    &lt;span class="s"&gt;exporters:&lt;/span&gt;
      &lt;span class="s"&gt;# NOTE: Prior to v0.86.0 use `logging` instead of `debug`.&lt;/span&gt;
      &lt;span class="s"&gt;debug:&lt;/span&gt;
        &lt;span class="s"&gt;verbosity: detailed&lt;/span&gt;
    &lt;span class="s"&gt;service:&lt;/span&gt;
      &lt;span class="s"&gt;pipelines:&lt;/span&gt;
        &lt;span class="s"&gt;metrics:&lt;/span&gt;
          &lt;span class="s"&gt;receivers: [otlp]&lt;/span&gt;
          &lt;span class="s"&gt;processors: []&lt;/span&gt;
          &lt;span class="s"&gt;exporters: [debug]&lt;/span&gt;
&lt;span class="s"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opentelemetry-collector&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test.test&lt;/span&gt;
    &lt;span class="na"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-collector&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grpc&lt;/span&gt; &lt;span class="c1"&gt;# important for istio!&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4317&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the otel-collector instance and service is created&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods,svc &lt;span class="nt"&gt;-n&lt;/span&gt; server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b)&lt;u&gt;Deploy the OTEL Exporter (client)&lt;/u&gt;&lt;br&gt;
The OTEL Exporter is the client that generates (i.e source) the metrics. We use the &lt;a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/hostmetricsreceiver/README.md" rel="noopener noreferrer"&gt;hostmetrics&lt;/a&gt; receiver plugin. This will vary according to the needs.&lt;br&gt;
The other receiver is deployed in the &lt;code&gt;client&lt;/code&gt; namespace. The client certificates are mounted as &lt;code&gt;volumeMounts&lt;/code&gt; just as before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opentelemetry.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OpenTelemetryCollector&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deployment&lt;/span&gt;
  &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client&lt;/span&gt;
    &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/pki/ca-trust/source/client-ca&lt;/span&gt;
    &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client&lt;/span&gt;
    &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;receivers:&lt;/span&gt;
      &lt;span class="s"&gt;# Data sources: metrics&lt;/span&gt;
      &lt;span class="s"&gt;hostmetrics:&lt;/span&gt;
        &lt;span class="s"&gt;scrapers:&lt;/span&gt;
          &lt;span class="s"&gt;cpu:&lt;/span&gt;
          &lt;span class="s"&gt;disk:&lt;/span&gt;
    &lt;span class="s"&gt;processors:&lt;/span&gt;
      &lt;span class="s"&gt;# Data sources: traces&lt;/span&gt;
      &lt;span class="s"&gt;attributes:&lt;/span&gt;
        &lt;span class="s"&gt;actions:&lt;/span&gt;
          &lt;span class="s"&gt;- key: environment&lt;/span&gt;
            &lt;span class="s"&gt;value: external-test-3&lt;/span&gt;
            &lt;span class="s"&gt;action: insert&lt;/span&gt;
      &lt;span class="s"&gt;# Data sources: traces, metrics, logs&lt;/span&gt;
    &lt;span class="s"&gt;exporters:&lt;/span&gt;
      &lt;span class="s"&gt;otlp:&lt;/span&gt;
        &lt;span class="s"&gt;endpoint: test.server.svc.cluster.local:4317&lt;/span&gt;
        &lt;span class="s"&gt;tls:&lt;/span&gt;
          &lt;span class="s"&gt;ca_file: /etc/pki/ca-trust/source/client-ca/ca.pem&lt;/span&gt;
          &lt;span class="s"&gt;cert_file: /etc/pki/ca-trust/source/client-ca/tls.pem&lt;/span&gt;
          &lt;span class="s"&gt;key_file: /etc/pki/ca-trust/source/client-ca/tls.key&lt;/span&gt;
      &lt;span class="s"&gt;debug: #just to print metrics in logs of the client&lt;/span&gt;
    &lt;span class="s"&gt;service:&lt;/span&gt;
      &lt;span class="s"&gt;pipelines:&lt;/span&gt;
        &lt;span class="s"&gt;metrics:&lt;/span&gt;
          &lt;span class="s"&gt;receivers: [hostmetrics]&lt;/span&gt;
          &lt;span class="s"&gt;processors: [attributes]&lt;/span&gt;
          &lt;span class="s"&gt;exporters: [otlp,debug]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the otelcollector instance is created&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;c) &lt;u&gt;Verify the setup&lt;/u&gt;&lt;br&gt;
Check the logs of the server otel (receiver). You should see logs of metrics generated by the client otel (exporter).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;otel-receiver-pod&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>mtls</category>
      <category>opentelemetry</category>
      <category>kubernetes</category>
      <category>observability</category>
    </item>
    <item>
      <title>Accessing Privileged Containers in Cloud Foundry: A Guide</title>
      <dc:creator>Vipin Vijaykumar</dc:creator>
      <pubDate>Thu, 19 Dec 2024 09:28:25 +0000</pubDate>
      <link>https://dev.to/vipinvkmenon/accessing-privileged-containers-in-cloud-foundry-a-guide-5fim</link>
      <guid>https://dev.to/vipinvkmenon/accessing-privileged-containers-in-cloud-foundry-a-guide-5fim</guid>
      <description>&lt;p&gt;&lt;strong&gt;Usually, your productive apps don’t and shouldn't have SSH access in Cloud Foundry.&lt;/strong&gt; However, you might need to SSH into an application for debugging purposes. Fortunately, this is achievable by enabling the SSH permission for both the &lt;a href="https://cli.cloudfoundry.org/en-US/v6/allow-space-ssh.html" rel="noopener noreferrer"&gt;space&lt;/a&gt; and the &lt;a href="https://docs.cloudfoundry.org/devguide/deploy-apps/ssh-apps.html#enable-disable-ssh" rel="noopener noreferrer"&gt;app&lt;/a&gt; and be &lt;a href="https://docs.cloudfoundry.org/running/config-ssh.html" rel="noopener noreferrer"&gt;enabled by the operator&lt;/a&gt;, after which you can use the cf ssh command. This approach suffices for most scenarios.&lt;/p&gt;

&lt;p&gt;But sometimes, this level of access isn’t enough. You might require privileged access to the application container for advanced troubleshooting or debugging. Why might you need this? That’s a question only you can answer, but this blog will show you how it’s done.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;This process can only be executed if you are an operator of the Cloud Foundry deployment and can access the Diego cells.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Steps to Gain Privileged Access&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieve the app-guid for the application
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cf app &amp;lt;app-name&amp;gt; --guid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Log in to BOSH and target the Cloud Foundry deployment
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bosh -d cf vms
bosh -d cf ssh &amp;lt;diego-cell&amp;gt;  #(any Diego cell)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Use the cfdot utility to locate application details&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In Diego terms, applications are referred to as “lrps” (long-running processes). Using the app-guid, identify the lrp instance_id and the diego_cell where the application or process is running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cfdot actual-lrps | grep -E '&amp;lt;app-guid&amp;gt;' | jq -r '. | select(.index == 0)' # Index 0 is for the 0th instance of the application.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will provide the instance_guid and cell_id of the application container.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SSH into the respective Diego Cell
From the cell_id identified in the previous step, SSH into the corresponding Diego Cell.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bosh -d cf ssh &amp;lt;diego_cell&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Attach to the Garden container
You can attach it to the container using one of two methods:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Approach 1: Using &lt;code&gt;runc&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo /var/vcap/packages/runc/bin/runc --root /run/containerd/runc/garden/ exec &amp;lt;instance_guid&amp;gt; --tty /bin/sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Approach 2: Using ctr&lt;br&gt;
This approach is not recommended as the &lt;code&gt;ctr&lt;/code&gt; command line is not actively maintained and the commands may vary over time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo /var/vcap/packages/containerd/bin/ctr -a /var/vcap/sys/run/containerd/containerd.sock -n garden tasks exec --exec-id my-shell --tty &amp;lt;instance_guid&amp;gt; /bin/sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running either of these commands, you will have privileged access to the application container.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;! A Word of Caution !&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With great power comes great responsibility. &lt;br&gt;
Privileged access should be used sparingly and responsibly if possible NEVER. Always assess the necessity of these steps, and avoid this process unless required. &lt;br&gt;
Things can break here if you are NOT careful!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I've messed up something what do I do&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fear not. Mostly deleting the app instance can reset and set things back to usual.&lt;/p&gt;

</description>
      <category>cloudfoundry</category>
      <category>containers</category>
      <category>linux</category>
      <category>cloud</category>
    </item>
    <item>
      <title>How to get into an app container manually in Cloud Foundry with Garden-Containerd Backend</title>
      <dc:creator>Vipin Vijaykumar</dc:creator>
      <pubDate>Mon, 18 Nov 2019 09:54:26 +0000</pubDate>
      <link>https://dev.to/vipinvkmenon/how-to-get-into-an-app-container-manually-in-cloud-foundry-with-garden-containerd-backend-23l3</link>
      <guid>https://dev.to/vipinvkmenon/how-to-get-into-an-app-container-manually-in-cloud-foundry-with-garden-containerd-backend-23l3</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;A method to get into a container on the Diego Cell&lt;/p&gt;

&lt;h2&gt;
  
  
  Purpose
&lt;/h2&gt;

&lt;p&gt;The requirement is to be able to access the application container in Cloud Foundry in scenarios where cf ssh  would not be possible. Main reasons being troubleshooting inside the container. This write-up describes the procedures for the same. The original article for containers based on garden-runc is provided below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Procedure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Obtain the app &lt;code&gt;process-guid&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cf curl /v2/apps/$(cf app &amp;lt;app-name&amp;gt; --guid)| jq -r '.metadata.guid + "-" + .entity.version'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Obtain the hosts in which the app is running. Choose one of the I
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cf curl v2/apps/$(cf app &amp;lt;app-name&amp;gt; --guid)/stats | grep host
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Login to the bosh director.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Identify the cell/VM Instance name associated to the IP&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SSH into the VM&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bosh -e &amp;lt;environment&amp;gt; -d &amp;lt;deployment&amp;gt; ssh &amp;lt;Instance-Name&amp;gt;
diego-cell/8cbe944d-d0cc-4158-802e-59426506faf7:~$ sudo -i
diego-cell/8cbe944d-d0cc-4158-802e-59426506faf7:~#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Obtain the "instance_guid"   for the process-guid from step 1
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cfdot actual-lrp-groups | grep d7c18647-9560-4327-8bcc-b7ab1ecdf295-2d6ebf81-7857-46d8-8bdc-f958795abf86
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Attach into the container using ctr  (containerd client)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export containerid=&amp;lt;Instance_Guid_&amp;gt;
/var/vcap/packages/containerd/bin/ctr -a /var/vcap/sys/run/containerd/containerd.sock -n garden tasks exec --exec-id my-shell --tty $containerd /bin/bash
root@9b5900f2-34f3-4317-63bb-863c:/#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You will now be in the container.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cloudfoundry</category>
      <category>docker</category>
      <category>cloud</category>
      <category>bosh</category>
    </item>
  </channel>
</rss>
