Skip to content

Commit

Permalink
add the ability to list eligible child resources for a given scope (#124
Browse files Browse the repository at this point in the history
)
  • Loading branch information
demoray authored Jul 11, 2024
1 parent c9e7660 commit 8e0f4c2
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 1 deletion.
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ Usage: role [OPTIONS] <COMMAND>
Commands:
assignment Manage role assignments
definition Manage role definitions
resources Commands related to resources in Azure
Options:
--verbose...
Expand Down Expand Up @@ -768,6 +769,79 @@ $ az-pim role definition list --subscription 00000000-0000-0000-0000-00000000000
$
```

### az-pim role resources

```
Commands related to resources in Azure
Usage: resources [OPTIONS] <COMMAND>
Commands:
list List the child resources of a resource which you have eligible access
Options:
--verbose...
Increase logging verbosity. Provide repeatedly to increase the verbosity
--quiet
Only show errors
-h, --help
Print help
```
#### az-pim role resources list

```
List the child resources of a resource which you have eligible access
Usage: list [OPTIONS]
Options:
--subscription <SUBSCRIPTION>
Limit the scope by the specified Subscription
--verbose...
Increase logging verbosity. Provide repeatedly to increase the verbosity
--quiet
Only show errors
--resource-group <RESOURCE_GROUP>
Limit the scope by the specified Resource Group
This argument requires `subscription` to be set.
--provider <PROVIDER>
Provider
This argument requires `subscription` and `resource_group` to be set.
--scope <SCOPE>
Specify scope directly
-h, --help
Print help (see a summary with '-h')
```
##### Example Usage

```
$ az-pim role resources list --subscription 00000000-0000-0000-0000-000000000000
[
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/DefaultResourceGroup-EUS",
"name": "DefaultResourceGroup-EUS",
"type": "resourcegroup"
},
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/DefaultResourceGroup-SUK",
"name": "DefaultResourceGroup-SUK",
"type": "resourcegroup"
}
]
```

## az-pim init <SHELL>

```
Expand Down
57 changes: 56 additions & 1 deletion src/bin/az-pim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ impl Cmd {
| "az-pim deactivate interactive"
| "az-pim role"
| "az-pim role assignment"
| "az-pim role definition" => None,
| "az-pim role definition"
| "az-pim role resources" => None,
"az-pim list" => Some(include_str!("../help/az-pim-list.txt")),
"az-pim activate role <ROLE> <SCOPE> <JUSTIFICATION>" => {
Some(include_str!("../help/az-pim-activate-role.txt"))
Expand Down Expand Up @@ -86,6 +87,9 @@ impl Cmd {
"az-pim role definition list" => {
Some(include_str!("../help/az-pim-role-definition-list.txt"))
}
"az-pim role resources list" => {
Some(include_str!("../help/az-pim-role-resources-list.txt"))
}
unsupported => unimplemented!("unable to generate example for {unsupported}"),
}
}
Expand Down Expand Up @@ -449,6 +453,12 @@ enum RoleSubCommand {
#[clap(subcommand)]
cmd: DefinitionSubCommand,
},

/// Commands related to resources in Azure
Resources {
#[clap(subcommand)]
cmd: ResourcesSubCommand,
},
}

#[derive(Subcommand)]
Expand Down Expand Up @@ -678,6 +688,50 @@ impl DefinitionSubCommand {
}
}

#[derive(Subcommand)]
enum ResourcesSubCommand {
/// List the child resources of a resource which you have eligible access
List {
/// Limit the scope by the specified Subscription
#[arg(long)]
subscription: Option<Uuid>,

/// Limit the scope by the specified Resource Group
///
/// This argument requires `subscription` to be set.
#[arg(long, requires = "subscription")]
resource_group: Option<String>,

/// Provider
///
/// This argument requires `subscription` and `resource_group` to be set.
#[arg(long, requires = "resource_group")]
provider: Option<String>,

/// Specify scope directly
#[arg(long, conflicts_with = "subscription", required_unless_present_any = ["subscription", "resource_group", "provider"])]
scope: Option<Scope>,
},
}

