Custom Extensions Guide
This guide explains how to extend elevai-connect with your own custom resources while maintaining compatibility with core updates.
🎯 Overview
The custom/ directory is specifically designed for your organization-specific extensions. This architecture ensures:
- ✅ Zero conflicts with core infrastructure updates
- ✅ Easy upgrades - pull core updates without breaking your customizations
- ✅ Clean separation - core vs. custom resources remain independent
- ✅ Full access - reference core resources in your custom code
- ✅ Flexibility - extend in any direction your business needs
When to use custom extensions: - Organization-specific integrations - Third-party API connections - Custom Lambda functions - Additional AWS resources - Business logic implementations
🏗️ Architecture
Directory Structure
elevai-connect/
├── core/ # ⚠️ Don't modify (maintained by project)
│ ├── alerting/
│ ├── connect.py
│ ├── dynamodb.py
│ ├── lambda_functions.py
│ ├── s3.py
│ ├── iam.py
│ └── qconnect.py
├── custom/ # ✅ Your extensions go here
│ ├── __init__.py
│ ├── README.md
│ ├── my_feature.py # Your custom modules
│ └── integrations/ # Organize as needed
│ ├── salesforce.py
│ └── zendesk.py
└── __main__.py # Orchestrates core + custom
How It Works
- Core infrastructure deploys first (Connect, S3, IAM, etc.)
- Core resources are passed to your custom modules
- Your custom code references and extends core resources
- Everything deploys together in a single
pulumi up
Core Resources Available
When you create custom resources, you have access to:
core_resources = {
"connect_instance": aws.connect.Instance,
"s3": {
"recordings": aws.s3.Bucket,
"exports": aws.s3.Bucket,
"knowledge_base": aws.s3.Bucket,
},
"dynamodb": {
"contacts": aws.dynamodb.Table,
"agents": aws.dynamodb.Table,
},
"iam": {
"lambda_role": aws.iam.Role,
"connect_role": aws.iam.Role,
},
"sns": {
"error_topic": aws.sns.Topic,
"info_warning_topic": aws.sns.Topic,
},
"qconnect": {
"assistant": aws.connect.Assistant,
"knowledge_base": aws.connect.KnowledgeBase,
},
}
🚀 Getting Started
Step 1: Create Your Custom Module
Create a new file in the custom/ directory:
Step 2: Basic Module Structure
# custom/my_feature.py
import pulumi
import pulumi_aws as aws
from typing import Dict
def create_custom_resources(core_resources: Dict, tags: Dict) -> Dict:
"""
Create custom resources for your organization.
Args:
core_resources: Dictionary of core infrastructure resources
tags: Common tags to apply to all resources
Returns:
Dictionary of created custom resources
"""
# Access core resources
connect_instance = core_resources.get("connect_instance")
recordings_bucket = core_resources["s3"]["recordings"]
# Create your custom resources here
custom_lambda = aws.lambda_.Function(
"my-custom-function",
runtime="python3.11",
handler="index.handler",
role=core_resources["iam"]["lambda_role"].arn,
code=pulumi.AssetArchive({
".": pulumi.FileArchive("./lambda/my-function")
}),
environment={
"variables": {
"CONNECT_INSTANCE_ID": connect_instance.id,
"RECORDINGS_BUCKET": recordings_bucket.id,
}
},
tags=tags,
)
# Return your resources for reference
return {
"custom_lambda": custom_lambda,
}
Step 3: Register Your Module
Edit custom/__init__.py to import your module:
# custom/__init__.py
from .my_feature import create_custom_resources
__all__ = ["create_custom_resources"]
Step 4: Enable in Main
Your custom resources are automatically loaded if the custom/__init__.py exports them. The __main__.py orchestrates everything.
💡 Best Practices
Code Organization
Do: - ✅ Create separate files for different features - ✅ Use meaningful module names - ✅ Return created resources for reference - ✅ Apply tags to all resources
Don't:
- ❌ Modify files in core/ directory
- ❌ Duplicate core functionality
- ❌ Hardcode values (use config)
- ❌ Create circular dependencies
Resource Naming
Use consistent naming conventions:
# Good - descriptive, prefixed
custom_lambda = aws.lambda_.Function(
"salesforce-integration",
name=f"{project}-{stack}-salesforce-sync",
...
)
# Bad - generic, unclear
lambda1 = aws.lambda_.Function(
"fn",
name="function",
...
)
Configuration Management
Store custom settings in your Pulumi config:
# Pulumi.dev.yaml
config:
# Core settings
connect:instanceAlias: my-instance
# Your custom settings
custom:
salesforce:
instanceUrl: https://your-org.salesforce.com
apiVersion: "58.0"
zendesk:
subdomain: your-company
apiToken: encrypted-token
Access in your code:
import pulumi
config = pulumi.Config("custom")
salesforce_url = config.require("salesforce:instanceUrl")
zendesk_subdomain = config.require("zendesk:subdomain")
Error Handling
Always handle potential errors:
def create_custom_resources(core_resources, tags):
# Validate required resources exist
if "connect_instance" not in core_resources:
raise ValueError("Connect instance not found in core resources")
try:
# Create resources
lambda_function = aws.lambda_.Function(...)
except Exception as e:
pulumi.log.error(f"Failed to create custom resources: {e}")
raise
return {"lambda": lambda_function}
📖 Additional Resources
🆘 Support
For help with custom extensions: - 🐛 Open an Issue - 💬 Start a Discussion