Infraestructura como Código con Pulumi

Ejemplos modernos de infraestructura como código usando Pulumi con TypeScript, Python y Go

💻 Pulumi TypeScript AWS typescript

🟢 simple ⭐⭐

Despliegue de infraestructura AWS usando Pulumi con TypeScript

⏱️ 15 min 🏷️ pulumi, typescript, aws, serverless, iac
Prerequisites: Node.js, AWS account, Pulumi CLI
// Pulumi TypeScript for AWS
import * as pulumi from '@pulumi/pulumi';
import * as aws from '@pulumi/aws';

// Get AWS provider configuration
const config = new pulumi.Config();
const stackName = pulumi.getStack();
const environment = stackName === 'prod' ? 'production' : 'development';

// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket('my-app-bucket', {
    bucket: pulumi.interpolate`my-app-bucket-${stackName}-${Math.floor(Math.random() * 1000000)}`,
    acl: 'private',
    versioning: {
        enabled: true,
    },
    tags: {
        Name: pulumi.interpolate`my-app-bucket-${stackName}`,
        Environment: environment,
        Project: 'pulumi-demo',
        ManagedBy: 'pulumi'
    },
    website: {
        indexDocument: 'index.html',
        errorDocument: 'error.html'
    },
    forceDestroy: true
});

// Create IAM role for Lambda
const lambdaRole = new aws.iam.Role('app-lambda-role', {
    assumeRolePolicy: JSON.stringify({
        Version: '2012-10-17',
        Statement: [{
            Action: 'sts:AssumeRole',
            Effect: 'Allow',
            Principal: {
                Service: 'lambda.amazonaws.com'
            }
        }]
    }),
    tags: {
        Name: pulumi.interpolate`app-lambda-role-${stackName}`,
        Environment: environment
    }
});

// Attach basic execution policy
new aws.iam.RolePolicyAttachment('app-lambda-basic-execution', {
    role: lambdaRole.name,
    policyArn: aws.iam.ManagedPolicies.AWSLambdaBasicExecutionRole,
});

// Create Lambda function
const lambdaFunction = new aws.lambda.Function('app-api-function', {
    name: pulumi.interpolate`app-api-${stackName}`,
    runtime: 'nodejs18.x',
    handler: 'index.handler',
    role: lambdaRole.arn,
    s3Bucket: bucket.id,
    s3Key: 'api/function.zip',
    timeout: 30,
    memorySize: 256,
    environment: {
        variables: {
            NODE_ENV: environment,
            BUCKET_NAME: bucket.id
        }
    },
    tags: {
        Name: pulumi.interpolate`app-api-${stackName}`,
        Environment: environment
    }
});

// Create API Gateway
const api = new aws.apigateway.RestApi('app-api', {
    name: pulumi.interpolate`app-api-${stackName}`,
    description: pulumi.interpolate`API Gateway for Pulumi demo in ${environment}`,
    endpointConfiguration: {
        types: 'REGIONAL',
    }
});

// Create API resource
const apiResource = new aws.apigateway.Resource('app-api-resource', {
    restApi: api.id,
    parentId: api.rootResourceId,
    pathPart: '{proxy+}'
});

// Create API method
const apiMethod = new aws.apigateway.Method('app-api-method', {
    restApi: api.id,
    resourceId: apiResource.id,
    httpMethod: 'ANY',
    authorization: 'NONE'
});

// Create API integration
const apiIntegration = new aws.apigateway.Integration('app-api-integration', {
    restApi: api.id,
    resourceId: apiResource.id,
    httpMethod: apiMethod.httpMethod,
    integrationHttpMethod: 'POST',
    type: 'AWS_PROXY',
    uri: lambdaFunction.invokeArn
});

// Grant API Gateway permission to invoke Lambda
const lambdaPermission = new aws.lambda.Permission('app-api-lambda-permission', {
    statementId: 'AllowAPIGatewayInvoke',
    action: 'lambda:InvokeFunction',
    function: lambdaFunction.name,
    principal: 'apigateway.amazonaws.com',
    sourceArn: pulumi.interpolate`arn:aws:execute-api:${aws.getRegion()}:${aws.getAccountId()}:${api.id}/*/*`
});

// Create CloudWatch Log Group for Lambda
const logGroup = new aws.cloudwatch.LogGroup('app-lambda-logs', {
    name: pulumi.interpolate`/aws/lambda/${lambdaFunction.name}`,
    retentionInDays: 14,
    tags: {
        Name: pulumi.interpolate`${lambdaFunction.name}-logs`,
        Environment: environment
    }
});

// Output values
export const bucketName = bucket.id;
export const bucketArn = bucket.arn;
export const apiEndpoint = pulumi.interpolate`https://${api.id}.execute-api.${aws.getRegion()}.amazonaws.com/prod`;
export const lambdaFunctionName = lambdaFunction.name;
export const logGroupName = logGroup.name;

