1. Docs
  2. Pulumi IDP
  3. Best Practices
  4. Patterns
  5. Validating Component Inputs using Policy functions

IDP Pattern: Validating Component Inputs using Policy functions

    Description

    This pattern involves creating reusable policy functions that can be used both in Pulumi Policy Packs and within component constructors to validate inputs. This ensures consistent validation logic across policy enforcement and component creation.

    When to use this pattern

    • Consistent validation: When you want the same validation logic in policies and components
    • Early error detection: When you want to catch configuration errors before deployment
    • Reusable validation: When validation logic should be shared across multiple components
    • Developer experience: When you want to provide helpful error messages for invalid inputs

    When NOT to use this pattern

    • Simple components: When input validation would be trivial or unnecessary
    • Performance concerns: When validation logic is expensive and slows component creation
    • One-off validation: When validation is only needed in one place

    How to use this pattern

    Create policy functions that can be imported and used by both Policy Packs and component constructors.

    Example

    Define reusable policy functions:

    // policies/database-validation.ts
    export interface DatabaseValidationArgs {
      instanceClass: string;
      storage: number;
      backupRetentionDays: number;
      multiAz: boolean;
      environment: "dev" | "staging" | "prod";
    }
    
    export function validateDatabaseInputs(args: DatabaseValidationArgs): string[] {
      const errors: string[] = [];
    
      // Validate instance class format
      if (!args.instanceClass.startsWith("db.")) {
        errors.push("Instance class must start with 'db.' (e.g., 'db.t3.micro')");
      }
    
      // Validate storage constraints
      if (args.storage < 20 || args.storage > 16384) {
        errors.push("Storage must be between 20 GB and 16,384 GB");
      }
    
      // Validate backup retention based on environment
      const minRetention = args.environment === "prod" ? 7 : 1;
      if (args.backupRetentionDays < minRetention) {
        errors.push(`${args.environment} environment requires backup retention >= ${minRetention} days`);
      }
    
      // Validate multi-AZ requirement for production
      if (args.environment === "prod" && !args.multiAz) {
        errors.push("Production databases must have Multi-AZ enabled");
      }
    
      return errors;
    }
    

    Use the policy function in a Policy Pack:

    // policies/database-policy.ts
    import { PolicyPack, validateResourceOfType } from "@pulumi/policy";
    import { validateDatabaseInputs } from "./database-validation";
    import { aws } from "@pulumi/aws";
    
    new PolicyPack("database-policies", {
      policies: [{
        name: "database-configuration",
        description: "Validate database configuration meets organizational standards",
        enforcementLevel: "mandatory",
        validateResource: validateResourceOfType(aws.rds.Instance, (instance, args, reportViolation) => {
          const errors = validateDatabaseInputs({
            instanceClass: instance.instanceClass,
            storage: instance.allocatedStorage,
            backupRetentionDays: instance.backupRetentionPeriod || 0,
            multiAz: instance.multiAz || false,
            environment: instance.tags?.Environment || "dev",
          });
    
          errors.forEach(error => reportViolation(error));
        }),
      }],
    });
    

    Use the same policy function in a component:

    // components/database.ts
    import { validateDatabaseInputs } from "../policies/database-validation";
    
    export class Database extends ComponentResource {
      constructor(name: string, args: DatabaseArgs, opts?: ComponentResourceOptions) {
        super("acme:components:Database", name, {}, opts);
    
        // Validate inputs using the same policy function
        const errors = validateDatabaseInputs(args);
        if (errors.length > 0) {
          throw new Error(`Invalid database configuration:\n${errors.join('\n')}`);
        }
    
        // Create database with validated inputs
        const db = new aws.rds.Instance(name, {
          instanceClass: args.instanceClass,
          allocatedStorage: args.storage,
          backupRetentionPeriod: args.backupRetentionDays,
          multiAz: args.multiAz,
          tags: { Environment: args.environment },
        }, { parent: this });
      }
    }
    

    This ensures consistent validation whether the database is created directly or through a component.

      IDP Builder Course. Register Now.