"""Provider block generator for Terraform HCL output. Generates a providers.tf file containing: - A terraform { required_providers { ... } } block listing all providers used - Individual provider configuration blocks with platform-specific settings (endpoints, certificates, credentials) for each distinct provider type. """ from __future__ import annotations from iac_reverse.models import ProviderType, ScanProfile, GeneratedFile # --------------------------------------------------------------------------- # Provider metadata: maps ProviderType to Terraform provider details # --------------------------------------------------------------------------- # Each entry: (terraform_provider_name, source, version_constraint) _PROVIDER_METADATA: dict[ProviderType, tuple[str, str, str]] = { ProviderType.KUBERNETES: ( "kubernetes", "hashicorp/kubernetes", "~> 2.0", ), ProviderType.DOCKER_SWARM: ( "docker", "kreuzwerker/docker", "~> 3.0", ), ProviderType.SYNOLOGY: ( "synology", "synology-community/synology", "~> 0.2", ), ProviderType.HARVESTER: ( "harvester", "harvester/harvester", "~> 0.6", ), ProviderType.BARE_METAL: ( "redfish", "dell/redfish", "~> 1.0", ), ProviderType.WINDOWS: ( "windows", "hashicorp/windows", "~> 0.1", ), } def _generate_provider_config( provider_type: ProviderType, profile: ScanProfile ) -> str: """Generate the provider configuration block for a given provider type. Uses credentials and endpoints from the ScanProfile to populate platform-specific configuration attributes. """ tf_name = _PROVIDER_METADATA[provider_type][0] lines: list[str] = [] lines.append(f'provider "{tf_name}" {{') if provider_type == ProviderType.KUBERNETES: host = profile.credentials.get("host", "") cluster_ca = profile.credentials.get("cluster_ca_certificate", "") token = profile.credentials.get("token", "") lines.append(f' host = "{host}"') lines.append(f' cluster_ca_certificate = "{cluster_ca}"') lines.append(f' token = "{token}"') elif provider_type == ProviderType.DOCKER_SWARM: host = profile.credentials.get("host", "") cert_path = profile.credentials.get("cert_path", "") lines.append(f' host = "{host}"') lines.append(f' cert_path = "{cert_path}"') elif provider_type == ProviderType.SYNOLOGY: url = profile.credentials.get("url", "") username = profile.credentials.get("username", "") password = profile.credentials.get("password", "") lines.append(f' url = "{url}"') lines.append(f' username = "{username}"') lines.append(f' password = "{password}"') elif provider_type == ProviderType.HARVESTER: kubeconfig = profile.credentials.get("kubeconfig", "") lines.append(f' kubeconfig = "{kubeconfig}"') elif provider_type == ProviderType.BARE_METAL: endpoint = profile.credentials.get("endpoint", "") username = profile.credentials.get("username", "") password = profile.credentials.get("password", "") lines.append(f' endpoint = "{endpoint}"') lines.append(f' username = "{username}"') lines.append(f' password = "{password}"') elif provider_type == ProviderType.WINDOWS: host = profile.credentials.get("host", "") username = profile.credentials.get("username", "") password = profile.credentials.get("password", "") lines.append(f' host = "{host}"') lines.append(f' username = "{username}"') lines.append(f' password = "{password}"') lines.append("") lines.append(" winrm {") winrm_port = profile.credentials.get("winrm_port", "5985") winrm_use_ssl = profile.credentials.get("winrm_use_ssl", "false") lines.append(f" port = {winrm_port}") lines.append(f" use_ssl = {winrm_use_ssl}") lines.append(" }") lines.append("}") return "\n".join(lines) def _generate_required_providers_block( provider_types: set[ProviderType], ) -> str: """Generate the terraform { required_providers { ... } } block.""" lines: list[str] = [] lines.append("terraform {") lines.append(" required_providers {") for provider_type in sorted(provider_types, key=lambda p: p.value): tf_name, source, version = _PROVIDER_METADATA[provider_type] lines.append(f" {tf_name} = {{") lines.append(f' source = "{source}"') lines.append(f' version = "{version}"') lines.append(" }") lines.append(" }") lines.append("}") return "\n".join(lines) # --------------------------------------------------------------------------- # ProviderBlockGenerator # --------------------------------------------------------------------------- class ProviderBlockGenerator: """Generates Terraform provider configuration blocks. Accepts a list of ScanProfiles and a set of ProviderTypes used in the generated code, and produces a providers.tf file containing: - A terraform { required_providers { ... } } block - Individual provider blocks with platform-specific configuration """ def generate( self, profiles: list[ScanProfile], provider_types: set[ProviderType], ) -> GeneratedFile: """Generate the providers.tf file content. Args: profiles: List of ScanProfiles providing credentials/endpoints. provider_types: Set of distinct ProviderTypes used in the code. Returns: A GeneratedFile with filename "providers.tf" and the HCL content. """ # Build a map from ProviderType -> first matching profile profile_map: dict[ProviderType, ScanProfile] = {} for profile in profiles: if profile.provider not in profile_map: profile_map[profile.provider] = profile sections: list[str] = [] # 1. required_providers block sections.append(_generate_required_providers_block(provider_types)) # 2. Individual provider configuration blocks for provider_type in sorted(provider_types, key=lambda p: p.value): profile = profile_map.get(provider_type) if profile is not None: sections.append( _generate_provider_config(provider_type, profile) ) else: # Generate a placeholder block if no profile matches tf_name = _PROVIDER_METADATA[provider_type][0] sections.append( f'provider "{tf_name}" {{\n # No profile provided\n}}' ) content = "\n\n".join(sections) + "\n" return GeneratedFile( filename="providers.tf", content=content, resource_count=0, )