// Create stack outputs
new pulumi.Output('bucket_name', bucketName);
new pulumi.Output('api_endpoint', apiEndpoint);
new pulumi.Output('lambda_function_name', lambdaFunctionName);

// Stack exports for cross-stack references
if (environment === 'production') {
    bucket.id.apply(id => pulumi.stackReference('prod', id));
    api.id.apply(id => pulumi.stackReference('prod-api', id));
}

💻 Pulumi Python Google Cloud python

🟡 intermediate ⭐⭐⭐

Despliegue de infraestructura GCP usando Pulumi con Python

⏱️ 25 min 🏷️ pulumi, python, gcp, cloud run, cloud sql
Prerequisites: Python, GCP project, Pulumi CLI
# Pulumi Python for Google Cloud Platform
import pulumi
import pulumi_gcp as gcp

# Get configuration
config = pulumi.Config()
stack_name = pulumi.get_stack()
environment = 'production' if stack_name == 'prod' else 'development'
project_id = config.require('project_id')
region = config.get('region', 'us-central1')

# Create GCP provider
gcp_provider = gcp.provider.Provider('gcp',
    project=project_id,
    region=region
)

# Create VPC network
vpc_network = gcp.compute.Network('app-vpc',
    name=f'app-vpc-{stack_name}',
    auto_create_subnetworks=False,
    description=f'VPC for application in {environment}',
    routing_mode='REGIONAL',
    opts=pulumi.ResourceOptions(provider=gcp_provider)
)

# Create subnets
public_subnet = gcp.compute.Subnetwork('app-public-subnet',
    name=f'app-public-subnet-{stack_name}',
    ip_cidr_range='10.0.1.0/24',
    region=region,
    network=vpc_network.id,
    private_ip_google_access=False,
    opts=pulumi.ResourceOptions(provider=gcp_provider)
)

private_subnet = gcp.compute.Subnetwork('app-private-subnet',
    name=f'app-private-subnet-{stack_name}',
    ip_cidr_range='10.0.2.0/24',
    region=region,
    network=vpc_network.id,
    private_ip_google_access=True,
    opts=pulumi.ResourceOptions(provider=gcp_provider)
)

# Create Cloud Storage bucket
bucket_suffix = pulumi.random.RandomString('bucket-suffix',
    length=8,
    special=False,
    upper=False
).result

storage_bucket = gcp.storage.Bucket('app-storage-bucket',
    name=bucket_suffix.apply(lambda suffix: f'app-storage-bucket-{stack_name}-{suffix}'),
    location=region,
    storage_class='STANDARD',
    uniform_bucket_level_access=True,
    versioning= {
        'enabled': True
    },
    labels={
        'environment': environment,
        'managed-by': 'pulumi'
    },
    opts=pulumi.ResourceOptions(provider=gcp_provider)
)

# Create Cloud SQL instance
sql_instance = gcp.sql.DatabaseInstance('app-sql-instance',
    name=f'app-sql-instance-{stack_name}',
    database_version='POSTGRES_14',
    region=region,
    settings={
        'tier': 'db-g1-small',
        'disk_size': 20,
        'disk_type': 'PD_SSD',
        'ip_configuration': {
            'ipv4_enabled': False,
            'private_network': vpc_network.id,
            'authorized_networks': []
        },
        'backup_configuration': {
            'enabled': True,
            'binary_log_enabled': True
        }
    },
    deletion_protection=False,
    opts=pulumi.ResourceOptions(provider=gcp_provider)
)

# Create SQL database
sql_database = gcp.sql.Database('app-sql-database',
    name='app_database',
    instance=sql_instance.name,
    opts=pulumi.ResourceOptions(provider=gcp_provider)
)

# Create Cloud Run service
cloud_run_service = gcp.cloudrun.Service('app-cloudrun-service',
    name=f'app-service-{stack_name}',
    location=region,
    template={
        'spec': {
            'containers': [{
                'image': 'gcr.io/cloudrun/hello:latest',
                'ports': [{
                    'name': 'http1',
                    'containerPort': 8080
                }],
                'envs': [
                    {
                        'name': 'DATABASE_HOST',
                        'value': sql_instance.private_ip_address.apply(lambda ip: f'{ip}:5432')
                    },
                    {
                        'name': 'DATABASE_NAME',
                        'value': sql_database.name
                    },
                    {
                        'name': 'STORAGE_BUCKET',
                        'value': storage_bucket.name
                    },
                    {
                        'name': 'ENVIRONMENT',
                        'value': environment
                    }
                ]
            }],
            'container_concurrency': 80,
            'timeout_seconds': 30
        }
    },
    traffic=[{
        'percent': 100,
        'latest_revision': True
    }],
    opts=pulumi.ResourceOptions(provider=gcp_provider)
)

