Skip to content

Commit

Permalink
feat: Added scripts to manage Azure Active Directory App Role Assignm…
Browse files Browse the repository at this point in the history
…ents (#336)

Co-authored-by: Stijn Moreels <[email protected]>
Co-authored-by: Pim Simons <[email protected]>
Closes #330
  • Loading branch information
pim-simons authored Nov 29, 2022
1 parent 7bb8bd4 commit bf1271d
Show file tree
Hide file tree
Showing 16 changed files with 1,304 additions and 12 deletions.
3 changes: 2 additions & 1 deletion build/templates/run-pester-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ steps:
where { $_.ModuleName -ne $null -and $_.ModuleName -notlike 'Arcus.Scripting.*' -and $_.ModuleVersion -ne "#{Package.Version}#" } |
% { Write-Host "Install $($_.ModuleName) module $($_.ModuleVersion)"
Install-Module $_.ModuleName -MaximumVersion $_.ModuleVersion -AllowClobber -Force -SkipPublisherCheck } }
Install-Module -Name Az -Force -AllowClobber -SkipPublisherCheck -MaximumVersion 5.6.0
Install-Module -Name Az -Force -AllowClobber -SkipPublisherCheck -MaximumVersion 9.1.1
Install-Module -Name AzTable -Force -SkipPublisherCheck -MaximumVersion 2.1.0
Install-Module -Name Microsoft.Graph.Applications -Force -SkipPublisherCheck -MaximumVersion 1.15.0
Write-Host "Done installing, start importing modules"
displayName: 'Install Pester test framework and Az required modules'
- powershell: |
Expand Down
122 changes: 122 additions & 0 deletions docs/preview/03-Features/powershell/azure-active-directory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: "Azure Active Directory"
layout: default
---

# Azure Active Directory

## Installation

To have access to the following features, you have to import the module:

```powershell
PS> Install-Module -Name Arcus.Scripting.ActiveDirectory
```

## Access Rights to Azure Active Directory

To interact with Azure Active Directory these scripts use the [Microsoft.Graph.Applications](https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.applications/) module, import this module:

```powershell
PS> Install-Module -Name Microsoft.Graph.Applications
```

After importing this module, make sure you are connected to Microsoft Graph with the following scopes:

```powershell
PS> Connect-MgGraph -Scopes "Application.ReadWrite.All,AppRoleAssignment.ReadWrite.All"
```

## Listing the Roles and Role Assignments for an Azure Active Directory Application

Lists the roles and role assignments for an Azure Active Directory Application.

| Parameter | Mandatory | Description |
| ------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ClientId` | yes | The client ID of the Azure Active Directory Application Registration for which the roles and assignments are retrieved. |
| `RolesAssignedToClientId` | no | The client ID of the Azure Active Directory Application Registration to which roles have been assigned, used when you only want to retrieve the assignments for this specific application. |

**Example**

Retrieving all information for a Client Id.

```powershell
PS> List-AzADAppRoleAssignments `
-ClientId "b885c208-6067-44bd-aba9-4010c62b7d85"
#Found role 'FirstRole' on Active Directory Application 'main-application'
#Role 'FirstRole' is assigned to the Active Directory Application 'client-application-one' with ID '6ea09bbd-c21c-460c-b58a-f4a720f51826'
#Role 'FirstRole' is assigned to the Active Directory Application 'client-application-two' with ID 'ebafc99d-cbf4-4bd2-9295-f2b785cfc1a1'
#Found role 'SecondRole' on Active Directory Application 'arcus-scripting-test-main'
#Role 'SecondRole' is assigned to the Active Directory Application 'client-application-one' with ID '6ea09bbd-c21c-460c-b58a-f4a720f51826'
```

Retrieving all information for a Client Id and a specific role.

