turbot / steampipe-plugin-azuread

Use SQL to instantly query groups, service principals, users and more from Azure Active Directory. Open source CLI. No DB required.
https://hub.steampipe.io/plugins/turbot/azuread
Apache License 2.0
7 stars 12 forks source link

Support querying appRoleAssignments #173

Closed laurensknoll closed 6 months ago

laurensknoll commented 8 months ago

This change adds support for querying appRoleAssignments: Assignments recording that a user, group, or service principal is assigned an app role for an app.

Because the appRoleAssignments are accessible via multiple entry points, the following tables are added:

Note that the app_role_assigned_to endpoints where not available in the used graph sdk version, therefore the package is updated to the latest version, causing changes in additional files.

Example queries

I've included a couple example queries, if needed, I can add more.

azuread_user_app_role_assignment get

select *
from azuread_user_app_role_assignment
where id = 'E0hQUn45H0GRSeh7dgWzpXedJZbRURRFjtFVb556x7c'
  and user_id = '52504813-397e-411f-9149-e87b7605b3a5'
[
 {
  "_ctx": {
   "connection_name": "azuread",
   "steampipe": {
    "sdk_version": "5.8.0"
   }
  },
  "app_role_id": "9e79c739-f165-49e8-8745-fe08f1634b3b",
  "created_date_time": "2024-03-06T16:29:47+01:00",
  "deleted_date_time": null,
  "id": "E0hQUn45H0GRSeh7dgWzpXedJZbRURRFjtFVb556x7c",
  "principal_display_name": "..",
  "principal_id": "52504813-397e-411f-9149-e87b7605b3a5",
  "principal_type": "User",
  "resource_display_name": "Google Cloud / G Suite Connector by Microsoft",
  "resource_id": "13e1f26a-d4fe-4b81-b23e-a39a454f3d17",
  "tenant_id": "c4c0a0f9-1600-4834-a577-30c7ef8a2228",
  "user_id": "52504813-397e-411f-9149-e87b7605b3a5"
 }
]

azuread_user_app_role_assignment list

select *
from azuread_user_app_role_assignment
where user_id = '52504813-397e-411f-9149-e87b7605b3a5'
[
 {
  "_ctx": {
   "connection_name": "azuread",
   "steampipe": {
    "sdk_version": "5.8.0"
   }
  },
  "app_role_id": "9e79c739-f165-49e8-8745-fe08f1634b3b",
  "created_date_time": "2024-03-06T16:29:47+01:00",
  "deleted_date_time": null,
  "id": "E0hQUn45H0GRSeh7dgWzpXedJZbRURRFjtFVb556x7c",
  "principal_display_name": "...",
  "principal_id": "52504813-397e-411f-9149-e87b7605b3a5",
  "principal_type": "User",
  "resource_display_name": "Google Cloud / G Suite Connector by Microsoft",
  "resource_id": "13e1f26a-d4fe-4b81-b23e-a39a454f3d17",
  "tenant_id": "c4c0a0f9-1600-4834-a577-30c7ef8a2228",
  "user_id": "52504813-397e-411f-9149-e87b7605b3a5"
 }
]

azuread_application_app_role_assigned_to get

select *
from azuread_application_app_role_assigned_to
where id = 'E0hQUn45H0GRSeh7dgWzpXedJZbRURRFjtFVb556x7c'
  and app_id = 'b724df63-9946-41a9-8bd0-a524c81dbf2c'
[
 {
  "_ctx": {
   "connection_name": "azuread",
   "steampipe": {
    "sdk_version": "5.8.0"
   }
  },
  "app_id": "b724df63-9946-41a9-8bd0-a524c81dbf2c",
  "app_role_id": "9e79c739-f165-49e8-8745-fe08f1634b3b",
  "created_date_time": "2024-03-06T16:29:47+01:00",
  "deleted_date_time": null,
  "id": "E0hQUn45H0GRSeh7dgWzpXedJZbRURRFjtFVb556x7c",
  "principal_display_name": "..",
  "principal_id": "52504813-397e-411f-9149-e87b7605b3a5",
  "principal_type": "User",
  "resource_display_name": "Google Cloud / G Suite Connector by Microsoft",
  "resource_id": "13e1f26a-d4fe-4b81-b23e-a39a454f3d17",
  "tenant_id": "c4c0a0f9-1600-4834-a577-30c7ef8a2228"
 }
]

azuread_application_app_role_assigned_to list