# Create IAM policy for Cloud Run
cloud_run_iam = gcp.cloudrun.IamPolicy('app-cloudrun-iam',
    location=region,
    project=project_id,
    bindings=[{
        'role': 'roles/run.invoker',
        'members': ['allUsers']
    }],
    cloudrun=cloud_run_service.name,
    opts=pulumi.ResourceOptions(provider=gcp_provider)
)

# Create Cloud Scheduler job
scheduler_job = gcp.cloudscheduler.Job('app-scheduler-job',
    name=f'app-scheduler-{stack_name}',
    description='Scheduled job for data processing',
    schedule='0 2 * * *',  # Daily at 2 AM
    time_zone='America/Los_Angeles',
    pubsub_target={
        'topic_name': gcp.pubsub.Topic('app-topic',
            name=f'app-topic-{stack_name}',
            opts=pulumi.ResourceOptions(provider=gcp_provider)
        ).id.apply(lambda id: f'projects/{project_id}/topics/{id}'),
        'data': '{"task": "daily_cleanup"}'
    },
    opts=pulumi.ResourceOptions(provider=gcp_provider)
)

# Create Cloud Function for background processing
cloud_function = gcp.cloudfunctions.Function('app-background-function',
    name=f'app-background-func-{stack_name}',
    description='Background processing function',
    runtime='python39',
    available_memory_mb=256,
    source_archive_bucket=storage_bucket.name,
    source_archive_object=gcp.storage.BucketObject('function-source',
        name='background-function.zip',
        bucket=storage_bucket.name,
        source=pulumi.FileArchive('./function-source'),
        opts=pulumi.ResourceOptions(provider=gcp_provider)
    ).name,
    entry_point='handler',
    event_trigger={
        'event_type': 'google.pubsub.topic.publish',
        'resource': scheduler_job.pubsub_target.topic_name
    },
    opts=pulumi.ResourceOptions(provider=gcp_provider)
)

# Export values
pulumi.export('project_id', project_id)
pulumi.export('vpc_network_name', vpc_network.name)
pulumi.export('public_subnet_name', public_subnet.name)
pulumi.export('private_subnet_name', private_subnet.name)
pulumi.export('storage_bucket_name', storage_bucket.name)
pulumi.export('sql_instance_name', sql_instance.name)
pulumi.export('sql_database_name', sql_database.name)
pulumi.export('sql_instance_ip', sql_instance.private_ip_address)
pulumi.export('cloud_run_service_url', cloud_run_service.statuses[0].url)
pulumi.export('scheduler_job_name', scheduler_job.name)
pulumi.export('cloud_function_name', cloud_function.name)

💻 Pulumi Go Kubernetes go

🟡 intermediate ⭐⭐⭐⭐

Despliegue de recursos Kubernetes usando Pulumi con Go

⏱️ 30 min 🏷️ pulumi, go, kubernetes, helm, deployment
Prerequisites: Go programming, Kubernetes, Helm
// Pulumi Go for Kubernetes
package main

