use crate::prelude::fetch_all_policy_assignments;
use crate::prelude::fetch_all_policy_set_definitions;
use cloud_terrastodon_azure_types::prelude::DistinctByScope;
use cloud_terrastodon_azure_types::prelude::PolicyAssignment;
use cloud_terrastodon_azure_types::prelude::PolicyDefinitionIdReference;
use cloud_terrastodon_azure_types::prelude::Scope;
use cloud_terrastodon_azure_types::prelude::ScopeImpl;
use cloud_terrastodon_command::CommandBuilder;
use cloud_terrastodon_command::CommandKind;
use cloud_terrastodon_user_input::Choice;
use cloud_terrastodon_user_input::PickerTui;
use eyre::Result;
use eyre::bail;
use eyre::eyre;
use itertools::Itertools;
use rand::RngCore;
use tracing::info;
pub async fn remediate_policy_assignment() -> Result<()> {
info!("Fetching policy assignments");
let policy_assignments = fetch_all_policy_assignments().await?;
info!("Building choices of policies to remediate");
let choices = policy_assignments
.into_iter()
.distinct_by_scope()
.map(|ass| Choice::<PolicyAssignment> {
key: format!("{} {:?}", ass.name, ass.properties.display_name),
value: ass,
});
info!("Prompting for user choice");
let policy_assignment: PolicyAssignment = PickerTui::new()
.set_header("Choose policy to remediate")
.pick_one(choices)?;
info!("Finding policy definition for chosen");
match policy_assignment.properties.policy_definition_id {
PolicyDefinitionIdReference::PolicySetDefinitionId(policy_set_definition_id) => {
info!("Remediating a policy set - must prompt for inner choice");
let Some(policy_set_definition) = fetch_all_policy_set_definitions()
.await?
.into_iter()
.find(|def| def.id == policy_set_definition_id)
else {
bail!("Could not find policy set definition with id {policy_set_definition_id:?}");
};
info!("Found policy set definition - prompting for inner definitions to remediate");
let reference_ids = policy_set_definition
.policy_definitions
.ok_or(eyre!(
"Expected {policy_set_definition_id:?} to have inner policy definitions"
))?
.into_iter()
.map(|x| Choice {
key: x.policy_definition_reference_id.to_owned(),
value: x,
})
.collect_vec();
let chosen = PickerTui::new()
.set_header("Pick the inner definitions to remediate")
.pick_many(reference_ids)?;
info!("Launching remediation tasks");
for choice in chosen {
let mut cmd = CommandBuilder::new(CommandKind::AzureCLI);
cmd.args(["policy", "remediation", "create"]);
cmd.args([
"--name",
format!("myRemediation{:x}", rand::rng().next_u32()).as_ref(),
]);
cmd.args(["--policy-assignment", &policy_assignment.id.expanded_form()]);
cmd.args([
"--definition-reference-id",
choice.policy_definition_reference_id.as_ref(),
]);
let scope = &policy_assignment.properties.scope;
match scope {
ScopeImpl::ManagementGroup(management_group_id) => {
cmd.args(["--management-group", &management_group_id.short_form()]);
}
ScopeImpl::ResourceGroup(resource_group_id) => {
cmd.args(["--resource-group", &resource_group_id.short_form()]);
}
x => {
bail!(
"Scope is of unsupported kind, expected management group or resource group, got {x:?}"
);
}
}
cmd.should_announce(true);
cmd.run_raw().await?;
}
}
PolicyDefinitionIdReference::PolicyDefinitionId(_policy_definition_id) => {
info!("Remediating a policy definition");
let mut cmd = CommandBuilder::new(CommandKind::AzureCLI);
cmd.args(["policy", "remediation", "create"]);
cmd.args([
"--name",
format!("myRemediation{:x}", rand::rng().next_u32()).as_ref(),
]);
cmd.args(["--policy-assignment", &policy_assignment.id.expanded_form()]);
let scope = &policy_assignment.properties.scope;
match scope {
ScopeImpl::ManagementGroup(management_group_id) => {
cmd.args(["--management-group", &management_group_id.short_form()]);
}
ScopeImpl::ResourceGroup(resource_group_id) => {
cmd.args(["--resource-group", &resource_group_id.short_form()]);
}
x => {
bail!(
"Scope is of unsupported kind, expected management group or resource group, got {x:?}"
);
}
}
cmd.should_announce(true);
cmd.run_raw().await?;
}
}
Ok(())
}