use cloud_terrastodon_azure::prelude::ResourceGroupScoped;
use cloud_terrastodon_azure::prelude::RoleAssignmentId;
use cloud_terrastodon_azure::prelude::Scope;
use cloud_terrastodon_azure::prelude::SubscriptionScoped;
use cloud_terrastodon_azure::prelude::fetch_all_resource_groups;
use cloud_terrastodon_azure::prelude::fetch_all_role_assignments;
use cloud_terrastodon_azure::prelude::fetch_all_security_groups;
use cloud_terrastodon_azure::prelude::fetch_all_subscriptions;
use cloud_terrastodon_azure::prelude::uuid::Uuid;
use cloud_terrastodon_hcl::prelude::HclImportBlock;
use cloud_terrastodon_hcl::prelude::HclProviderReference;
use cloud_terrastodon_hcl::prelude::HclWriter;
use cloud_terrastodon_hcl::prelude::ProviderKind;
use cloud_terrastodon_hcl::prelude::Sanitizable;
use cloud_terrastodon_pathing::AppDir;
use cloud_terrastodon_user_input::Choice;
use cloud_terrastodon_user_input::PickerTui;
use eyre::Result;
use eyre::bail;
use std::collections::HashMap;
use std::collections::HashSet;
use tokio::fs::remove_dir_all;
use tokio::join;
use tracing::info;
pub async fn resource_group_import_wizard_menu() -> Result<()> {
info!("Confirming remove existing imports");
let start_from_scratch = "start from scratch";
let keep_existing_imports = "keep existing imports";
match PickerTui::new()
.set_header("This will wipe any existing imports from the Cloud Terrastodon work directory. Proceed?")
.pick_one(vec![start_from_scratch, keep_existing_imports])? {
x if x == start_from_scratch => {
info!("Removing existing imports");
let _ = remove_dir_all(AppDir::Imports.as_path_buf()).await;
let _ = remove_dir_all(AppDir::Processed.as_path_buf()).await;
}
x if x == keep_existing_imports => {
info!("Keeping existing imports");
}
_ => unreachable!(),
}
info!("Fetching a bunch of data");
let (
subscriptions,
resource_groups,
role_assignments,
security_groups,
) = join!(
fetch_all_subscriptions(),
fetch_all_resource_groups(),
fetch_all_role_assignments(),
fetch_all_security_groups(),
);
let subscriptions = subscriptions?
.into_iter()
.map(|sub| (sub.id.to_owned(), sub))
.collect::<HashMap<_, _>>();
let resource_groups = resource_groups?;
let role_assignments = role_assignments?;
let security_groups = security_groups?;
info!("Building pick list");
let mut resource_group_choices = Vec::new();
for rg in resource_groups {
let Some(sub) = subscriptions.get(&rg.subscription_id) else {
bail!(
"Failed to find subscription {} for resource group {}",
rg.subscription_id,
rg.name
);
};
let choice = Choice {
key: format!("{:16} {}", sub.name, rg.name),
value: (rg, sub),
};
resource_group_choices.push(choice);
}
info!("Picking resource groups");
let resource_groups = PickerTui::new()
.set_header("Pick which to import")
.pick_many(resource_group_choices)?;
info!("You chose {} resource groups", resource_groups.len());
let mut used_resource_groups = HashSet::new();
let mut used_subscriptions = HashSet::new();
info!("Building resource group imports");
let mut rg_imports = Vec::new();
for (rg, sub) in resource_groups {
used_resource_groups.insert(rg.id.to_owned());
let mut import_block: HclImportBlock = rg.into();
import_block.provider = HclProviderReference::Alias {
kind: ProviderKind::AzureRM,
name: sub.name.sanitize(),
};
used_subscriptions.insert(sub);
rg_imports.push(import_block);
}
info!("Writing resource group imports");
HclWriter::new(AppDir::Imports.join("resource_group_imports.tf"))
.overwrite(rg_imports)
.await?
.format_file()
.await?;
let mut used_principals: HashSet<Uuid> = HashSet::new();
info!("Building role assignment imports");
let mut ra_imports = Vec::new();
for ra in role_assignments {
let RoleAssignmentId::ResourceGroupScoped(ra_id) = &ra.id else {
continue;
};
if !used_resource_groups.contains(ra_id.resource_group_id()) {
continue;
}
let Some(sub) = subscriptions.get(ra_id.subscription_id()) else {
bail!(
"Could not find subscription for role assignment {}",
ra_id.expanded_form()
);
};
used_principals.insert(*ra.principal_id);
let mut import_block: HclImportBlock = ra.into();
import_block.provider = HclProviderReference::Alias {
kind: ProviderKind::AzureRM,
name: sub.name.sanitize(),
};
ra_imports.push(import_block);
}
info!("Writing role assignment imports");
HclWriter::new(AppDir::Imports.join("role_assignment_imports.tf"))
.overwrite(ra_imports)
.await?
.format_file()
.await?;
info!("Building security group imports");
let mut sg_imports = Vec::new();
for sg in security_groups {
if !used_principals.contains(sg.id.as_ref()) {
continue;
}
let import_block: HclImportBlock = sg.into();
sg_imports.push(import_block);
}
info!("Writing security group imports");
HclWriter::new(AppDir::Imports.join("security_groups.tf"))
.overwrite(sg_imports)
.await?
.format_file()
.await?;
info!("Building provider blocks");
let mut providers = Vec::new();
for sub in used_subscriptions {
let provider = sub.clone().into_provider_block();
providers.push(provider);
}
info!("Writing provider blocks");
HclWriter::new(AppDir::Imports.join("boilerplate.tf"))
.merge(providers)
.await?
.format_file()
.await?;
Ok(())
}