import (
	"context"
	"fmt"

	"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes"
	"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apps/v1"
	"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
	"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/networking/v1"
	"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/rbac/v1"
	"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Get configuration
		config := ctx.NewConfig()
		stackName := ctx.Stack()
		environment := "development"
		if stackName == "prod" {
			environment = "production"
		}

		namespaceName := fmt.Sprintf("app-%s", stackName)

		// Create Kubernetes provider
		k8sProvider, err := kubernetes.NewProvider(ctx, "k8s", &kubernetes.ProviderArgs{
			Kubeconfig: config.Get("kubeconfig"),
		})
		if err != nil {
			return err
		}

		// Create namespace
		namespace, err := v1.NewNamespace(ctx, "app-namespace", &v1.NamespaceArgs{
			Metadata: &v1.ObjectMetaArgs{
				Name: pulumi.String(namespaceName),
				Labels: pulumi.StringMap{
					"name":       pulumi.String(namespaceName),
					"environment": pulumi.String(environment),
					"managed-by": pulumi.String("pulumi"),
				},
			},
		}, pulumi.Provider(k8sProvider))
		if err != nil {
			return err
		}

		// Create ConfigMap for application configuration
		appConfigMap, err := v1.NewConfigMap(ctx, "app-config", &v1.ConfigMapArgs{
			Metadata: &v1.ObjectMetaArgs{
				Name:      pulumi.String("app-config"),
				Namespace: namespace.Name,
			},
			Data: pulumi.StringMap{
				"API_URL":         pulumi.String("https://api.example.com"),
				"LOG_LEVEL":       pulumi.String("info"),
				"REDIS_HOST":      pulumi.String("redis-service"),
				"REDIS_PORT":      pulumi.String("6379"),
				"DB_HOST":         pulumi.String("postgres-service"),
				"DB_PORT":         pulumi.String("5432"),
				"ENVIRONMENT":     pulumi.String(environment),
			},
		}, pulumi.Provider(k8sProvider))
		if err != nil {
			return err
		}

		// Create Secret for sensitive data
		appSecret, err := v1.NewSecret(ctx, "app-secret", &v1.SecretArgs{
			Metadata: &v1.ObjectMetaArgs{
				Name:      pulumi.String("app-secret"),
				Namespace: namespace.Name,
			},
			StringData: pulumi.StringMap{
				"DB_PASSWORD":   pulumi.String("changeme123!"),
				"API_KEY":       pulumi.String("secret-api-key"),
				"JWT_SECRET":    pulumi.String("jwt-secret-key"),
			},
		}, pulumi.Provider(k8sProvider))
		if err != nil {
			return err
		}

		// Create Redis deployment
		redisDeployment, err := v1.NewDeployment(ctx, "redis-deployment", &v1.DeploymentArgs{
			Metadata: &v1.ObjectMetaArgs{
				Name:      pulumi.String("redis"),
				Namespace: namespace.Name,
				Labels: pulumi.StringMap{
					"app":  pulumi.String("redis"),
					"tier": pulumi.String("cache"),
				},
			},
			Spec: &v1.DeploymentSpecArgs{
				Replicas: pulumi.Int(1),
				Selector: &v1.LabelSelectorArgs{
					MatchLabels: pulumi.StringMap{
						"app": pulumi.String("redis"),
					},
				},
				Template: &v1.PodTemplateSpecArgs{
					Metadata: &v1.ObjectMetaArgs{
						Labels: pulumi.StringMap{
							"app": pulumi.String("redis"),
						},
					},
					Spec: &v1.PodSpecArgs{
						Containers: v1.ContainerArray{
							&v1.ContainerArgs{
								Name:  pulumi.String("redis"),
								Image: pulumi.String("redis:7-alpine"),
								Ports: v1.ContainerPortArray{
									&v1.ContainerPortArgs{
										ContainerPort: pulumi.Int(6379),
									},
								},
								Resources: &v1.ResourceRequirementsArgs{
									Requests: pulumi.StringMap{
										"memory": pulumi.String("64Mi"),
										"cpu":    pulumi.String("250m"),
									},
									Limits: pulumi.StringMap{
										"memory": pulumi.String("128Mi"),
										"cpu":    pulumi.String("500m"),
									},
								},
							},
						},
					},
				},
			},
		}, pulumi.Provider(k8sProvider))
		if err != nil {
			return err
		}

		// Create Redis service
		redisService, err := v1.NewService(ctx, "redis-service", &v1.ServiceArgs{
			Metadata: &v1.ObjectMetaArgs{
				Name:      pulumi.String("redis-service"),
				Namespace: namespace.Name,
			},
			Spec: &v1.ServiceSpecArgs{
				Selector: pulumi.StringMap{
					"app": pulumi.String("redis"),
				},
				Ports: v1.ServicePortArray{
					&v1.ServicePortArgs{
						Port:       pulumi.Int(6379),
						TargetPort: pulumi.Int(6379),
					},
				},
			},
		}, pulumi.Provider(k8sProvider))
		if err != nil {
			return err
		}

		// Create application deployment
		appDeployment, err := v1.NewDeployment(ctx, "app-deployment", &v1.DeploymentArgs{
			Metadata: &v1.ObjectMetaArgs{
				Name:      pulumi.String("app"),
				Namespace: namespace.Name,
				Labels: pulumi.StringMap{
					"app":  pulumi.String("my-app"),
					"tier": pulumi.String("frontend"),
				},
			},
			Spec: &v1.DeploymentSpecArgs{
				Replicas: pulumi.IntPtr(3),
				Selector: &v1.LabelSelectorArgs{
					MatchLabels: pulumi.StringMap{
						"app": pulumi.String("my-app"),
					},
				},
				Template: &v1.PodTemplateSpecArgs{
					Metadata: &v1.ObjectMetaArgs{
						Labels: pulumi.StringMap{
							"app": pulumi.String("my-app"),
						},
					},
					Spec: &v1.PodSpecArgs{
						Containers: v1.ContainerArray{
							&v1.ContainerArgs{
								Name:  pulumi.String("app"),
								Image: pulumi.String("nginx:alpine"),
								Ports: v1.ContainerPortArray{
									&v1.ContainerPortArgs{
										ContainerPort: pulumi.Int(80),
									},
								},
								Env: v1.EnvVarArray{
									&v1.EnvVarArgs{
										Name:  pulumi.String("ENVIRONMENT"),
										Value: pulumi.String(environment),
									},
								},
								Resources: &v1.ResourceRequirementsArgs{
									Requests: pulumi.StringMap{
										"memory": pulumi.String("64Mi"),
										"cpu":    pulumi.String("250m"),
									},
									Limits: pulumi.StringMap{
										"memory": pulumi.String("128Mi"),
										"cpu":    pulumi.String("500m"),
									},
								},
								ReadinessProbe: &v1.ProbeArgs{
									HttpGet: &v1.HTTPGetActionArgs{
										Path: pulumi.String("/"),
										Port: pulumi.Int(80),
									},
									InitialDelaySeconds: pulumi.Int(5),
									PeriodSeconds:       pulumi.Int(10),
								},
							},
						},
					},
				},
			},
		}, pulumi.Provider(k8sProvider))
		if err != nil {
			return err
		}

		// Create application service
		appService, err := v1.NewService(ctx, "app-service", &v1.ServiceArgs{
			Metadata: &v1.ObjectMetaArgs{
				Name:      pulumi.String("app-service"),
				Namespace: namespace.Name,
			},
			Spec: &v1.ServiceSpecArgs{
				Type:     pulumi.String("LoadBalancer"),
				Selector: pulumi.StringMap{
					"app": pulumi.String("my-app"),
				},
				Ports: v1.ServicePortArray{
					&v1.ServicePortArgs{
						Port:       pulumi.Int(80),
						TargetPort: pulumi.Int(80),
					},
				},
			},
		}, pulumi.Provider(k8sProvider))
		if err != nil {
			return err
		}

		// Create Ingress for HTTP routing
		appIngress, err := networkingv1.NewIngress(ctx, "app-ingress", &networkingv1.IngressArgs{
			Metadata: &v1.ObjectMetaArgs{
				Name:      pulumi.String("app-ingress"),
				Namespace: namespace.Name,
				Annotations: pulumi.StringMap{
					"kubernetes.io/ingress.class": pulumi.String("nginx"),
					"cert-manager.io/cluster-issuer": pulumi.String("letsencrypt-prod"),
				},
			},
			Spec: &networkingv1.IngressSpecArgs{
				Tls: networkingv1.IngressTLSArray{
					&networkingv1.IngressTLSArgs{
						Hosts: pulumi.StringArray{
							pulumi.String("app.example.com"),
						},
						SecretName: pulumi.String("app-tls"),
					},
				},
				Rules: networkingv1.IngressRuleArray{
					&networkingv1.IngressRuleArgs{
						Host: pulumi.String("app.example.com"),
						Http: &networkingv1.HTTPIngressRuleValueArgs{
							Paths: networkingv1.HTTPIngressPathArray{
								&networkingv1.HTTPIngressPathArgs{
									Path:     pulumi.String("/"),
									PathType: pulumi.String("Prefix"),
									Backend: &networkingv1.IngressBackendArgs{
										Service: &networkingv1.IngressServiceBackendArgs{
											Name: appService.Name,
											Port: &networkingv1.ServiceBackendPortArgs{
												Number: pulumi.Int(80),
											},
										},
									},
								},
							},
						},
					},
				},
			},
		}, pulumi.Provider(k8sProvider))
		if err != nil {
			return err
		}

		// Deploy Helm chart for monitoring (Prometheus)
		prometheusChart, err := helmv3.NewRelease(ctx, "prometheus", &helmv3.ReleaseArgs{
			Chart:     pulumi.String("prometheus"),
			Version:   pulumi.String("25.8.0"),
			Namespace: namespace.Name,
			RepositoryOpts: &helmv3.RepositoryOptsArgs{
				Repo: pulumi.String("https://prometheus-community.github.io/helm-charts"),
			},
			Values: pulumi.Map{
				"server": pulumi.Map{
					"persistentVolume": pulumi.Map{
						"enabled": pulumi.Bool(false),
					},
				},
				"alertmanager": pulumi.Map{
					"enabled": pulumi.Bool(false),
				},
			},
		}, pulumi.Provider(k8sProvider))
		if err != nil {
			return err
		}

		// Export outputs
		ctx.Export("namespace_name", namespace.Name)
		ctx.Export("redis_deployment_name", redisDeployment.Name)
		ctx.Export("redis_service_name", redisService.Name)
		ctx.Export("app_deployment_name", appDeployment.Name)
		ctx.Export("app_service_name", appService.Name)
		ctx.Export("app_load_balancer_ip", appService.Status.Apply(func(status *v1.ServiceStatus) *string {
			if status.LoadBalancer != nil && len(status.LoadBalancer.Ingress) > 0 {
				return status.LoadBalancer.Ingress[0].Ip
			}
			return pulumi.String("")
		}))
		ctx.Export("ingress_host", appIngress.Spec.Apply(func(spec *networkingv1.IngressSpec) string {
			if len(spec.Rules) > 0 {
				return *spec.Rules[0].Host
			}
			return ""
		}))
		ctx.Export("prometheus_release_name", prometheusChart.Name)

		return nil
	})
}

