cloud_terrastodon_entrypoint 0.35.0

Main entrypoint for the Cloud Terrastodon CLI
use cloud_terrastodon_azure::AzureTenantId;
use cloud_terrastodon_azure::fetch_all_policy_assignments;
use cloud_terrastodon_azure::fetch_all_policy_definitions;
use cloud_terrastodon_azure::fetch_all_policy_set_definitions;
use cloud_terrastodon_azure::fetch_all_subscriptions;
use cloud_terrastodon_hcl::HclImportBlock;
use cloud_terrastodon_hcl::HclProviderReference;
use cloud_terrastodon_hcl::HclWriter;
use cloud_terrastodon_hcl::ProviderKind;
use cloud_terrastodon_hcl::Sanitizable;
use cloud_terrastodon_pathing::AppDir;
use eyre::Result;
use eyre::eyre;
use std::collections::HashMap;
use std::collections::HashSet;
use tokio::try_join;
use tracing::info;

pub async fn build_policy_imports(tenant_id: AzureTenantId) -> Result<()> {
    info!("Fetching information");
    let (policy_definitions, policy_set_definitions, policy_assignments, subscriptions) = try_join!(
        fetch_all_policy_definitions(tenant_id),
        fetch_all_policy_set_definitions(tenant_id),
        fetch_all_policy_assignments(tenant_id),
        fetch_all_subscriptions(tenant_id),
    )?;

    let subscriptions = subscriptions
        .into_iter()
        .map(|sub| (sub.id, sub))
        .collect::<HashMap<_, _>>();

    let mut imports: Vec<HclImportBlock> = Default::default();
    let mut seen_ids: HashSet<String> = HashSet::new();
    let mut provider_blocks: HashSet<_> = Default::default();

    info!("Writing policy definition import blocks");
    for policy_definition in policy_definitions {
        if policy_definition.policy_type == "Custom" {
            let provider = if let Some(subscription_id) = policy_definition.id.subscription_id() {
                let subscription = subscriptions.get(&subscription_id).ok_or(eyre!(format!(
                    "Could not find subscription with id {}",
                    &subscription_id
                )))?;
                let azurerm_provider_block = subscription.into_provider_block();
                provider_blocks.insert(azurerm_provider_block.clone());
                HclProviderReference::Alias {
                    kind: ProviderKind::AzureRM,
                    name: subscription.name.sanitize(),
                }
            } else {
                HclProviderReference::Inherited
            };
            let mut block: HclImportBlock = policy_definition.into();
            block.provider = provider;
            if seen_ids.insert(block.id.clone()) {
                imports.push(block);
            }
        }
    }

    info!("Writing policy set definition import blocks");
    for policy_set_definition in policy_set_definitions {
        if policy_set_definition.policy_type == "Custom" {
            let provider =
                if let Some(subscription_id) = policy_set_definition.id.subscription_id() {
                    let subscription = subscriptions.get(&subscription_id).ok_or(eyre!(
                        format!("Could not find subscription with id {}", &subscription_id)
                    ))?;
                    let azurerm_provider_block = subscription.into_provider_block();
                    provider_blocks.insert(azurerm_provider_block.clone());
                    HclProviderReference::Alias {
                        kind: ProviderKind::AzureRM,
                        name: subscription.name.sanitize(),
                    }
                } else {
                    HclProviderReference::Inherited
                };
            let mut block: HclImportBlock = policy_set_definition.into();
            block.provider = provider;
            if seen_ids.insert(block.id.clone()) {
                imports.push(block);
            }
        }
    }

    info!("Writing policy assignment import blocks");
    for policy_assignment in policy_assignments {
        //todo: filter out inherited assignments that cause the terraform block label to contain a mismatched management group name
        let import_block: HclImportBlock = policy_assignment.into();
        let provider = import_block.provider;
        let id = import_block.id;
        let to = import_block.to;
        // to.use_name(|name| format!("{}_{}", name, management_group.name()).sanitize());
        let block = HclImportBlock { provider, id, to };
        if seen_ids.insert(block.id.clone()) {
            imports.push(block);
        }
    }

    if imports.is_empty() {
        return Err(eyre!("Imports should not be empty"));
    }

    info!("Writing boilerplate.tf");
    HclWriter::new(AppDir::Imports.join("boilerplate.tf"))
        .format_on_write()
        .merge(provider_blocks)
        .await?;

    info!("Writing policy_imports.tf");
    HclWriter::new(AppDir::Imports.join("policy_imports.tf"))
        .format_on_write()
        .overwrite(imports)
        .await?;

    Ok(())
}