From 902c3746e3b3990452a575226e3d0b285469bf76 Mon Sep 17 00:00:00 2001 From: Prateek-Sharma13 Date: Wed, 8 Oct 2025 12:28:49 +0530 Subject: [PATCH 1/9] doc: created pulumi and ibmcloud tutorial --- .gitignore | 1 + docs/pulumi.md | 652 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 653 insertions(+) create mode 100644 docs/pulumi.md diff --git a/.gitignore b/.gitignore index 9f11b75..68729d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .idea/ +.DS_Store \ No newline at end of file diff --git a/docs/pulumi.md b/docs/pulumi.md new file mode 100644 index 0000000..d9d4c9e --- /dev/null +++ b/docs/pulumi.md @@ -0,0 +1,652 @@ +# IBM Cloud and Pulumi + +[Pulumi](https://www.pulumi.com/docs/iac/get-started/) is an open-source Infrastructure as Code (IaC) platform that lets you automate, secure, and manage cloud resources, configurations, and secrets — all using real programming languages like Python, Go, .NET, TypeScript, C# etc. + +With Pulumi, you can define cloud infrastructure using familiar coding practices such as loops, functions, and classes, making your cloud automation more powerful and maintainable than traditional declarative tools. + +When used with IBM Cloud, Pulumi allows you to provision and manage a wide range of IBM Cloud services — such as VPCs, Kubernetes clusters, Object Storage, and Databases — using Python or any supported language. + +This enables developers to: + +- Write infrastructure code in a language they already know. +- Automate IBM Cloud environments using version control and CI/CD pipelines. +- Integrate IBM Terraform modules to reuse existing templates and best practices. + +## Table of Contents + +- [Introduction to Pulumi with Python](#introduction) +- [Installation](#installation) +- [Key Concepts](#concepts) +- [Pulumi Configuration](#pulumi-configuration) +- [Pulumi Commands](#commands) +- [Usage](#usage) +- [Python Examples](#python-examples) +- [Best Practices](#best-practices) +- [Troubleshooting Common Issues](#troubleshooting-common-issues) +- [References](#references) + +## Introduction + +Pulumi is an Infrastructure as Code (IaC) platform that allows you to define cloud infrastructure using various programming languages. This tutorial focuses on using Pulumi with IBM Cloud through Python. + +**Why Pulumi with Python?** + +- Familiar syntax: Use Python, one of the most popular programming languages. +- Rich ecosystem: Leverage Python's extensive library ecosystem. +- Strong typing: Benefit from type hints and IDE support. +- Reusable components: Create classes and modules for infrastructure. +- Testing: Write unit tests for your infrastructure code. + + +## Installation + +Before starting, ensure you have the following: + +1. **IBM Cloud Account** + - Sign up at [https://cloud.ibm.com](https://cloud.ibm.com). + - Create and download the IBMCloud API key. + +2. **Installed Tools** + + | Tool | Description | Installation | + |------|--------------|---------------| + | Pulumi | Infrastructure as Code tool | [Install Pulumi](https://www.pulumi.com/docs/install/) | + | Python | Runtime for your Pulumi program | [Install Python 3.8+](https://www.python.org/downloads/) | + | IBM Cloud CLI | To manage cloud resources | [Install IBM Cloud CLI](https://cloud.ibm.com/docs/cli?topic=cli-getting-started) | + | Terraform (optional) | For using IBM Terraform modules | [Install Terraform](https://developer.hashicorp.com/terraform/downloads) | + +3. **Set Environment Variables** + + ```bash + export IBMCLOUD_API_KEY= + export PULUMI_CONFIG_PASSPHRASE= + + # Install Pulumi + curl -fsSL https://get.pulumi.com | sh + + # Install dependencies: + pip install pulumi pulumi_ibm + + # Verify installation + pulumi version + python --version + + # Configure IBM Cloud: + + ibmcloud login + pulumi config set ibm:region us-south + + # Create a new Pulumi Project: + + mkdir pulumi-ibmcloud-demo && cd pulumi-ibmcloud-demo + pulumi new python -y + ``` + +## Concepts + +**Stack** - A Stack is an isolated instance of your Pulumi program, typically representing an environment (dev, staging, prod). + +```py +import pulumi + +# Get current stack name +stack = pulumi.get_stack() +print(f"Current stack: {stack}") +``` + +**Project** - A Project is a single Pulumi program containing your infrastructure definitions. + +**Configuration** - Stack-specific settings that can be changed without modifying code. + +**State** - Pulumi maintains state to track current infrastructure and manage updates. + + +## Pulumi Configuration + +Configuration Files - Configure IBM Cloud Provider in Pulumi + +In `Pulumi.yaml`, add: + +```yaml +name: pulumi-ibmcloud-demo +runtime: python +description: IBM Cloud infrastructure example using Python +``` + +In `Pulumi.dev.yaml` add the Stack-specific configuration: + +```py +config: + pulumi-ibmcloud-demo::apiKey: ${IBMCLOUD_API_KEY} + pulumi-ibmcloud-demo:region: us-south + pulumi-ibmcloud-demo:resource-group: default + pulumi-ibmcloud-demo:cos-instance-name: my-dev-cos +``` + +The IBM Cloud provider must be installed as a Local Package. Use below command to successfully generate a Python SDK for the ibm package: + +```bash +pulumi package add terraform-provider ibm-cloud/ibm +``` + +Setting Configuration + +```bash +# Set configuration values +pulumi config set region us-south +pulumi config set resource-group default --secret +pulumi config set --path tags '["env:dev", "team:platform"]' +``` + +Accessing Configuration in Python + +```py +import pulumi +import json + +config = pulumi.Config() + +# Get configuration values +region = config.require("region") +resource_group = config.get("resource-group") or "default" +tags = config.get_object("tags") or ["environment:dev"] + +# Type-safe configuration with classes +class AppConfig: + def __init__(self): + self.region = config.require("region") + self.resource_group = config.get("resource-group") or "default" + self.environment = pulumi.get_stack() + +app_config = AppConfig() +``` + +## Commands + +```bash +# Initialize a new Pulumi project with Python +pulumi new python + +# Create a new stack +pulumi stack init dev + +# Install dependencies +pip install -r requirements.txt + +# Preview infrastructure changes +pulumi preview + +# Deploy infrastructure +pulumi up + +# Destroy infrastructure +pulumi destroy + +# View stack outputs +pulumi stack output + +# View stack configuration +pulumi config + +# Refresh state +pulumi refresh +``` + +## Usage + +**Using Terraform IBM Modules (TIM) with Python** + +While Pulumi doesn't directly use Terraform modules, you can achieve similar patterns using Pulumi components and the IBM Cloud provider. + +Environment Setup + +```bash +# Set IBM Cloud API key +export IBMCLOUD_API_KEY=your_actual_api_key + +# Or configure via Pulumi config +pulumi config set ibm:apiKey --secret +``` + +Setting up IBM Cloud Provider in Python + +```py +import pulumi +import pulumi_ibm as ibm + +# Configure IBM Cloud provider +provider = ibm.Provider( + "ibm-provider", + region="us-south" + # Credentials are automatically picked from: + # 1. IBMCLOUD_API_KEY environment variable + # 2. IBM Cloud CLI configuration + # 3. Pulumi configuration +) +``` + +## Python Examples + +To grasp the core concepts and workflow of Pulumi, here are a few foundational examples to help you get started. + +
+ Example 1: Simple Cloud Object Storage Instance +
+ +```py +import pulumi +import pulumi_ibm as ibm + +config = pulumi.Config() +region = config.require("region") +resource_group = config.get("resource-group") or "default" + +# Create a Cloud Object Storage instance +cos_instance = ibm.resource.Instance( + "my-cos-instance", + name="my-dev-cos-instance", + service="cloud-object-storage", + plan="lite", + location="global", + resource_group_name=resource_group, + tags=["environment:dev", "managed-by:pulumi"] +) + +# Create a bucket in the COS instance +cos_bucket = ibm.cos.Bucket( + "my-bucket", + bucket_name="my-unique-bucket-name", + resource_instance_id=cos_instance.id, + region=region, + storage_class="standard", + endpoint_type="public" +) + +# Export the bucket details +pulumi.export("bucket_name", cos_bucket.bucket_name) +pulumi.export("bucket_region", cos_bucket.region) +pulumi.export("cos_instance_id", cos_instance.id) +``` + +
+ +
+ Example 2: VPC with Subnets and Security Groups +
+ +```py +import pulumi +import pulumi_ibm as ibm + +config = pulumi.Config() +region = config.require("region") + +# Create a VPC +vpc = ibm.is.Vpc( + "my-vpc", + name="my-development-vpc", + resource_group=config.get("resource-group"), + tags=["environment:dev"] +) + +# Create a subnet +subnet = ibm.is.Subnet( + "my-subnet", + name="my-subnet", + vpc=vpc.id, + zone=f"{region}-1", + ipv4_cidr_block="10.240.0.0/24", + tags=["environment:dev"] +) + +# Create a security group +security_group = ibm.is.SecurityGroup( + "my-security-group", + name="my-security-group", + vpc=vpc.id, + rules=[ + ibm.is.SecurityGroupRuleArgs( + direction="inbound", + ip_version="ipv4", + protocol="tcp", + port_min=22, + port_max=22, + remote="0.0.0.0/0" + ), + ibm.is.SecurityGroupRuleArgs( + direction="inbound", + ip_version="ipv4", + protocol="tcp", + port_min=80, + port_max=80, + remote="0.0.0.0/0" + ) + ] +) + +# Export VPC details +pulumi.export("vpc_id", vpc.id) +pulumi.export("vpc_name", vpc.name) +pulumi.export("subnet_id", subnet.id) +pulumi.export("security_group_id", security_group.id) + +``` + +
+ + +
+ Example 3: IAM Service ID and API Key +
+ +```py +import pulumi +import pulumi_ibm as ibm + +# Create a service ID +service_id = ibm.iam.ServiceId( + "my-service-id", + name="my-app-service-id", + description="Service ID for my application" +) + +# Create an API key for the service ID +api_key = ibm.iam.ServiceApiKey( + "my-api-key", + name="my-app-api-key", + iam_service_id=service_id.iam_id, + description="API key for my application" +) + +# Export the API key (be careful with this in production!) +pulumi.export("api_key_value", api_key.apikey) +``` + +
+ +
+ Example 4: Kubernetes Cluster with Node Pool +
+ +```py +import pulumi +import pulumi_ibm as ibm + +config = pulumi.Config() + +# Create a Kubernetes cluster +cluster = ibm.container.VpcCluster( + "my-cluster", + name="my-k8s-cluster", + vpc_id=config.require("vpc_id"), + subnet_ids=config.require_object("subnet_ids"), + kube_version=config.get("kube_version") or "1.28", + flavor=config.get("flavor") or "bx2.4x16", + worker_count=config.get_int("worker_count") or 2, + resource_group_id=config.get("resource_group_id"), + tags=["environment:dev", "managed-by:pulumi"] +) + +# Create a worker pool +worker_pool = ibm.container.VpcWorkerPool( + "my-worker-pool", + cluster=cluster.id, + flavor=config.get("worker_pool_flavor") or "bx2.4x16", + worker_count=config.get_int("worker_pool_count") or 3, + name="additional-worker-pool", + vpc_id=config.require("vpc_id"), + subnet_ids=config.require_object("subnet_ids") +) + +# Export cluster details +pulumi.export("cluster_id", cluster.id) +pulumi.export("cluster_name", cluster.name) +pulumi.export("kube_version", cluster.kube_version) +``` + +
+ +## Best Practices + +**(a) Configuration Management with Dataclasses** + +Instead of scattering configuration access throughout your code, create dedicated configuration classes that centralize all your configuration needs. This provides several benefits: + +- Type Safety: Catch configuration errors at development time rather than deployment time. +- Documentation: The configuration structure serves as documentation for what settings are available +- Validation: You can add validation logic to ensure configuration values are correct +- Reusability: The same configuration pattern can be used across multiple projects + +This dataclass example shows how to create a clean, typed configuration interface that validates required settings and provides sensible defaults for optional ones. + +```py +from dataclasses import dataclass +from typing import List, Optional +import pulumi + +@dataclass +class AppConfig: + region: str + resource_group: str + environment: str + tags: List[str] + + @classmethod + def from_pulumi_config(cls): + config = pulumi.Config() + return cls( + region=config.require("region"), + resource_group=config.get("resource-group") or "default", + environment=pulumi.get_stack(), + tags=config.get_object("tags") or ["managed-by:pulumi"] + ) + +app_config = AppConfig.from_pulumi_config() +``` + +**(b) Error Handling and Validation** + +Resource naming constraints vary across cloud providers, and IBM Cloud has specific requirements for resource names. Instead of discovering naming issues during deployment, implement proactive validation: + + - Consistency: Ensure all resources follow a consistent naming pattern across your organization + - Compliance: Enforce naming conventions that include environment, project, and team information + - Uniqueness: Prevent naming collisions by incorporating stack names and timestamps + - Readability: Make resource names meaningful for operations teams + +The validation function below demonstrates how to programmatically enforce naming standards, ensuring your infrastructure remains organized and maintainable. + + +```py +import re +import pulumi + +def validate_resource_name(name: str) -> str: + """Validate and format resource names.""" + stack = pulumi.get_stack() + formatted_name = f"{name}-{stack}".lower() + # Remove invalid characters + cleaned_name = re.sub(r'[^a-z0-9-]', '-', formatted_name) + # Ensure it starts with a letter + if not cleaned_name[0].isalpha(): + cleaned_name = f"res-{cleaned_name}" + return cleaned_name + +# Usage +cos_instance = ibm.resource.Instance( + "cos", + name=validate_resource_name("my-app-cos"), + # ... other properties +) +``` + +**(c) Tagging Strategy** + +Tags are crucial for cost management, operations, and security in cloud environments. A well-designed tagging strategy helps: + +- Cost Allocation: Track spending by department, project, or environment. +- Operations: Filter and search resources during incident response. +- Automation: Enable automated policies based on tag values. +- Compliance: Demonstrate resource ownership and purpose. + +The tagging utility functions below shows how to implement a consistent tagging approach that combines base tags (applied to all resources) with resource-specific tags, making your infrastructure self-documenting. + +```py +from typing import Dict +import pulumi + +def get_base_tags() -> Dict[str, str]: + """Get base tags for all resources.""" + return { + "Environment": pulumi.get_stack(), + "ManagedBy": "pulumi", + "Project": "my-project", + "Created": "2024-01-01" # In real code, use datetime + } + +def apply_tags(tags: Dict[str, str]) -> pulumi.Input[list]: + """Convert tag dictionary to IBM Cloud tag format.""" + base_tags = get_base_tags() + all_tags = {**base_tags, **tags} + return [f"{k}:{v}" for k, v in all_tags.items()] + +# Usage +cos_instance = ibm.resource.Instance( + "cos", + name="my-cos", + service="cloud-object-storage", + plan="standard", + tags=apply_tags({"Purpose": "application-data"}) +) +``` + +**(d) Resource Dependencies and Ordering** + +While Pulumi can often infer dependencies automatically, explicit dependency declaration is crucial for: + +- Predictable Deployments: Ensure resources are created in the correct order +- Error Prevention: Avoid race conditions where resources reference others that don't exist yet +- Clear Intent: Make the resource relationships obvious to other developers +- Performance: Optimize deployment parallelism where safe + +The dependency example below demonstrates how to explicitly declare relationships between resources, particularly important when dealing with networking components that must exist before dependent resources. + +```py +import pulumi +import pulumi_ibm as ibm + +# Explicit dependency management +vpc = ibm.is.Vpc("main-vpc", name="main-vpc") + +# Subnet depends on VPC +subnet = ibm.is.Subnet( + "main-subnet", + name="main-subnet", + vpc=vpc.id, + zone="us-south-1", + ipv4_cidr_block="10.240.0.0/24", + # Explicit dependency + opts=pulumi.ResourceOptions(depends_on=[vpc]) +) + +# Security group depends on VPC +security_group = ibm.is.SecurityGroup( + "main-sg", + name="main-security-group", + vpc=vpc.id, + opts=pulumi.ResourceOptions(depends_on=[vpc]) +) +``` + +**(e) Output Processing and Transformation** + +Pulumi's Output system handles the asynchronous nature of cloud resource creation. Proper output management enables: + +- Data Transformation: Process resource attributes into useful formats +- Cross-Resource References: Safely use outputs from one resource as inputs to another +- Runtime Configuration: Generate configuration files, connection strings, or URLs based on created resources +- Monitoring: Export meaningful information for operations teams + +The output processing examples show how to work with Pulumi's functional programming model to transform and combine resource outputs, creating valuable derived information from your infrastructure. + +```py +import pulumi +import pulumi_ibm as ibm + +cos_instance = ibm.resource.Instance("cos", ...) + +# Process outputs +cos_endpoint = cos_instance.id.apply( + lambda id: f"https://{id}.s3.us-south.cloud-object-storage.appdomain.cloud" +) + +# Combine multiple outputs +cluster_details = pulumi.Output.all( + cluster.id, + cluster.name, + cluster.kube_version +).apply(lambda args: { + "id": args[0], + "name": args[1], + "version": args[2] +}) + +pulumi.export("cos_endpoint", cos_endpoint) +pulumi.export("cluster_details", cluster_details) +``` + +**(f) Security Practices** + +When working with IBM Cloud IAM, follow security best practices: + +- **Secrets**: Store sensitive data using `pulumi config set --secret ` +- **State Management**: Use Pulumi Cloud or IBM COS bucket for secure state storage. +- Commit only code, not `.pulumi/` state directories. +- Follow Principle of Least Privilege. + +**(g) Cleanup: Implement automated cleanup of unused resources.** + +**(h) Wrap patterns in reusable functions or classes.** + +## Troubleshooting Common Issues + +a) Authentication Problems + +```bash +# Verify IBM Cloud login +ibmcloud account show + +# Check API key +echo ${IBMCLOUD_API_KEY} + +# Test with Pulumi preview +pulumi preview +``` + +b) Python Dependency Issues + +```bash +# Ensure you're in the virtual environment +source venv/bin/activate + +# Install dependencies +pip install -r requirements.txt + +# Update Pulumi +pulumi update +``` + +c) Resource Naming Conflicts + +```py +# Use unique names with stack suffix +def get_resource_name(base_name: str) -> str: + stack = pulumi.get_stack() + return f"{base_name}-{stack}" +```` + +## References + +- [Pulumi IBM Cloud Provider](https://www.pulumi.com/registry/packages/ibm/) +- [IBM Cloud Terraform Modules](https://github.com/terraform-ibm-modules) +- [Pulumi Terraform Conversion Guide](https://www.pulumi.com/docs/using-pulumi/adopting-pulumi/migrating/terraform/) +- [terraform-ibm-base-ocp-vpc Module](https://github.com/terraform-ibm-modules/terraform-ibm-base-ocp-vpc) From cb774fdd9a983a3984e4dfa35ec4693a6d0f77fe Mon Sep 17 00:00:00 2001 From: Prateek-Sharma13 Date: Wed, 8 Oct 2025 12:37:00 +0530 Subject: [PATCH 2/9] doc: updated reference to doc --- README.md | 1 + docs/_sidebar.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index b5d8d7b..9791a79 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ You can see the published documentation at https://terraform-ibm-modules.github. - [Porting an older module ](https://terraform-ibm-modules.github.io/documentation/#/migrate-module.md) - [Validation tests](https://terraform-ibm-modules.github.io/documentation/#/tests.md) - [GitHub Actions workflows](https://terraform-ibm-modules.github.io/documentation/#/gh-actions.md) + - [Pulumi and IBMCloud](pulumi.md) - [Adding your module to IBM Cloud](https://terraform-ibm-modules.github.io/documentation/#/onboard-ibm-cloud.md) - Maintaining the GitHub project - [Maintainers contribution process](https://terraform-ibm-modules.github.io/documentation/#/maintain-module.md) diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 4b99a59..95ca2f3 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -7,6 +7,7 @@ - [Porting an older module ](migrate-module.md) - [Validation tests](tests.md) - [GitHub Actions workflows](gh-actions.md) + - [Pulumi and IBMCloud](pulumi.md) - [Adding your module to IBM Cloud](onboard-ibm-cloud.md) - Maintaining the GitHub project - [Maintainers contribution process](maintain-module.md) From 90134d8f5645756b6536ebb76348973c91dfd7c6 Mon Sep 17 00:00:00 2001 From: terraform-ibm-modules-ops Date: Wed, 8 Oct 2025 07:07:19 +0000 Subject: [PATCH 3/9] docs: README ToC updated from sidebar [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9791a79..0e70be1 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ You can see the published documentation at https://terraform-ibm-modules.github. - [Porting an older module ](https://terraform-ibm-modules.github.io/documentation/#/migrate-module.md) - [Validation tests](https://terraform-ibm-modules.github.io/documentation/#/tests.md) - [GitHub Actions workflows](https://terraform-ibm-modules.github.io/documentation/#/gh-actions.md) - - [Pulumi and IBMCloud](pulumi.md) + - [Pulumi and IBMCloud](https://terraform-ibm-modules.github.io/documentation/#/pulumi.md) - [Adding your module to IBM Cloud](https://terraform-ibm-modules.github.io/documentation/#/onboard-ibm-cloud.md) - Maintaining the GitHub project - [Maintainers contribution process](https://terraform-ibm-modules.github.io/documentation/#/maintain-module.md) From 26b5c83164bade47f2481c03c63f0192ef28e7aa Mon Sep 17 00:00:00 2001 From: Prateek-Sharma13 Date: Wed, 22 Oct 2025 14:57:02 +0530 Subject: [PATCH 4/9] fix: examples --- docs/pulumi.md | 202 ++++++++++++++++++++++++++----------------------- 1 file changed, 109 insertions(+), 93 deletions(-) diff --git a/docs/pulumi.md b/docs/pulumi.md index d9d4c9e..89d7949 100644 --- a/docs/pulumi.md +++ b/docs/pulumi.md @@ -238,34 +238,33 @@ import pulumi import pulumi_ibm as ibm config = pulumi.Config() -region = config.require("region") -resource_group = config.get("resource-group") or "default" +resource_group = config.get("resource-group") or "Default" # Create a Cloud Object Storage instance -cos_instance = ibm.resource.Instance( +cos_instance = ibm.ResourceInstance( "my-cos-instance", name="my-dev-cos-instance", service="cloud-object-storage", plan="lite", location="global", - resource_group_name=resource_group, + resource_group_id=resource_group, tags=["environment:dev", "managed-by:pulumi"] ) -# Create a bucket in the COS instance -cos_bucket = ibm.cos.Bucket( +# Create a COS bucket +cos_bucket = ibm.CosBucket( "my-bucket", bucket_name="my-unique-bucket-name", resource_instance_id=cos_instance.id, - region=region, + region_location="us-south", storage_class="standard", endpoint_type="public" ) -# Export the bucket details +# Output the details pulumi.export("bucket_name", cos_bucket.bucket_name) -pulumi.export("bucket_region", cos_bucket.region) pulumi.export("cos_instance_id", cos_instance.id) + ``` @@ -279,129 +278,146 @@ import pulumi import pulumi_ibm as ibm config = pulumi.Config() -region = config.require("region") # Create a VPC -vpc = ibm.is.Vpc( +vpc = ibm.IsVpc( "my-vpc", name="my-development-vpc", resource_group=config.get("resource-group"), tags=["environment:dev"] ) +# Create an address prefix +vpc_address_prefix = ibm.IsVpcAddressPrefix( + "my-address-prefix", + cidr="10.0.1.0/24", + vpc=vpc.is_vpc_id, + zone="us-south-1" +) + # Create a subnet -subnet = ibm.is.Subnet( +vpc_subnet = ibm.IsSubnet( "my-subnet", - name="my-subnet", - vpc=vpc.id, - zone=f"{region}-1", - ipv4_cidr_block="10.240.0.0/24", - tags=["environment:dev"] + ipv4_cidr_block="10.0.1.0/24", + vpc=vpc.is_vpc_id, + zone="us-south-1", + opts = pulumi.ResourceOptions( + depends_on=[vpc_address_prefix] + ) ) # Create a security group -security_group = ibm.is.SecurityGroup( - "my-security-group", - name="my-security-group", - vpc=vpc.id, - rules=[ - ibm.is.SecurityGroupRuleArgs( - direction="inbound", - ip_version="ipv4", - protocol="tcp", - port_min=22, - port_max=22, - remote="0.0.0.0/0" - ), - ibm.is.SecurityGroupRuleArgs( - direction="inbound", - ip_version="ipv4", - protocol="tcp", - port_min=80, - port_max=80, - remote="0.0.0.0/0" - ) - ] +security_group = ibm.IsSecurityGroup( + "my-security-group", + vpc=vpc.is_vpc_id, ) -# Export VPC details +# Create Security Group Rules + +sg_rule_1 = ibm.IsSecurityGroupRule("my-sg-rule1", + group=security_group.is_security_group_id, + direction="inbound", + remote="127.0.0.1", + icmp={ + "code": 20, + "type": 30, + }) + +sg_rule_2 = ibm.IsSecurityGroupRule("my-sg-rule2", + group=security_group.is_security_group_id, + direction="inbound", + remote="127.0.0.1", + udp={ + "port_min": 805, + "port_max": 807, + }) + +sg_rule_3 = ibm.IsSecurityGroupRule("my-sg-rule3", + group=security_group.is_security_group_id, + direction="outbound", + remote="127.0.0.1", + tcp={ + "port_min": 8080, + "port_max": 8080, + }) + +# Output VPC details pulumi.export("vpc_id", vpc.id) pulumi.export("vpc_name", vpc.name) -pulumi.export("subnet_id", subnet.id) +pulumi.export("address_prefix", vpc_address_prefix.id) +pulumi.export("subnet_id", vpc_subnet.id) pulumi.export("security_group_id", security_group.id) ``` -
- Example 3: IAM Service ID and API Key + Example 3: Red Hat OpenShift Cluster with a default worker pool with one worker node.
```py import pulumi import pulumi_ibm as ibm -# Create a service ID -service_id = ibm.iam.ServiceId( - "my-service-id", - name="my-app-service-id", - description="Service ID for my application" -) - -# Create an API key for the service ID -api_key = ibm.iam.ServiceApiKey( - "my-api-key", - name="my-app-api-key", - iam_service_id=service_id.iam_id, - description="API key for my application" -) - -# Export the API key (be careful with this in production!) -pulumi.export("api_key_value", api_key.apikey) -``` +config = pulumi.Config() -
+# Create a VPC -
- Example 4: Kubernetes Cluster with Node Pool -
+vpc = ibm.IsVpc( + "my-vpc", + name="gen2-vpc", + resource_group=config.get("resource-group"), + tags=["environment:dev"] +) -```py -import pulumi -import pulumi_ibm as ibm +# Create an address prefix +vpc_address_prefix = ibm.IsVpcAddressPrefix( + "my-address-prefix", + cidr="10.0.1.0/24", + vpc=vpc.is_vpc_id, + zone="us-south-1" +) -config = pulumi.Config() +# Create a subnet +vpc_subnet = ibm.IsSubnet( + "my-subnet", + ipv4_cidr_block="10.0.1.0/24", + vpc=vpc.is_vpc_id, + zone="us-south-1", + opts = pulumi.ResourceOptions( + depends_on=[vpc_address_prefix] + ) +) -# Create a Kubernetes cluster -cluster = ibm.container.VpcCluster( - "my-cluster", - name="my-k8s-cluster", - vpc_id=config.require("vpc_id"), - subnet_ids=config.require_object("subnet_ids"), - kube_version=config.get("kube_version") or "1.28", - flavor=config.get("flavor") or "bx2.4x16", - worker_count=config.get_int("worker_count") or 2, - resource_group_id=config.get("resource_group_id"), - tags=["environment:dev", "managed-by:pulumi"] +# Create a COS instance +cos_instance = ibm.ResourceInstance("cosInstance", + service="cloud-object-storage", + plan="standard", + location="global" ) -# Create a worker pool -worker_pool = ibm.container.VpcWorkerPool( - "my-worker-pool", - cluster=cluster.id, - flavor=config.get("worker_pool_flavor") or "bx2.4x16", - worker_count=config.get_int("worker_pool_count") or 3, - name="additional-worker-pool", - vpc_id=config.require("vpc_id"), - subnet_ids=config.require_object("subnet_ids") +# Create an OCP Cluster +cluster = ibm.ContainerVpcCluster("my-ocp-cluster", + vpc_id=vpc.is_vpc_id, + kube_version="4.18.24_openshift", + flavor="bx2.16x64", + worker_count=2, + entitlement="cloud_pak", + cos_instance_crn=cos_instance.resource_instance_id, + resource_group_id=config.get("resource-group"), + zones=[{ + "subnet_id": vpc_subnet.id, + "name": "us-south-1", + }] ) -# Export cluster details -pulumi.export("cluster_id", cluster.id) -pulumi.export("cluster_name", cluster.name) -pulumi.export("kube_version", cluster.kube_version) +# Outputs +pulumi.export("vpc_id", vpc.id) +pulumi.export("vpc_name", vpc.name) +pulumi.export("subnet_id", vpc_subnet.id) +pulumi.export("ocp_cluster_id", cluster.id) + ```
From dc4e111df0f88d131a5d945d85e769a6097eb447 Mon Sep 17 00:00:00 2001 From: Prateek-Sharma13 Date: Wed, 22 Oct 2025 17:26:33 +0530 Subject: [PATCH 5/9] removed unused ref --- docs/pulumi.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/pulumi.md b/docs/pulumi.md index 89d7949..ed8c487 100644 --- a/docs/pulumi.md +++ b/docs/pulumi.md @@ -665,4 +665,3 @@ def get_resource_name(base_name: str) -> str: - [Pulumi IBM Cloud Provider](https://www.pulumi.com/registry/packages/ibm/) - [IBM Cloud Terraform Modules](https://github.com/terraform-ibm-modules) - [Pulumi Terraform Conversion Guide](https://www.pulumi.com/docs/using-pulumi/adopting-pulumi/migrating/terraform/) -- [terraform-ibm-base-ocp-vpc Module](https://github.com/terraform-ibm-modules/terraform-ibm-base-ocp-vpc) From b53fad145e5c44fc401d72a4c04277284c6addf6 Mon Sep 17 00:00:00 2001 From: Prateek-Sharma13 Date: Thu, 23 Oct 2025 12:00:21 +0530 Subject: [PATCH 6/9] tf module example included --- docs/pulumi.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/pulumi.md b/docs/pulumi.md index ed8c487..a6d8142 100644 --- a/docs/pulumi.md +++ b/docs/pulumi.md @@ -418,6 +418,50 @@ pulumi.export("vpc_name", vpc.name) pulumi.export("subnet_id", vpc_subnet.id) pulumi.export("ocp_cluster_id", cluster.id) +``` + + + +
+ Example 4: Use existing Terraform IBM Modules (TIM) to create Watson Discovery instance +
+ +In order to use a Terraform module in Pulumi, first add it to your project using the `pulumi package add` command. Refer [here](https://www.pulumi.com/docs/iac/guides/building-extending/using-existing-tools/use-terraform-module/) for more information. + +```sh + pulumi package add terraform-module [] +``` + +This will generate a local SDK which can be imported in the Pulumi program. + +So, the Watson discovery package can be added as: + +```sh +pulumi package add terraform-module terraform-ibm-modules/watsonx-discovery/ibm v1.11.1 wx-discovery +``` + +```py +import pulumi +import pulumi_ibm as ibm +import pulumi_wx_discovery as wxd_mod + + +config = pulumi.Config() + +wxd = wxd_mod.Module( + "my-wxd-resource", + resource_group_id=config.get("resource-group"), + watson_discovery_name = "my-wxd", +) + +# Show outputs +pulumi.export("account_id", wxd.account_id) +pulumi.export("wxd_id", wxd.id) +pulumi.export("crn", wxd.crn) +pulumi.export("plan_id", wxd.plan_id) +pulumi.export("dashboard_url", wxd.dashboard_url) + + ```
From d9ab8f213b7c1777a986af25e100a0ba086af844 Mon Sep 17 00:00:00 2001 From: Prateek-Sharma13 Date: Thu, 23 Oct 2025 12:04:47 +0530 Subject: [PATCH 7/9] minor formatting changes --- docs/pulumi.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/pulumi.md b/docs/pulumi.md index a6d8142..702eb1b 100644 --- a/docs/pulumi.md +++ b/docs/pulumi.md @@ -269,6 +269,8 @@ pulumi.export("cos_instance_id", cos_instance.id) +
+
Example 2: VPC with Subnets and Security Groups
@@ -352,6 +354,8 @@ pulumi.export("security_group_id", security_group.id)
+
+
Example 3: Red Hat OpenShift Cluster with a default worker pool with one worker node.
@@ -422,6 +426,8 @@ pulumi.export("ocp_cluster_id", cluster.id)
+
+
Example 4: Use existing Terraform IBM Modules (TIM) to create Watson Discovery instance
From c8426769f27eff60b98b2b3e5104ac5b1520cc5c Mon Sep 17 00:00:00 2001 From: Prateek-Sharma13 Date: Tue, 11 Nov 2025 14:48:43 +0530 Subject: [PATCH 8/9] doc: added cos module example --- docs/pulumi.md | 177 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 2 deletions(-) diff --git a/docs/pulumi.md b/docs/pulumi.md index 702eb1b..c56c8d1 100644 --- a/docs/pulumi.md +++ b/docs/pulumi.md @@ -1,4 +1,4 @@ -# IBM Cloud and Pulumi +# Use your favorite programming language with Terraform IBM Modules and Pulumi [Pulumi](https://www.pulumi.com/docs/iac/get-started/) is an open-source Infrastructure as Code (IaC) platform that lets you automate, secure, and manage cloud resources, configurations, and secrets — all using real programming languages like Python, Go, .NET, TypeScript, C# etc. @@ -428,6 +428,8 @@ pulumi.export("ocp_cluster_id", cluster.id)
+### Terraform IBM Module examples +
Example 4: Use existing Terraform IBM Modules (TIM) to create Watson Discovery instance
@@ -443,7 +445,7 @@ This will generate a local SDK which can be imported in the Pulumi program. So, the Watson discovery package can be added as: ```sh -pulumi package add terraform-module terraform-ibm-modules/watsonx-discovery/ibm v1.11.1 wx-discovery +pulumi package add terraform-module terraform-ibm-modules/watsonx-discovery/ibm 1.11.1 wx_discovery ``` ```py @@ -467,9 +469,180 @@ pulumi.export("crn", wxd.crn) pulumi.export("plan_id", wxd.plan_id) pulumi.export("dashboard_url", wxd.dashboard_url) +``` + +
+ +
+ Example 5: Create Object Storage instance and buckets +
+ +As shown in Example 4, a local SDK will be imported in the Pulumi program. + +So, the cloud object storage module can be used as: + +```sh +pulumi package add terraform-module terraform-ibm-modules/cos/ibm 10.1.14 ibm_cos_module +pulumi package add terraform-module terraform-ibm-modules/resource-group/ibm 1.2.1 ibm_rg_module ``` +```py +"""A Python Pulumi program""" + +import pulumi +import pulumi_ibm_rg_module as rgmod +import pulumi_ibm_cos_module as cosmod +import pulumi_ibm as ibm +import os +import random +import string +import glob +from pulumi_ibm import IamAccessGroup, IamAccessGroupPolicy +from pulumi_ibm import CosBucketWebsiteConfiguration + +REGION = "us-south" +PREFIX = "pulumi2" +API_KEY = os.getenv("IBMCLOUD_API_KEY") + +# Set this to an existing resource group name to use it, or None to create a new one +EXISTING_RESOURCE_GROUP = "Default" # or e.g. "Default" + +# Resource Group +if EXISTING_RESOURCE_GROUP: + rg = rgmod.Module( + "resource_group", + existing_resource_group_name=EXISTING_RESOURCE_GROUP + ) + resource_group_id = None # Will be looked up by name in COS module +else: + rg = rgmod.Module( + "resource_group", + resource_group_name=f"{PREFIX}-resource-group" + ) + resource_group_id = rg.resource_group_id + +# Random 4-character suffix for bucket name +BUCKET_SUFFIX = ''.join(random.choices(string.ascii_lowercase + string.digits, k=4)) +BUCKET_NAME = f"{PREFIX}-web-bucket" +COS_INSTANCE_NAME = f"{PREFIX}-cos" + +# COS Instance and Bucket +cos = cosmod.Module( + "cos_instance_bucket", + resource_group_id=rg.resource_group_id, + region=REGION, + cos_instance_name=COS_INSTANCE_NAME, + bucket_name=BUCKET_NAME, + create_cos_instance=True, + create_cos_bucket=True, + bucket_storage_class="standard", + kms_encryption_enabled=False, + retention_enabled=False, + object_versioning_enabled=False, + archive_days=None, + expire_days=None, + add_bucket_name_suffix=False # We already add our own +) + +# Upload files from static directory +STATIC_DIR = os.path.join(os.path.dirname(__file__), "static") +if os.path.isdir(STATIC_DIR): + files = [os.path.basename(f) for f in glob.glob(os.path.join(STATIC_DIR, "*")) if os.path.isfile(f)] + for fname in files: + ibm.cos_bucket_object.CosBucketObject( + f"file-{fname}", + bucket_crn=cos.bucket_crn.apply(lambda crns: crns[0] if isinstance(crns, list) else crns), + bucket_location=REGION, + content_file=os.path.join(STATIC_DIR, fname), + key=fname + ) +else: + pulumi.log.warn(f"Static directory not found: {STATIC_DIR}") + +# Lookup the 'Public Access' IAM access group +public_access_group = ibm.get_iam_access_group( + access_group_name="Public Access" +) + +# Grant public read access to the bucket +ibm.IamAccessGroupPolicy( + "cos-public-access-policy", + access_group_id=public_access_group.groups[0].id, + roles=["Object Reader"], + resources={ + "service": "cloud-object-storage", + "resource_type": "bucket", + "resource_instance_id": cos.cos_instance_guid.apply(lambda x: x[0] if isinstance(x, list) else x), + "resource": cos.bucket_name.apply(lambda x: x[0] if isinstance(x, list) else x), + } +) + +# Configure the COS bucket for static web hosting +ibm.CosBucketWebsiteConfiguration( + "website-config", + bucket_crn=cos.bucket_crn.apply(lambda x: x[0] if isinstance(x, list) else x), + bucket_location=REGION, + website_configuration={ + "index_document": {"suffix": "index.html"}, + "error_document": {"key": "error.html"}, + } +) + +# Export outputs +pulumi.export("bucket_name", cos.bucket_name) +pulumi.export("cos_instance_name", cos.cos_instance_name) +pulumi.export("website_endpoint", cos.bucket_crn.apply(lambda crn: f"https://{BUCKET_NAME}.s3.{REGION}.cloud-object-storage.appdomain.cloud")) + +``` + +
+ Sharing the `Pulumi.yaml` configuration for this example +
+ +```yaml +name: pulumi-start-ibmcloud +description: A minimal IBM Cloud Python Pulumi program +runtime: + name: python + options: + toolchain: pip + virtualenv: venv +config: + pulumi:tags: + value: + pulumi:template: python +packages: + ibm_cos_module: + source: terraform-module + version: 0.1.8 + parameters: + - terraform-ibm-modules/cos/ibm + - 10.1.14 + - ibm_cos_module + ibm_rg_module: + source: terraform-module + version: 0.1.8 + parameters: + - terraform-ibm-modules/resource-group/ibm + - 1.2.1 + - ibm_rg_module + ibm: + source: terraform-provider + version: 0.12.0 + parameters: + - ibm-cloud/ibm + ibm_cos_replication_module: + source: terraform-module + version: 0.1.8 + parameters: + - terraform-ibm-modules/cos/ibm//examples/replication + - 10.1.14 + - ibm_cos_replication_module +``` + +
+
## Best Practices From df48ee4abd405c749691138a5018f21ac596f67a Mon Sep 17 00:00:00 2001 From: Prateek-Sharma13 Date: Tue, 11 Nov 2025 14:52:31 +0530 Subject: [PATCH 9/9] doc: removed comment --- docs/pulumi.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/pulumi.md b/docs/pulumi.md index c56c8d1..fdcbd28 100644 --- a/docs/pulumi.md +++ b/docs/pulumi.md @@ -488,7 +488,6 @@ pulumi package add terraform-module terraform-ibm-modules/resource-group/ibm 1.2 ``` ```py -"""A Python Pulumi program""" import pulumi import pulumi_ibm_rg_module as rgmod