💻 Infraestructura multi-nube con Pulumi typescript

🔴 complex ⭐⭐⭐⭐⭐

Despliegue de infraestructura a través de múltiples proveedores de nube usando Pulumi

⏱️ 45 min 🏷️ pulumi, multi-cloud, aws, azure, gcp, hybrid
Prerequisites: Advanced Pulumi, Multiple cloud accounts, Cross-cloud networking
// Multi-Cloud Infrastructure with Pulumi
import * as pulumi from '@pulumi/pulumi';
import * as aws from '@pulumi/aws';
import * as azure from '@pulumi/azure-native';
import * as gcp from '@pulumi/gcp';

// Configuration
const config = new pulumi.Config();
const stackName = pulumi.getStack();
const environment = stackName === 'prod' ? 'production' : 'development';

// AWS Configuration
const awsRegion = config.get('awsRegion', 'us-west-2');
const awsProvider = new aws.Provider('aws', {
    region: awsRegion,
});

// Azure Configuration
const azureLocation = config.get('azureLocation', 'East US');
const azureResourceGroupName = pulumi.interpolate `rg-pulumi-multi-${stackName}`;
const azureResourceGroup = new azure.resources.ResourceGroup('azure-rg', {
    resourceGroupName: azureResourceGroupName,
    location: azureLocation,
    tags: {
        environment,
        managedBy: 'pulumi'
    }
});