impl ResourcesSubCommand {
fn run(self, client: &PimClient) -> Result<()> {
match self {
Self::List {
subscription,
resource_group,
scope,
provider,
} => {
let scope = build_scope(subscription, resource_group, scope, provider)?
.context("valid scope must be provided")?;
output(&client.eligible_child_resources(&scope)?)?;
}
}
Ok(())
}
}

/// Parse a single key-value pair of `X=Y` into a typed tuple of `(X, Y)`.
///
/// # Errors
Expand Down Expand Up @@ -816,6 +870,7 @@ fn main() -> Result<()> {
SubCommand::Role { cmd } => match cmd {
RoleSubCommand::Assignment { cmd } => cmd.run(&client),
RoleSubCommand::Definition { cmd } => cmd.run(&client),
RoleSubCommand::Resources { cmd } => cmd.run(&client),
},
SubCommand::Readme => {
build_readme();
Expand Down
13 changes: 13 additions & 0 deletions src/help/az-pim-role-resources-list.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
$ az-pim role resources list --subscription 00000000-0000-0000-0000-000000000000
[
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/DefaultResourceGroup-EUS",
"name": "DefaultResourceGroup-EUS",
"type": "resourcegroup"
},
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/DefaultResourceGroup-SUK",
"name": "DefaultResourceGroup-SUK",
"type": "resourcegroup"
}
]
13 changes: 13 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod definitions;
mod graph;
pub mod interactive;
mod latest;
pub mod resources;
pub mod roles;
pub mod scope;

Expand All @@ -26,6 +27,7 @@ use crate::{
backend::Backend,
definitions::{Definition, Definitions},
graph::get_objects_by_ids,
resources::ChildResource,
roles::{RoleAssignment, RoleAssignments},
scope::Scope,
};
Expand Down Expand Up @@ -498,6 +500,17 @@ impl PimClient {
Ok(assignments)
}

pub fn eligible_child_resources(&self, scope: &Scope) -> Result<BTreeSet<ChildResource>> {
info!("listing eligible child resources for {scope}");
let value = self
.backend
.request(Method::GET, Operation::EligibleChildResources)
.scope(scope.clone())
.send()
.context("unable to list eligible child resources")?;
ChildResource::parse(&value)
}

/// List all assignments (not just those managed by PIM)
pub fn role_definitions(&self, scope: &Scope) -> Result<Vec<Definition>> {
info!("listing role definitions for {scope}");
Expand Down
47 changes: 47 additions & 0 deletions src/resources.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::BTreeSet;

use crate::scope::Scope;

#[derive(Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq, Debug)]
pub struct ChildResource {
pub id: Scope,
pub name: String,
#[serde(rename = "type")]
pub type_: String,
}

impl ChildResource {
pub(crate) fn parse(data: &Value) -> Result<BTreeSet<Self>> {
let mut results = BTreeSet::new();

if let Some(value) = data.get("value") {
if let Some(value) = value.as_array() {
for entry in value {
let child_resource: ChildResource = serde_json::from_value(entry.clone())?;
results.insert(child_resource);
}
}
}

Ok(results)
}
}

#[cfg(test)]
mod tests {
use super::ChildResource;
use anyhow::Result;
use insta::assert_json_snapshot;
use serde_json::{from_str, Value};

#[test]
fn test_child_resource_parse() -> Result<()> {
let data: Value = from_str(include_str!("../tests/data/child-resources.json"))?;
let result = ChildResource::parse(&data)?;
assert_json_snapshot!(result);
Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
source: src/resources.rs
expression: result
---
[
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group",
"name": "DefaultResourceGroup-EUS",
"type": "resourcegroup"
},
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/my-resource-group2",
"name": "DefaultResourceGroup-EUS",
"type": "resourcegroup"
}
]
14 changes: 14 additions & 0 deletions tests/data/child-resources.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"value": [
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group",
"name": "DefaultResourceGroup-EUS",
"type": "resourcegroup"
},
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000001/resourceGroups/my-resource-group2",
"name": "DefaultResourceGroup-EUS",
"type": "resourcegroup"
}
]
}

0 comments on commit 8e0f4c2

Please sign in to comment.