```powershell
PS> List-AzADAppRoleAssignments `
-ClientId 'b885c208-6067-44bd-aba9-4010c62b7d85' `
-RolesAssignedToClientId '6ea09bbd-c21c-460c-b58a-f4a720f51826'
#Found role 'FirstRole' on Active Directory Application 'main-application'
#Role 'FirstRole' is assigned to the Active Directory Application 'client-application-one' with id '6ea09bbd-c21c-460c-b58a-f4a720f51826'
#Found role 'SecondRole' on Active Directory Application 'main-application'
#Role 'SecondRole' is assigned to the Active Directory Application 'client-application-one' with id '6ea09bbd-c21c-460c-b58a-f4a720f51826'
```

## Add a Role and Assignment for an Azure Active Directory Application

Adds a role assignment for an Azure Active Directory Application. The role will be added to the Azure Active Directory Application Registration defined by the `ClientId` parameter, and a role assignment for this role will be added to the Azure Active Directory Application Registration defined by the `AssignRoleToClientId` parameter.

| Parameter | Mandatory | Description |
| ---------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ClientId` | yes | The client ID of the Azure Active Directory Application Registration to which the role will be added if not present. |
| `Role` | yes | The name of the role. |
| `AssignRoleToClientId` | yes | The client ID of the Azure Active Directory Application Registration for which the role assignment will be created. The role assignment will be created based on the role added to the Azure Active Directory Application Registration defined by the `ClientId`. |

**Example**

```powershell
PS> Add-AzADAppRoleAssignment `
-ClientId "b885c208-6067-44bd-aba9-4010c62b7d85" `
-Role "DummyRole" `
-AssignRoleToClientId "6ea09bbd-c21c-460c-b58a-f4a720f51826"
#Active Directory Application 'main-application' does not contain the role 'DummyRole', adding the role
#Added Role 'DummyRole' to Active Directory Application 'main-application'
#Role Assignment for the role 'DummyRole' added to the Active Directory Application 'client-application-one'
```

## Remove a Role and Assignment from an Azure Active Directory Application

Removes a role assignment for an Azure Active Directory Application.