// GCP Configuration
const gcpConfig = new gcp.Provider('gcp', {
    project: config.require('gcpProjectId'),
    region: config.get('gcpRegion', 'us-central1'),
});

// AWS Infrastructure
const awsVpc = new aws.ec2.Vpc('aws-vpc', {
    cidrBlock: '10.0.0.0/16',
    enableDnsHostnames: true,
    enableDnsSupport: true,
    tags: {
        Name: pulumi.interpolate `pulumi-multi-${stackName}`,
        Environment: environment
    }
}, { provider: awsProvider });

const awsSubnet = new aws.ec2.Subnet('aws-subnet', {
    vpcId: awsVpc.id,
    cidrBlock: '10.0.1.0/24',
    availabilityZone: pulumi.interpolate `${awsRegion}a`,
    mapPublicIpOnLaunch: true,
    tags: {
        Name: pulumi.interpolate `pulumi-multi-${stackName}-subnet`,
        Environment: environment
    }
}, { provider: awsProvider });

const awsSecurityGroup = new aws.ec2.SecurityGroup('aws-sg', {
    vpcId: awsVpc.id,
    ingress: [
        {
            protocol: 'tcp',
            fromPort: 22,
            toPort: 22,
            cidrBlocks: ['0.0.0.0/0'],
            description: 'SSH'
        },
        {
            protocol: 'tcp',
            fromPort: 80,
            toPort: 80,
            cidrBlocks: ['0.0.0.0/0'],
            description: 'HTTP'
        }
    ],
    egress: [{
        protocol: '-1',
        fromPort: 0,
        toPort: 0,
        cidrBlocks: ['0.0.0.0/0']
    }],
    tags: {
        Name: pulumi.interpolate `pulumi-multi-${stackName}-sg`,
        Environment: environment
    }
}, { provider: awsProvider });

// AWS Lambda function
const awsLambda = new aws.lambda.Function('aws-lambda', {
    name: pulumi.interpolate `pulumi-multi-${stackName}-function`,
    runtime: 'nodejs18.x',
    handler: 'index.handler',
    role: aws.iam.getRoleDocument('lambda-role-doc', {
        assumeRolePolicy: JSON.stringify({
            Version: '2012-10-17',
            Statement: [{
                Action: 'sts:AssumeRole',
                Effect: 'Allow',
                Principal: { Service: 'lambda.amazonaws.com' }
            }]
        })
    }).then(doc => new aws.iam.Role('aws-lambda-role', {
        assumeRolePolicy: doc.json,
        name: pulumi.interpolate `pulumi-multi-${stackName}-lambda-role`
    }, { provider: awsProvider }).arn),
    code: new pulumi.asset.AssetArchive({
        'index.js': new pulumi.asset.StringAsset(`
            exports.handler = async (event) => {
                return {
                    statusCode: 200,
                    body: JSON.stringify({
                        message: 'Hello from AWS Lambda!',
                        cloud: 'AWS',
                        region: process.env.AWS_REGION,
                        timestamp: new Date().toISOString()
                    })
                };
            };
        `)
    }),
    environment: {
        variables: {
            ENVIRONMENT: environment
        }
    },
    tags: {
        Name: pulumi.interpolate `pulumi-multi-${stackName}-lambda`,
        Environment: environment
    }
}, { provider: awsProvider });

