cloud_terrastodon_entrypoint 0.35.1

Main entrypoint for the Cloud Terrastodon CLI
use crate::cli::azure::resource_group::AzureResourceGroupBrowseArgs;
use crate::interactive::apply_processed;
use crate::interactive::azure_devops_project_import_wizard_menu;
use crate::interactive::browse_azure_devops_project_teams;
use crate::interactive::browse_azure_devops_projects;
use crate::interactive::browse_oauth2_permission_grants;
use crate::interactive::browse_policy_assignments;
use crate::interactive::browse_policy_definitions;
use crate::interactive::browse_resources_menu;
use crate::interactive::browse_role_assignments;
use crate::interactive::browse_security_groups;
use crate::interactive::browse_service_principals;
use crate::interactive::browse_storage_accounts;
use crate::interactive::browse_users;
use crate::interactive::build_group_imports;
use crate::interactive::build_imports_from_existing;
use crate::interactive::build_policy_imports;
use crate::interactive::build_resource_group_imports;
use crate::interactive::build_role_assignment_imports;
use crate::interactive::bulk_user_id_lookup;
use crate::interactive::clean_all_menu;
use crate::interactive::clean_imports;
use crate::interactive::clean_processed;
use crate::interactive::copy_azurerm_backend_menu;
use crate::interactive::create_import_block_for_role_assignment;
use crate::interactive::create_new_action_variant;
use crate::interactive::create_oauth2_permission_grants;
use crate::interactive::create_role_assignment_menu;
use crate::interactive::dump_tags;
use crate::interactive::dump_work_items;
use crate::interactive::find_resource_owners_menu;
use crate::interactive::init_processed;
use crate::interactive::jump_to_block;
use crate::interactive::list_imports;
use crate::interactive::open_dir;
use crate::interactive::pim_activate;
use crate::interactive::plan_processed;
use crate::interactive::populate_cache;
use crate::interactive::remove_oauth2_permission_grants;
use crate::interactive::resource_group_import_wizard_menu;
use crate::interactive::run_query_menu;
use crate::interactive::tag_empty_resource_group_menu;
use crate::interactive::tag_resources_menu;
use crate::noninteractive::dump_security_groups_as_json;
use crate::noninteractive::perform_import;
use crate::noninteractive::process_generated;
use crate::noninteractive::write_imports_for_all_resource_groups;
use crate::noninteractive::write_imports_for_all_role_assignments;
use crate::noninteractive::write_imports_for_all_security_groups;
use cloud_terrastodon_azure::AzureTenantArgument;
use cloud_terrastodon_azure::AzureTenantId;
use cloud_terrastodon_azure::evaluate_policy_assignment_compliance;
use cloud_terrastodon_azure::remediate_policy_assignment;
use cloud_terrastodon_command::USE_TOFU_FLAG_KEY;
use cloud_terrastodon_pathing::AppDir;
use eyre::Result;
use itertools::Itertools;
use std::env;
use std::path::PathBuf;
use strum::VariantArray;
use tokio::fs;
pub const THIS_FILE: &str = file!();
#[derive(Debug, VariantArray)]
pub enum MenuAction {
    BuildPolicyImports,
    BuildGroupImports,
    BuildResourceGroupImports,
    BuildRoleAssignmentImports,
    ResourceGraphQuery,
    BuildImportsFromExisting,
    ResourceGroupImportWizard,
    BrowseResourceGroups,
    BrowseRoleAssignments,
    BrowseUsers,
    BrowseSecurityGroups,
    BrowseResources,
    DumpTags,
    TagResources,
    PerformImport,
    ProcessGenerated,
    Clean,
    BuildAllImports,
    CleanImports,
    CleanProcessed,
    CopyAzureRMBackend,
    CreateRoleAssignment,
    InitProcessed,
    ApplyProcessed,
    PlanProcessed,
    JumpToBlock,
    ListImports,
    FindResourceOwners,
    RemediatePolicyAssignment,
    EvaluatePolicyAssignmentCompliance,
    UseTerraform,
    UseTofu,
    PopulateCache,
    PimActivate,
    OpenDir,
    TagEmptyResourceGroups,
    Quit,
    CreateNewActionVariant,
    BrowsePolicyAssignments,
    DumpSecurityGroups,
    BrowsePolicyDefinitions,
    BulkUserIdLookup,
    DumpWorkItems,
    BrowseOAuth2PermissionGrants,
    RemoveOAuth2PermissionGrants,
    CreateOAuth2PermissionGrants,
    AzureDevOpsProjectImportWizard,
    BrowseAzureDevOpsProjects,
    BrowseAzureDevOpsProjectTeams,
    BrowseServicePrincipals,
    BrowseStorageAccounts,
    CreateImportBlockForRoleAssignment,
}
#[derive(Eq, PartialEq, Debug)]
pub enum MenuActionResult {
    QuitApplication,
    Continue,
    PauseAndContinue,
}
impl MenuAction {
    pub fn name(&self) -> &str {
        match self {
            MenuAction::ResourceGroupImportWizard => "build imports - resource group import wizard",
            MenuAction::BuildAllImports => "build imports - import all",
            MenuAction::CopyAzureRMBackend => "copy azurerm backend",
            MenuAction::BrowseResourceGroups => "browse resource groups",
            MenuAction::BrowseUsers => "browse users",
            MenuAction::BrowseRoleAssignments => "browse role assignments",
            MenuAction::BrowseSecurityGroups => "browse security groups",
            MenuAction::BuildPolicyImports => "build imports - create policy_imports.tf",
            MenuAction::BuildResourceGroupImports => {
                "build imports - create resource_group_imports.tf"
            }
            MenuAction::BuildGroupImports => "build imports - create group_imports.tf",
            MenuAction::BuildRoleAssignmentImports => "build imports - create role_assignments.tf",
            MenuAction::BuildImportsFromExisting => "build imports - build from existing",
            MenuAction::PerformImport => {
                "perform import - tf plan -generate-config-out generated.tf"
            }
            MenuAction::ProcessGenerated => "processed - create from generated.tf",
            MenuAction::Clean => "clean all",
            MenuAction::CleanImports => "clean imports",
            MenuAction::CleanProcessed => "clean processed",
            MenuAction::InitProcessed => "processed - tf init",
            MenuAction::ApplyProcessed => "processed - tf apply",
            MenuAction::PlanProcessed => "processed - tf plan",
            MenuAction::JumpToBlock => "jump to block",
            MenuAction::ListImports => "list imports",
            MenuAction::RemediatePolicyAssignment => "remediate policy assignment",
            MenuAction::EvaluatePolicyAssignmentCompliance => {
                "evaluate policy assignment complaince"
            }
            MenuAction::CreateRoleAssignment => "create role assignment",
            MenuAction::UseTerraform => "use terraform",
            MenuAction::UseTofu => "use tofu",
            MenuAction::PopulateCache => "populate cache",
            MenuAction::PimActivate => "pim activate",
            MenuAction::OpenDir => "open dir",
            MenuAction::Quit => "quit",
            MenuAction::TagEmptyResourceGroups => "tag empty resource groups",
            MenuAction::TagResources => "tag resources",
            MenuAction::BrowseResources => "browse resources",
            MenuAction::DumpTags => "dump tags",
            MenuAction::ResourceGraphQuery => "resource graph query",
            MenuAction::FindResourceOwners => "find resource owners",
            MenuAction::CreateNewActionVariant => "create new action variant",
            MenuAction::BrowsePolicyAssignments => "Browse policy assignments",
            MenuAction::DumpSecurityGroups => "dump security groups as json",
            MenuAction::BrowsePolicyDefinitions => "browse policy definitions",
            MenuAction::BulkUserIdLookup => "bulk user id lookup",
            MenuAction::DumpWorkItems => "dump work items",
            MenuAction::BrowseOAuth2PermissionGrants => "browse oauth2 permission grants",
            MenuAction::RemoveOAuth2PermissionGrants => "remove oauth2 permission grants",
            MenuAction::CreateOAuth2PermissionGrants => "create oauth2 permission grants",
            MenuAction::AzureDevOpsProjectImportWizard => {
                "build imports - azure devops project import wizard"
            }
            MenuAction::BrowseAzureDevOpsProjects => "browse azure devops projects",
            MenuAction::BrowseAzureDevOpsProjectTeams => "browse azure devops project teams",
            MenuAction::BrowseServicePrincipals => "browse service principals",
            MenuAction::BrowseStorageAccounts => "browse storage accounts",
            MenuAction::CreateImportBlockForRoleAssignment => {
                "create import block for role assignment"
            }
        }
    }
    pub async fn invoke(&self, tenant_id: AzureTenantId) -> Result<MenuActionResult> {
        match self {
            MenuAction::ResourceGroupImportWizard => {
                resource_group_import_wizard_menu(tenant_id).await?
            }
            MenuAction::CopyAzureRMBackend => copy_azurerm_backend_menu(tenant_id).await?,
            MenuAction::BrowseResourceGroups => {
                AzureResourceGroupBrowseArgs {
                    tenant: AzureTenantArgument::Id(tenant_id),
                }
                .invoke()
                .await?
            }
            MenuAction::BrowseRoleAssignments => browse_role_assignments(tenant_id).await?,
            MenuAction::BuildAllImports => {
                write_imports_for_all_resource_groups(tenant_id).await?;
                write_imports_for_all_security_groups(tenant_id).await?;
                write_imports_for_all_role_assignments(tenant_id).await?;
            }
            MenuAction::BrowseUsers => browse_users(tenant_id).await?,
            MenuAction::BrowseSecurityGroups => browse_security_groups(tenant_id).await?,
            MenuAction::BuildPolicyImports => build_policy_imports(tenant_id).await?,
            MenuAction::BuildGroupImports => build_group_imports(tenant_id).await?,
            MenuAction::BuildResourceGroupImports => {
                build_resource_group_imports(tenant_id).await?
            }
            MenuAction::BuildRoleAssignmentImports => {
                build_role_assignment_imports(tenant_id).await?
            }
            MenuAction::BuildImportsFromExisting => build_imports_from_existing().await?,
            MenuAction::PerformImport => perform_import().await?,
            MenuAction::ProcessGenerated => process_generated(tenant_id).await?,
            MenuAction::Clean => clean_all_menu().await?,
            MenuAction::CreateRoleAssignment => create_role_assignment_menu(tenant_id).await?,
            MenuAction::CleanImports => clean_imports().await?,
            MenuAction::CleanProcessed => clean_processed().await?,
            MenuAction::InitProcessed => init_processed().await?,
            MenuAction::ApplyProcessed => apply_processed().await?,
            MenuAction::PlanProcessed => plan_processed().await?,
            MenuAction::PimActivate => pim_activate(tenant_id).await?,
            MenuAction::JumpToBlock => {
                jump_to_block(AppDir::Processed.into()).await?;
                return Ok(MenuActionResult::Continue);
            }
            MenuAction::ListImports => {
                list_imports().await?;
                return Ok(MenuActionResult::Continue);
            }
            MenuAction::RemediatePolicyAssignment => remediate_policy_assignment(tenant_id).await?,
            MenuAction::EvaluatePolicyAssignmentCompliance => {
                evaluate_policy_assignment_compliance(tenant_id).await?
            }
            MenuAction::UseTofu => unsafe { env::set_var(USE_TOFU_FLAG_KEY, "1") },
            MenuAction::UseTerraform => unsafe { env::remove_var(USE_TOFU_FLAG_KEY) },
            MenuAction::PopulateCache => populate_cache(tenant_id).await?,
            MenuAction::OpenDir => open_dir().await?,
            MenuAction::Quit => return Ok(MenuActionResult::QuitApplication),
            MenuAction::TagEmptyResourceGroups => tag_empty_resource_group_menu(tenant_id).await?,
            MenuAction::TagResources => tag_resources_menu(tenant_id).await?,
            MenuAction::BrowseResources => browse_resources_menu(tenant_id).await?,
            MenuAction::DumpTags => dump_tags(tenant_id).await?,
            MenuAction::ResourceGraphQuery => run_query_menu(tenant_id).await?,
            MenuAction::FindResourceOwners => find_resource_owners_menu(tenant_id).await?,
            MenuAction::CreateNewActionVariant => create_new_action_variant().await?,
            MenuAction::BrowsePolicyAssignments => browse_policy_assignments(tenant_id).await?,
            MenuAction::DumpSecurityGroups => dump_security_groups_as_json(tenant_id).await?,
            MenuAction::BrowsePolicyDefinitions => browse_policy_definitions(tenant_id).await?,
            MenuAction::BulkUserIdLookup => bulk_user_id_lookup(tenant_id).await?,
            MenuAction::DumpWorkItems => dump_work_items().await?,
            MenuAction::BrowseOAuth2PermissionGrants => {
                browse_oauth2_permission_grants(tenant_id).await?
            }
            MenuAction::RemoveOAuth2PermissionGrants => {
                remove_oauth2_permission_grants(tenant_id).await?
            }
            MenuAction::CreateOAuth2PermissionGrants => {
                create_oauth2_permission_grants(tenant_id).await?
            }
            MenuAction::AzureDevOpsProjectImportWizard => {
                azure_devops_project_import_wizard_menu().await?
            }
            MenuAction::BrowseAzureDevOpsProjects => browse_azure_devops_projects().await?,
            MenuAction::BrowseAzureDevOpsProjectTeams => {
                browse_azure_devops_project_teams().await?
            }
            MenuAction::BrowseServicePrincipals => browse_service_principals(tenant_id).await?,
            MenuAction::BrowseStorageAccounts => browse_storage_accounts(tenant_id).await?,
            MenuAction::CreateImportBlockForRoleAssignment => {
                create_import_block_for_role_assignment(tenant_id).await?
            }
        }
        Ok(MenuActionResult::PauseAndContinue)
    }
    /// Some actions don't make sense if files are missing from expected locations.
    pub async fn is_available(&self) -> bool {
        async fn all_exist(required_files: impl IntoIterator<Item = PathBuf>) -> bool {
            for path in required_files {
                if !fs::try_exists(path).await.unwrap_or(false) {
                    return false;
                }
            }
            true
        }
        async fn any_exist(required_files: impl IntoIterator<Item = PathBuf>) -> bool {
            for path in required_files {
                if fs::try_exists(path).await.unwrap_or(false) {
                    return true;
                }
            }
            false
        }
        match self {
            MenuAction::PerformImport => {
                any_exist([
                    AppDir::Imports.join("policy_imports.tf"),
                    AppDir::Imports.join("group_imports.tf"),
                    AppDir::Imports.join("resource_group_imports.tf"),
                    AppDir::Imports.join("role_assignment_imports.tf"),
                    AppDir::Imports.join("azure_devops_project_imports.tf"),
                    AppDir::Imports.join("existing.tf"),
                ])
                .await
            }
            MenuAction::ListImports => all_exist([AppDir::Imports.into()]).await,
            MenuAction::ProcessGenerated => all_exist([AppDir::Imports.join("generated.tf")]).await,
            MenuAction::Clean => {
                any_exist(
                    AppDir::ok_to_clean()
                        .into_iter()
                        .map(|x| x.as_path_buf())
                        .collect_vec(),
                )
                .await
            }
            MenuAction::CleanImports => all_exist([AppDir::Imports.into()]).await,
            MenuAction::CleanProcessed => all_exist([AppDir::Processed.into()]).await,
            MenuAction::InitProcessed => all_exist([AppDir::Processed.join("generated.tf")]).await,
            MenuAction::ApplyProcessed | MenuAction::PlanProcessed => {
                all_exist([AppDir::Processed.join(".terraform.lock.hcl")]).await
            }
            MenuAction::JumpToBlock => all_exist([AppDir::Processed.join("generated.tf")]).await,
            MenuAction::UseTofu => env::var(USE_TOFU_FLAG_KEY).is_err(),
            MenuAction::UseTerraform => env::var(USE_TOFU_FLAG_KEY).is_ok(),
            #[cfg(not(debug_assertions))]
            MenuAction::CreateNewActionVariant => false,
            _ => true,
        }
    }
}
impl std::fmt::Display for MenuAction {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.name())
    }
}