| Parameter | Mandatory | Description |
| ---------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ClientId` | yes | The client ID of the Azure Active Directory Application Registration containing the role for which the assignment must be removed. |
| `Role` | yes | The name of the role. |
| `RemoveRoleFromClientId` | yes | The client ID of the Azure Active Directory Application Registration for which the role assignment will be removed. |
| `RemoveRoleIfNoAssignmentsAreLeft` | no | Indicate whether to remove the role from the Azure Active Directory Application Registration defined by the `ClientId` parameter when no more role assignments remain for the role. |

**Example**

Removes a role assignment.

```powershell
PS> Remove-AzADAppRoleAssignment `
-ClientId "b885c208-6067-44bd-aba9-4010c62b7d85" `
-Role "DummyRole" `
-RemoveRoleFromClientId "6ea09bbd-c21c-460c-b58a-f4a720f51826" `
#Role assignment for 'DummyRole' has been removed from Active Directory Application 'client-application-one'
```

Removes a role assignment and removes the fole if no assignments are left on the role.

```powershell
PS> Remove-AzADAppRoleAssignment `
-ClientId "b885c208-6067-44bd-aba9-4010c62b7d85" `
-Role "DummyRole" `
-RemoveRoleFromClientId "6ea09bbd-c21c-460c-b58a-f4a720f51826" `
-RemoveRoleIfNoAssignmentsAreLeft
#Role assignment for 'DummyRole' has been removed from Active Directory Application 'client-application-one'
#Role 'DummyRole' on Active Directory Application 'main-application' has been disabled as no more role assignments were left and the option 'RemoveRoleIfNoAssignmentsAreLeft' is set
#Role 'DummyRole' removed from Active Directory Application 'main-application' as no more role assignments were left and the option 'RemoveRoleIfNoAssignmentsAreLeft' is set
```

Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<#
.Synopsis
Return the app roles and their assignments that are present on an Azure Active Directory Application Registration.
.Description
Return the app roles that are present in an Azure Active Directory Application Registration and list the applications they are assigned to.
.Parameter ClientId
The client ID of the Azure Active Directory Application Registration from which the role assignments are to be retrieved.
.Parameter RolesAssignedToClientId
The client ID of the Azure Active Directory Application Registration to which roles are assigned.
#>
function List-AzADAppRoleAssignments {
param(
[Parameter(Mandatory = $true)][string] $ClientId = $(throw "ClientId is required"),
[Parameter(Mandatory = $false)][string] $RolesAssignedToClientId
)
. $PSScriptRoot\Scripts\List-AzADAppRoleAssignments.ps1 -ClientId $ClientId -RolesAssignedToClientId $RolesAssignedToClientId
}

Export-ModuleMember -Function List-AzADAppRoleAssignments

<#
.Synopsis
Add and assign a role to an Azure Active Directory Application Registration.
.Description
Add a role to an Azure Active Directory Application Registration and assign the role to a different Active Directory Application Registration.
.Parameter ClientId
The client ID of the Azure Active Directory Application Registration to which the role will be added.
.Parameter Role
The name of the role to add and assign.
.Parameter AssignRoleToClientId
The client ID of the Azure Active Directory Application Registration to which the role will be assigned.
#>
function Add-AzADAppRoleAssignment {
param(
[Parameter(Mandatory = $true)][string] $ClientId = $(throw "ClientId is required"),
[Parameter(Mandatory = $true)][string] $Role = $(throw "Role is required"),
[Parameter(Mandatory = $true)][string] $AssignRoleToClientId = $(throw "ClientId to assign the role to is required")
)
. $PSScriptRoot\Scripts\Add-AzADAppRoleAssignment.ps1 -ClientId $ClientId -Role $Role -AssignRoleToClientId $AssignRoleToClientId
}

Export-ModuleMember -Function Add-AzADAppRoleAssignment

<#
.Synopsis
Remove a role assignment from an Azure Active Directory Application Registration.
.Description
Remove a role assignment from an Azure Active Directory Application Registration and optionally remove the role if no role assignments are left.
.Parameter ClientId
The client ID of the Azure Active Directory Application Registration on which the role is present.
.Parameter Role
The name of the role to remove the assignment for.
.Parameter RemoveRoleFromClientId
The client ID of the Azure Active Directory Application Registration for which the role assignment will be removed.
.Parameter PassThru
Indicates that the role will be removed from the Azure Active Directory Application Registration if no role assigments are left.
#>
function Remove-AzADAppRoleAssignment {
param(
[Parameter(Mandatory = $true)][string] $ClientId = $(throw "ClientId is required"),
[Parameter(Mandatory = $true)][string] $Role = $(throw "Role is required"),
[Parameter(Mandatory = $true)][string] $RemoveRoleFromClientId = $(throw "ClientId to remove the role from is required"),
[Parameter(Mandatory = $false)][switch] $RemoveRoleIfNoAssignmentsAreLeft = $false
)

if ($RemoveRoleIfNoAssignmentsAreLeft) {
. $PSScriptRoot\Scripts\Remove-AzADAppRoleAssignment.ps1 -ClientId $ClientId -Role $Role -RemoveRoleFromClientId $RemoveRoleFromClientId -RemoveRoleIfNoAssignmentsAreLeft
} else {
. $PSScriptRoot\Scripts\Remove-AzADAppRoleAssignment.ps1 -ClientId $ClientId -Role $Role -RemoveRoleFromClientId $RemoveRoleFromClientId
}
}

Export-ModuleMember -Function Remove-AzADAppRoleAssignment
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{8c0379ad-d886-491f-9e1c-fce0a7d144e8}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>Arcus.Sripting.ActiveDirectory</RootNamespace>
<AssemblyName>Arcus.Sripting.ActiveDirectory</AssemblyName>
<Name>Arcus.Scripting.ActiveDirectory</Name>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Folder Include="Scripts\" />
</ItemGroup>
<ItemGroup>
<Compile Include="Arcus.Scripting.ActiveDirectory.psd1" />
<Compile Include="Arcus.Scripting.ActiveDirectory.psm1" />
<Compile Include="Scripts\Add-AzADAppRoleAssignment.ps1" />
<Compile Include="Scripts\List-AzADAppRoleAssignments.ps1" />
<Compile Include="Scripts\Remove-AzADAppRoleAssignment.ps1" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Target Name="Build" />
<Import Project="$(MSBuildExtensionsPath)\PowerShell Tools for Visual Studio\PowerShellTools.targets" Condition="Exists('$(MSBuildExtensionsPath)\PowerShell Tools for Visual Studio\PowerShellTools.targets')" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
param(
[Parameter(Mandatory = $true)][string] $ClientId = $(throw "ClientId is required"),
[Parameter(Mandatory = $true)][string] $Role = $(throw "Role is required"),
[Parameter(Mandatory = $true)][string] $AssignRoleToClientId = $(throw "ClientId to assign the role to is required")
)

$adApplication = Get-AzADApplication -Filter "AppId eq '$ClientId'"
if (!$adApplication) {
throw "Active Directory Application for the ClientId '$ClientId' could not be found"
}
$adServicePrincipal = Get-AzADServicePrincipal -Filter "AppId eq '$ClientId'"
if (!$adServicePrincipal) {
throw "Active Directory Service Principal for the ClientId '$ClientId' could not be found"
}

$adApplicationRoleAssignTo = Get-AzADApplication -Filter "AppId eq '$AssignRoleToClientId'"
if (!$adApplicationRoleAssignTo) {
throw "Active Directory Application for the ClientId '$AssignRoleToClientId' could not be found"
}
$adServicePrincipalRoleAssignTo = Get-AzADServicePrincipal -Filter "AppId eq '$AssignRoleToClientId'"
if (!$adServicePrincipalRoleAssignTo) {
throw "Active Directory Service Principal for the ClientId '$AssignRoleToClientId' could not be found"
}

try {
if ($adApplication.AppRole.Value -notcontains $Role) {
Write-Host "Active Directory Application '$($adApplication.DisplayName)' does not contain the role '$Role', adding the role"

$newRole = @{
"DisplayName" = $Role
"Description" = $Role
"Value" = $Role
"Id" = [Guid]::NewGuid().ToString()
"IsEnabled" = $true
"allowedMemberTypes" = @("User", "Application")
}

$adApplication.AppRole += $newRole

Update-AzADApplication -ObjectId $adApplication.Id -AppRole $adApplication.AppRole
Write-Host "Added role '$Role' to Active Directory Application '$($adApplication.DisplayName)'"

$currentAppRole = $newRole
} else {
Write-Host "Active Directory Application '$($adApplication.DisplayName)' already contains the role '$Role'"
$currentAppRole = $adApplication.AppRole | Where-Object Value -eq $Role
}

$currentRoleAssignments = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $adServicePrincipal.Id | Where-Object AppRoleId -eq $currentAppRole.Id
if ($currentRoleAssignments.AppRoleId -notcontains $currentAppRole.Id) {
$updatedAdServicePrincipal = Get-MgServicePrincipal -ServicePrincipalId $adServicePrincipal.Id

while ($updatedAdServicePrincipal.AppRoles.Value -notcontains $Role -and $counter -lt 10) {
Write-Host "Role '$Role' has been added to Active Directory Application '$($adApplication.DisplayName)' but not yet available for use, waiting 10 seconds to retry..."
Start-Sleep -Seconds 10
$counter++
$updatedAdServicePrincipal = Get-MgServicePrincipal -ServicePrincipalId $adServicePrincipal.Id
}

if ($counter -eq 10) {
throw "Exhausted the retries, the role '$Role' has been added to Active Directory Application '$($adApplication.DisplayName)' but not yet available for use"
}

$newRoleAssignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $adServicePrincipalRoleAssignTo.Id -PrincipalId $adServicePrincipalRoleAssignTo.Id -ResourceId $adServicePrincipal.Id -AppRoleId $currentAppRole.Id
Write-Host "Role Assignment for the role '$Role' added to the Active Directory Application '$($adApplicationRoleAssignTo.DisplayName)'"
} else {
Write-Host "Active Directory Application '$($adApplicationRoleAssignTo.DisplayName)' already contains a role assignment for the role '$Role'"
}
} catch {
throw "Adding the role '$Role' for the Active Directory Application with ClientId '$ClientId' failed. Details: $($_.Exception.Message)"
}
Loading

0 comments on commit bf1271d

Please sign in to comment.