// Azure Infrastructure
const azureVirtualNetwork = new azure.network.VirtualNetwork('azure-vnet', {
    virtualNetworkName: pulumi.interpolate `pulumi-multi-${stackName}-vnet`,
    resourceGroupName: azureResourceGroup.name,
    location: azureLocation,
    addressSpace: {
        addressPrefixes: ['10.1.0.0/16']
    },
    tags: {
        environment,
        managedBy: 'pulumi'
    }
});

const azureSubnet = new azure.network.Subnet('azure-subnet', {
    subnetName: pulumi.interpolate `pulumi-multi-${stackName}-subnet`,
    resourceGroupName: azureResourceGroup.name,
    virtualNetworkName: azureVirtualNetwork.name,
    addressPrefix: '10.1.1.0/24',
    privateEndpointNetworkPolicies: 'Disabled',
    privateLinkServiceNetworkPolicies: 'Enabled'
});

const azureNetworkSecurityGroup = new azure.network.NetworkSecurityGroup('azure-nsg', {
    networkSecurityGroupName: pulumi.interpolate `pulumi-multi-${stackName}-nsg`,
    resourceGroupName: azureResourceGroup.name,
    location: azureLocation,
    securityRules: [
        {
            name: 'AllowSSH',
            priority: 1000,
            direction: 'Inbound',
            access: 'Allow',
            protocol: 'Tcp',
            sourcePortRange: '*',
            destinationPortRange: '22',
            sourceAddressPrefix: '*',
            destinationAddressPrefix: '*'
        },
        {
            name: 'AllowHTTP',
            priority: 1001,
            direction: 'Inbound',
            access: 'Allow',
            protocol: 'Tcp',
            sourcePortRange: '*',
            destinationPortRange: '80',
            sourceAddressPrefix: '*',
            destinationAddressPrefix: '*'
        }
    ]
});

// Azure Function App
const azureStorageAccount = new azure.storage.StorageAccount('azure-storage', {
    accountName: pulumi.random.RandomString('storage-suffix', {
        length: 8,
        special: false,
        upper: false
    }).result.apply(suffix => `pulumi${suffix}`),
    resourceGroupName: azureResourceGroup.name,
    location: azureLocation,
    kind: 'StorageV2',
    sku: {
        name: azure.storage.SkuName.Standard_LRS
    },
    tags: {
        environment,
        managedBy: 'pulumi'
    }
});

const azureFunctionApp = new azure.web.WebApp('azure-function', {
    name: pulumi.interpolate `pulumi-multi-${stackName}-function`,
    resourceGroupName: azureResourceGroup.name,
    location: azureLocation,
    kind: 'functionapp',
    serverFarmId: new azure.web.AppServicePlan('azure-app-service-plan', {
        name: pulumi.interpolate `pulumi-multi-${stackName}-asp`,
        resourceGroupName: azureResourceGroup.name,
        location: azureLocation,
        sku: {
            name: azure.web.SkuName.Basic,
            tier: azure.web.SkuTier.Basic,
            size: 'B1'
        },
        kind: 'linux',
        reserved: true
    }).id,
    siteConfig: {
        linuxFxVersion: 'NODE|18-lts',
        appSettings: [
            {
                name: 'WEBSITE_RUN_FROM_PACKAGE',
                value: '1'
            },
            {
                name: 'FUNCTIONS_WORKER_RUNTIME',
                value: 'node'
            },
            {
                name: 'ENVIRONMENT',
                value: environment
            },
            {
                name: 'AzureWebJobsStorage',
                value: azureStorageAccount.primaryConnectionString
            }
        ]
    },
    tags: {
        environment,
        managedBy: 'pulumi'
    }
});

// GCP Infrastructure
const gcpVpcNetwork = new gcp.compute.Network('gcp-vpc', {
    name: pulumi.interpolate `pulumi-multi-${stackName}-vnet`,
    autoCreateSubnetworks: false,
    description: pulumi.interpolate `VPC for Pulumi multi-cloud demo in ${environment}`,
    routingMode: 'REGIONAL',
    tags: {
        environment,
        managedBy: 'pulumi'
    }
}, { provider: gcpConfig });

const gcpSubnet = new gcp.compute.Subnetwork('gcp-subnet', {
    name: pulumi.interpolate `pulumi-multi-${stackName}-subnet`,
    ipCidrRange: '10.2.1.0/24',
    region: config.get('gcpRegion', 'us-central1'),
    network: gcpVpcNetwork.id,
    privateIpGoogleAccess: false,
    tags: {
        environment,
        managedBy: 'pulumi'
    }
}, { provider: gcpConfig });