select *
from azuread_application_app_role_assigned_to arat
where app_id = 'b724df63-9946-41a9-8bd0-a524c81dbf2c'
[
 {
  "_ctx": {
   "connection_name": "azuread",
   "steampipe": {
    "sdk_version": "5.8.0"
   }
  },
  "app_id": "b724df63-9946-41a9-8bd0-a524c81dbf2c",
  "app_role_id": "9e79c739-f165-49e8-8745-fe08f1634b3b",
  "created_date_time": "2024-03-06T16:29:47+01:00",
  "deleted_date_time": null,
  "id": "E0hQUn45H0GRSeh7dgWzpXedJZbRURRFjtFVb556x7c",
  "principal_display_name": "...",
  "principal_id": "52504813-397e-411f-9149-e87b7605b3a5",
  "principal_type": "User",
  "resource_display_name": "Google Cloud / G Suite Connector by Microsoft",
  "resource_id": "13e1f26a-d4fe-4b81-b23e-a39a454f3d17",
  "tenant_id": "c4c0a0f9-1600-4834-a577-30c7ef8a2228"
 }
]
laurensknoll commented 8 months ago

@misraved , @madhushreeray30 Could you review this PR?

misraved commented 8 months ago

Great to see the PR @laurensknoll 🎉 !!

Thank you so much for the contribution 👍.

misraved commented 7 months ago

@laurensknoll the changes look good 👍!!

Few questions before we move forward with the PR:

laurensknoll commented 7 months ago

@laurensknoll the changes look good 👍!!

Few questions before we move forward with the PR:

  • Do the updated SDK dependencies have any effect on the pre-existing tables?
  • From the SDK CHANGELOG, it is important to note the following points:

    • Has the authentication mechanism changed?
    • Has any column/response parameter been removed?
    • Has the datatype of any column changed?

Happy to hear that @misraved . The SDK update did not affect the authentication mechanism, remove/rename columns or change data types.

PS. It would be useful to support a unit test to verify that with stub data or by supporting a transform.FromModel[model](func(m) { return m.GetId() })-kind of method to guard this changes. Do you know if anything like that is planned?

misraved commented 7 months ago

@laurensknoll thank you for the answers 👍.

Apologies, but we do not have a strong unit test to verify the changes.

Could you please explain the usage of transform.FromModel[model](func(m) { return m.GetId() }) in more detail? We could consider integrating it into our plugin and would like to understand its functionality and potential benefits better.

laurensknoll commented 7 months ago

@laurensknoll thank you for the answers 👍.

Apologies, but we do not have a strong unit test to verify the changes.

Could you please explain the usage of transform.FromModel[model](func(m) { return m.GetId() }) in more detail? We could consider integrating it into our plugin and would like to understand its functionality and potential benefits better.

Hi @misraved ,

Sure, the transform.FromModel takes a function instead of a string. This allows you to pass the interface method as ADUserInfo.GetId. As a result, the method reference is checked at compile-time.

Example table definition:

func tableAzureAdUser(_ context.Context) *plugin.Table {
    return &plugin.Table{
        Name:        "azuread_user",
        ...
        Columns: []*plugin.Column{
            {Name: "display_name", Transform: FromModel(ADUserInfo.GetDisplayName)},
            {Name: "id", Transform: FromModel(ADUserInfo.GetId)},
            {Name: "user_principal_name", Transform: FromModel(ADUserInfo.GetUserPrincipalName)},
                         ...
        },
    }
}

Example FromModel implementation:

func FromModel[V interface{}, R interface{}](f func(obj V) R) *transform.ColumnTransforms {
    fun := func(_ context.Context, d *transform.TransformData) (interface{}, error) {
        item := d.HydrateItem.(*V)
        if item == nil {
            return nil, fmt.Errorf("failed to cast hyradeItem %T to model %T", d.HydrateItem, *new(V))
        }

        return f(*item), nil
    }

    return transform.From(fun)
}
misraved commented 7 months ago

Thanks @laurensknoll for the insight 👍!!

misraved commented 6 months ago

Apologies for the delay on this PR @laurensknoll

I have tested the changes and they look good. Could you please resolve the merge conflicts in the PR so that we can proceed with the release?

Thanks!!

laurensknoll commented 6 months ago

Apologies for the delay on this PR @laurensknoll

I have tested the changes and they look good. Could you please resolve the merge conflicts in the PR so that we can proceed with the release?

Thanks!!

Thanks for the update @misraved . I've resolved the merge conflicts such that you can proceed with a final review.