const gcpFirewall = new gcp.compute.Firewall('gcp-firewall', {
    name: pulumi.interpolate `pulumi-multi-${stackName}-fw`,
    network: gcpVpcNetwork.id,
    allow: [{
        protocol: 'tcp',
        ports: ['22', '80']
    }],
    sourceRanges: ['0.0.0.0/0'],
    description: 'Allow SSH and HTTP traffic',
    tags: {
        environment,
        managedBy: 'pulumi'
    }
}, { provider: gcpConfig });

// GCP Cloud Function
const gcpBucketSuffix = pulumi.random.RandomString('gcp-bucket-suffix', {
    length: 8,
    special: false,
    upper: false
}).result;

const gcpBucket = new gcp.storage.Bucket('gcp-function-bucket', {
    name: gcpBucketSuffix.apply(suffix => `pulumi-multi-${stackName}-bucket-${suffix}`),
    location: config.get('gcpRegion', 'US-CENTRAL1'),
    forceDestroy: true,
    uniformBucketLevelAccess: true,
    tags: {
        environment,
        managedBy: 'pulumi'
    }
}, { provider: gcpConfig });

const gcpFunctionArchive = new gcp.storage.BucketObject('gcp-function-zip', {
    name: 'function.zip',
    bucket: gcpBucket.name,
    source: new pulumi.asset.AssetArchive({
        'index.js': new pulumi.asset.StringAsset(`
            exports.handler = async (req, res) => {
                res.status(200).json({
                    message: 'Hello from GCP Cloud Function!',
                    cloud: 'GCP',
                    region: process.env.FUNCTION_REGION,
                    timestamp: new Date().toISOString()
                });
            };
        `)
    })
}, { provider: gcpConfig });

const gcpFunction = new gcp.cloudfunctions.Function('gcp-function', {
    name: pulumi.interpolate `pulumi-multi-${stackName}-function`,
    description: pulumi.interpolate `Pulumi multi-cloud demo function in ${environment}`,
    runtime: 'nodejs18',
    entryPoint: 'handler',
    sourceArchiveBucket: gcpBucket.name,
    sourceArchiveObject: gcpFunctionArchive.name,
    triggerHttp: true,
    availableMemoryMb: 256,
    timeout: 60,
    environmentVariables: {
        ENVIRONMENT: environment
    },
    tags: {
        environment,
        managedBy: 'pulumi'
    }
}, { provider: gcpConfig });

// Enable Cloud Function invocations
const gcpFunctionIAM = new gcp.cloudfunctions.FunctionIamPolicy('gcp-function-iam', {
    region: gcpFunction.region,
    cloudFunction: gcpFunction.name,
    bindings: [{
        role: 'roles/cloudfunctions.invoker',
        members: ['allUsers']
    }]
}, { provider: gcpConfig });

// Cross-Cloud Networking (VPN connections would go here)
// This is a simplified example - in production you'd set up VPN tunnels or Direct Connect

// Monitoring Integration (using a centralized monitoring solution)
const monitoringConfig = {
    aws: {
        lambdaName: awsLambda.name,
        region: awsRegion
    },
    azure: {
        functionName: azureFunctionApp.name,
        resourceGroup: azureResourceGroup.name
    },
    gcp: {
        functionName: gcpFunction.name,
        region: config.get('gcpRegion', 'us-central1')
    }
};

// Export outputs
export const awsInfrastructure = {
    vpcId: awsVpc.id,
    subnetId: awsSubnet.id,
    lambdaFunctionName: awsLambda.name,
    lambdaArn: awsLambda.arn
};

export const azureInfrastructure = {
    resourceGroupName: azureResourceGroup.name,
    vnetName: azureVirtualNetwork.name,
    subnetName: azureSubnet.name,
    functionName: azureFunctionApp.name,
    functionUrl: azureFunctionApp.defaultHostName
};

export const gcpInfrastructure = {
    vpcName: gcpVpcNetwork.name,
    subnetName: gcpSubnet.name,
    functionName: gcpFunction.name,
    functionUrl: gcpFunction.httpsTriggerUrl
};

export const monitoringSetup = monitoringConfig;

// Additional exports for easy access
pulumi.export('aws_lambda_name', awsLambda.name);
pulumi.export('azure_function_name', azureFunctionApp.name);
pulumi.export('gcp_function_name', gcpFunction.name);
pulumi.export('gcp_function_url', gcpFunction.httpsTriggerUrl);

// Stack outputs for monitoring dashboard
pulumi.export('dashboard_config', pulumi.all([
    awsLambda.name,
    azureFunctionApp.name,
    gcpFunction.name
]).apply(([awsName, azureName, gcpName]) => JSON.stringify({
    environment,
    services: {
        aws: awsName,
        azure: azureName,
        gcp: gcpName
    },
    monitoring: {
        enabled: true,
        providers: ['cloudwatch', 'azure-monitor', 'cloud-monitoring']
    }
})));