Skip to content

Commit

Permalink
Merged PR 178: Release TMF v1.5
Browse files Browse the repository at this point in the history
Key features:

- roleManagement
- policies
- group license assignment
- renaming AccessPackages and AccessPackageAssignmentPolicies

Related work items: #144, #661, #725, #778, #852
  • Loading branch information
Roland Wagner committed Jul 20, 2022
2 parents a4d3633 + 46618f2 commit 73cd30d
Show file tree
Hide file tree
Showing 44 changed files with 1,897 additions and 135 deletions.
573 changes: 571 additions & 2 deletions README.md

Large diffs are not rendered by default.

15 changes: 13 additions & 2 deletions TMF/TMF.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,26 @@
'Register-TmfRoleManagementPolicy',
'Test-TmfRoleManagementPolicy',
'Invoke-TmfRoleManagementPolicy',
'Register-TmfRoleManagementPolicyRuleTemplates',
'Register-TmfRoleManagementPolicyRuleTemplate',
'Register-TmfRoleAssignment',
'Test-TmfRoleAssignment',
'Invoke-TmfRoleAssignment',
'Register-TmfRoleDefinition',
'Test-TmfRoleDefinition',
'Invoke-TmfRoleDefinition',
'Test-TmfRoleManagement',
'Invoke-TmfRoleManagement'
'Invoke-TmfRoleManagement',
'Register-TmfAuthenticationFlowsPolicy',
'Test-TmfAuthenticationFlowsPolicy',
'Invoke-TmfAuthenticationFlowsPolicy',
'Register-TmfAuthenticationMethodsPolicy',
'Test-TmfAuthenticationMethodsPolicy',
'Invoke-TmfAuthenticationMethodsPolicy',
'Register-TmfAuthorizationPolicy',
'Test-TmfAuthorizationPolicy',
'Invoke-TmfAuthorizationPolicy',
'Test-TmfPolicy',
'Invoke-TmfPolicy'
)

# Cmdlets to export from this module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ function Register-TmfAccessPackageAssignmentPolicy
Param (
[Parameter(Mandatory = $true)]
[string] $displayName,
[string[]] $oldNames,
[string] $description = "Access Package Assignment Policy has been created with Tenant Management Framework",
[Parameter(Mandatory = $true)]
[string] $accessPackage,
Expand Down Expand Up @@ -48,6 +49,10 @@ function Register-TmfAccessPackageAssignmentPolicy
sourceConfig = $sourceConfig
}

if ($PSBoundParameters.ContainsKey("oldNames")) {
Add-Member -InputObject $object -MemberType NoteProperty -Name "oldNames" -Value @($oldNames | ForEach-Object {Resolve-String $_})
}

"accessReviewSettings", "requestApprovalSettings", "requestorSettings" | ForEach-Object {
if ($PSBoundParameters.ContainsKey($_)) {
if ($script:supportedResources[$resourceName]["validateFunctions"].ContainsKey($_)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,15 @@ function Test-TmfAccessPackageAssignmentPolicy
}

try {
$resource = (Invoke-MgGraphRequest -Method GET -Uri ("$script:graphBaseUrl/identityGovernance/entitlementManagement/accessPackageAssignmentPolicies?`$filter=(displayName eq '{0}') and (accessPackageId eq '{1}')" -f [System.Web.HttpUtility]::UrlEncode($definition.displayName), $accessPackageId)).Value

$resource = (Invoke-MgGraphRequest -Method GET -Uri ("$script:graphBaseUrl/identityGovernance/entitlementManagement/accessPackageAssignmentPolicies?`$filter=(displayname eq '{0}') and (accessPackageId eq '{1}')" -f [System.Web.HttpUtility]::UrlEncode($definition.displayName), $accessPackageId)).Value

if (("oldNames" -in $definition.Properties()) -and (-not($resource))) {
foreach ($oldName in $definition.oldNames) {
$resource = (Invoke-MgGraphRequest -Method GET -Uri ("$script:graphBaseUrl/identityGovernance/entitlementManagement/accessPackageAssignmentPolicies?`$filter=(displayname eq '{0}') and (accessPackageId eq '{1}')" -f [System.Web.HttpUtility]::UrlEncode($oldName), $accessPackageId)).Value
if ($resource) {break}
}
}
}
catch {
Write-PSFMessage -Level Warning -String 'TMF.Error.QueryWithFilterFailed' -StringValues $filter -Tag 'failed'
Expand All @@ -68,7 +76,7 @@ function Test-TmfAccessPackageAssignmentPolicy
$result["GraphResource"] = $resource
if ($definition.present) {
$changes = @()
foreach ($property in ($definition.Properties() | Where-Object {$_ -notin "displayName", "present", "sourceConfig", "accessPackage"})) {
foreach ($property in ($definition.Properties() | Where-Object {$_ -notin "present", "sourceConfig", "accessPackage", "oldNames"})) {
$change = [PSCustomObject] @{
Property = $property
Actions = $null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ function Register-TmfAccessPackage
Param (
[Parameter(Mandatory = $true)]
[string] $displayName,
[string[]] $oldNames,
[string] $description = "Access Package has been created with Tenant Management Framework",
[bool] $isHidden = $false,
[bool] $isRoleScopesVisible = $true,
Expand Down Expand Up @@ -45,6 +46,11 @@ function Register-TmfAccessPackage
present = $present
sourceConfig = $sourceConfig
}

if ($PSBoundParameters.ContainsKey("oldNames")) {
Add-Member -InputObject $object -MemberType NoteProperty -Name "oldNames" -Value @($oldNames | ForEach-Object {Resolve-String $_})
}

Add-Member -InputObject $object -MemberType ScriptMethod -Name Properties -Value { ($this | Get-Member -MemberType NoteProperty).Name }

foreach ($policy in $assignmentPolicies) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,14 @@ function Test-TmfAccessPackage
}

try {
$resource = (Invoke-MgGraphRequest -Method GET -Uri ("$script:graphBaseUrl/identityGovernance/entitlementManagement/accessPackages?`$filter=displayName eq '{0}'&`$expand=accessPackageResourceRoleScopes(`$expand=accessPackageResourceRole,accessPackageResourceScope)" -f [System.Web.HttpUtility]::UrlEncode($definition.displayName))).Value
$resource = (Invoke-MgGraphRequest -Method GET -Uri ("$script:graphBaseUrl/identityGovernance/entitlementManagement/accessPackages?`$filter=(displayName eq '{0}')&`$expand=accessPackageResourceRoleScopes(`$expand=accessPackageResourceRole,accessPackageResourceScope)" -f [System.Web.HttpUtility]::UrlEncode($definition.displayName))).Value

if (("oldNames" -in $definition.Properties()) -and (-not ($resource))) {
foreach ($oldName in $definition.oldNames) {
$resource = (Invoke-MgGraphRequest -Method GET -Uri ("$script:graphBaseUrl/identityGovernance/entitlementManagement/accessPackages?`$filter=(displayName eq '{0}')&`$expand=accessPackageResourceRoleScopes(`$expand=accessPackageResourceRole,accessPackageResourceScope)" -f [System.Web.HttpUtility]::UrlEncode($oldName))).Value
if ($resource) {break}
}
}
}
catch {
Write-PSFMessage -Level Warning -String 'TMF.Error.QueryWithFilterFailed' -StringValues $filter -Tag 'failed'
Expand All @@ -99,7 +106,7 @@ function Test-TmfAccessPackage
$result["GraphResource"] = $resource
if ($definition.present) {
$changes = @()
foreach ($property in ($definition.Properties() | Where-Object {$_ -notin "displayName", "present", "sourceConfig"})) {
foreach ($property in ($definition.Properties() | Where-Object {$_ -notin "present", "sourceConfig", "oldNames"})) {
$change = [PSCustomObject] @{
Property = $property
Actions = $null
Expand Down
11 changes: 8 additions & 3 deletions TMF/functions/general/Get-TmfRequiredScope.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
[Parameter(ParameterSetName = "SpecifiedComponents")]
[switch] $AccessReviews,
[Parameter(ParameterSetName = "SpecifiedComponents")]
[switch] $RoleAssignments,
[switch] $RoleManagement,
[Parameter(ParameterSetName = "SpecifiedComponents")]
[switch] $Groups,
[Parameter(ParameterSetName = "SpecifiedComponents")]
Expand All @@ -59,6 +59,8 @@
[switch] $EntitlementManagement,
[Parameter(ParameterSetName = "SpecifiedComponents")]
[switch] $AdministrativeUnits,
[Parameter(ParameterSetName = "SpecifiedComponents")]
[switch] $Policies,
[Parameter(ParameterSetName = "All")]
[switch] $All
)
Expand All @@ -72,8 +74,8 @@
if($AccessReviews -or $All) {
$scopes += "Group.Read.All", "AccessReview.ReadWrite.All", "RoleManagement.Read.Directory", "Directory.Read.All", "Directory.AccessAsUser.All"
}
if($RoleAssignments -or $All) {
$scopes += "RoleManagement.ReadWrite.Directory", "Directory.AccessAsUser.All"
if($RoleManagement -or $All) {
$scopes += "RoleManagement.ReadWrite.Directory", "Directory.AccessAsUser.All", "RoleEligibilitySchedule.ReadWrite.Directory", "RoleAssignmentSchedule.ReadWrite.Directory", "RoleManagementPolicy.ReadWrite.Directory"
}
if ($Groups -or $All) {
$scopes += "Group.ReadWrite.All", "GroupMember.ReadWrite.All", "Directory.ReadWrite.All", "Directory.AccessAsUser.All"
Expand All @@ -96,6 +98,9 @@
if ($AdministrativeUnits -or $All) {
$scopes += "AdministrativeUnit.ReadWrite.All", "Directory.AccessAsUser.All", "RoleManagement.ReadWrite.Directory"
}
if ($Policies -or $All) {
$scopes += "Policy.ReadWrite.AuthenticationMethod", "Policy.ReadWrite.Authorization", "Policy.ReadWrite.AuthenticationFlows"
}
return ($scopes | Sort-Object -Unique)
}
}
54 changes: 47 additions & 7 deletions TMF/functions/groups/Invoke-TmfGroup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
}

try {
@("mailNickname", "groupTypes", "mailEnabled", "isAssignableToRole", "securityEnabled", "hideFromAddressLists", "hideFromOutlookClients", "resourceBehaviorOptions") | ForEach-Object {
@("mailNickname", "groupTypes", "mailEnabled", "isAssignableToRole", "securityEnabled", "resourceBehaviorOptions") | ForEach-Object {
if ($result.DesiredConfiguration.Properties() -contains $_) {
$requestBody[$_] = $result.DesiredConfiguration.$_
}
Expand All @@ -62,21 +62,47 @@

$requestBody = $requestBody | ConvertTo-Json -ErrorAction Stop
Write-PSFMessage -Level Verbose -String "TMF.Invoke.SendingRequestWithBody" -StringValues $requestMethod, $requestUrl, $requestBody
Invoke-MgGraphRequest -Method $requestMethod -Uri $requestUrl -Body $requestBody | Out-Null
$resource = Invoke-MgGraphRequest -Method $requestMethod -Uri $requestUrl -Body $requestBody

if ($result.DesiredConfiguration.Properties() -contains "hideFromOutlookClients" -or $result.DesiredConfiguration.Properties() -contains "hideFromAddressLists") {
$requestMethod = "PATCH"
$requestUrl = "$script:graphBaseUrl/groups/{0}" -f $resource.id
$requestBody = @{}
@("hideFromOutlookClients", "hideFromAddressLists") | Foreach-Object {
if ($result.DesiredConfiguration.Properties() -contains $_) {
$requestBody[$_] = $result.DesiredConfiguration.$_
}
}
$requestBody = $requestBody | ConvertTo-Json -ErrorAction Stop
Start-Sleep -Seconds 10 # Wait for group creation
Write-PSFMessage -Level Verbose -String "TMF.Invoke.SendingRequestWithBody" -StringValues $requestMethod, $requestUrl, $requestBody
Invoke-MgGraphRequest -Method $requestMethod -Uri $requestUrl -Body $requestBody | Out-Null
}

if ($result.DesiredConfiguration.Properties() -contains "privilegedAccess") {
if ($result.DesiredConfiguration.privilegedAccess) {
$groupId = (Invoke-MgGraphRequest -Method "GET" -Uri "$script:graphBaseUrl/groups?`$filter=displayName eq '$($result.DesiredConfiguration.displayName)'").value.Id
if ($result.DesiredConfiguration.privilegedAccess) {
$requestMethod = "POST"
$requestUrl = "$script:graphBaseUrl/privilegedAccess/aadGroups/resources/register"
$requestBody = @{
"externalId" = $groupId
"externalId" = $resource.id
}
$requestBody = $requestBody | ConvertTo-Json -ErrorAction Stop
Write-PSFMessage -Level Verbose -String "TMF.Invoke.SendingRequestWithBody" -StringValues $requestMethod, $requestUrl, $requestBody
Invoke-MgGraphRequest -Method $requestMethod -Uri $requestUrl -Body $requestBody | Out-Null
}
}

if ($result.DesiredConfiguration.Properties() -contains "assignedLicenses") {
$requestMethod = "POST"
$requestUrl = "$script:graphBaseUrl/groups/{0}/assignLicense" -f $resource.id
$requestBody = @{
"addLicenses" = @($result.DesiredConfiguration.assignedLicenses)
"removeLicenses" = @()
}
$requestBody = $requestBody | ConvertTo-Json -ErrorAction Stop -Depth 3
Write-PSFMessage -Level Verbose -String "TMF.Invoke.SendingRequestWithBody" -StringValues $requestMethod, $requestUrl, $requestBody
Invoke-MgGraphRequest -Method $requestMethod -Uri $requestUrl -Body $requestBody | Out-Null
}
}
catch {
Write-PSFMessage -Level Error -String "TMF.Invoke.ActionFailed" -StringValues $result.Tenant, $result.ResourceType, $result.ResourceName, $result.ActionType
Expand Down Expand Up @@ -148,6 +174,20 @@
}
}
}
"assignedLicenses" {
$requestMethod = "POST"
$requestUrl = "$script:graphBaseUrl/groups/{0}/assignLicense" -f $result.GraphResource.Id
$requestBody = @{
"addLicenses" = @()
"removeLicenses" = @()
}
if ($change.Actions["Add"]) { $requestBody["addLicenses"] = @($change.Actions["Add"]) }
if ($change.Actions["Remove"]) { $requestBody["removeLicenses"] = @($change.Actions["Remove"]) }

$requestBody = $requestBody | ConvertTo-Json -ErrorAction Stop -Depth 3
Write-PSFMessage -Level Verbose -String "TMF.Invoke.SendingRequestWithBody" -StringValues $requestMethod, $requestUrl, $requestBody
Invoke-MgGraphRequest -Method $requestMethod -Uri $requestUrl -Body $requestBody | Out-Null
}
"privilegedAccess" {
$requestMethod = "POST"
$requestUrl = "$script:graphBaseUrl/privilegedAccess/aadGroups/resources/register"
Expand All @@ -163,9 +203,9 @@
switch ($action) {
"Set" { $requestBody[$change.Property] = $change.Actions[$action] }
}
}
}
}
}
}
}

if ($requestBody.Keys -gt 0) {
Expand Down
8 changes: 8 additions & 0 deletions TMF/functions/groups/Register-TmfGroup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function Register-TmfGroup
[bool] $privilegedAccess,
[bool] $hideFromAddressLists,
[bool] $hideFromOutlookClients,
[object[]] $assignedLicenses,
[bool] $present = $true,
[string] $sourceConfig = "<Custom>",

Expand Down Expand Up @@ -80,6 +81,13 @@ function Register-TmfGroup
Add-Member -InputObject $object -MemberType NoteProperty -Name $_ -Value $PSBoundParameters[$_]
}
}

if ($PSBoundParameters.ContainsKey("assignedLicenses")) {
$assignedLicenses = @($assignedLicenses | Foreach-Object {
Validate-AssignedLicense -skuId $_.skuId -disabledPlans $_.disabledPlans
})
Add-Member -InputObject $object -MemberType NoteProperty -Name "assignedLicenses" -Value $assignedLicenses
}

Add-Member -InputObject $object -MemberType ScriptMethod -Name Properties -Value { ($this | Get-Member -MemberType NoteProperty).Name }

Expand Down
18 changes: 15 additions & 3 deletions TMF/functions/groups/Test-TmfGroup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@
else {
$filter = "(displayName eq '{0}')" -f [System.Web.HttpUtility]::UrlEncode($definition.displayName)
}

$select = ($definition.Properties() | Where-Object {$_ -notin "hideFromOutlookClients", "hideFromAddressLists", "privilegedAccess", "members", "oldNames", "present", "sourceConfig"})
$select += "id"
try {
$resource = (Invoke-MgGraphRequest -Method GET -Uri ("$script:graphBaseUrl/groups/?`$filter={0}" -f $filter)).Value
$resource = (Invoke-MgGraphRequest -Method GET -Uri ("$script:graphBaseUrl/groups/?`$filter={0}&`$select={1}" -f $filter, ($select -join ","))).Value
}
catch {
Write-PSFMessage -Level Warning -String 'TMF.Error.QueryWithFilterFailed' -StringValues $filter -Tag 'failed'
Expand Down Expand Up @@ -131,7 +134,7 @@
Property = "membershipRuleProcessingState"
Actions = @{"Set" = "On"}
}
}
}
}
"groupTypes" {
if ($resource.groupTypes) {
Expand All @@ -146,13 +149,22 @@
}
}
"resourceBehaviorOptions" {<# Is only used while creation of a group. #>}
{$_ -in @("hideFromAddressLists", "hideFromOutlookClients")} {
$tempResource = Invoke-MgGraphRequest -Method GET -Uri ("$script:graphBaseUrl/groups/{0}" -f $resource.id)
if ($definition.$property -ne $tempResource.$property) {
$change.Actions = @{"Set" = $definition.$property}
}
}
"privilegedAccess" {
if ($definition.$property) {
if (-not ((Invoke-MgGraphRequest -Method GET -Uri "$($script:graphBaseUrl)/privilegedAccess/aadGroups/resources?`$filter=id eq '$($resource.Id)'").value)) {
if (-Not (Invoke-MgGraphRequest -Method GET -Uri "$($script:graphBaseUrl)/privilegedAccess/aadGroups/resources?`$filter=id eq '$($resource.Id)'").value) {
$change.Actions = @{"Set" = "Activate"}
}
}
}
"assignedLicenses" {
$change.Actions = Compare-AssignedLicenses -ReferenceList $resource.$property -DifferenceList $definition.$property -Cmdlet $PSCmdlet
}
default {
if ($definition.$property -ne $resource.$property) {
$change.Actions = @{"Set" = $definition.$property}
Expand Down
41 changes: 41 additions & 0 deletions TMF/functions/policies/Invoke-TmfPolicy.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
function Invoke-TmfPolicy
{
<#
.SYNOPSIS
Performs the required actions for a resource type against the connected Tenant.
.DESCRIPTION
This command combines the Invoke commands of all Policy resources.
authenticationFlowsPolicies, authenticationMethodsPolicies, authorizationPolicies
#>
Param (
[switch] $DoNotRequireTenantConfirm
)

begin
{
Test-GraphConnection -Cmdlet $PSCmdlet
$tenant = Get-MgOrganization -Property displayName, Id
$policyResources = @("authenticationFlowsPolicies", "authenticationMethodsPolicies", "authorizationPolicies")
}
process
{
Write-PSFMessage -Level Host -FunctionName "Invoke-TmfPolicies" -String "TMF.TenantInformation" -StringValues $tenant.displayName, $tenant.Id
if (-Not $DoNotRequireTenantConfirm) {
if ((Read-Host "Is this the correct tenant? [y/n]") -notin @("y","Y")) {
Write-PSFMessage -Level Error -String "TMF.UserCanceled"
throw "Connected to the wrong tenant."
}
}

foreach ($resourceType in ($script:supportedResources.GetEnumerator() | Where-Object {$_.Value.invokeFunction -and $_.Name -in $policyResources} | Sort-Object {$_.Value.weight})) {
if ($script:desiredConfiguration[$resourceType.Name]) {
Write-PSFMessage -Level Host -FunctionName "Invoke-TmfPolicies" -String "TMF.StartingInvokeForResource" -StringValues $resourceType.Name
& $resourceType.Value["invokeFunction"] -Cmdlet $PSCmdlet
}
}
}
end
{

}
}
26 changes: 26 additions & 0 deletions TMF/functions/policies/Test-TmfPolicy.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
function Test-TmfPolicy
{
[CmdletBinding()]
Param ()

begin
{
Test-GraphConnection -Cmdlet $PSCmdlet
$tenant = Get-MgOrganization -Property displayName, Id
$policyResources = @("authenticationFlowsPolicies", "authenticationMethodsPolicies", "authorizationPolicies")
}
process
{
Write-PSFMessage -Level Host -FunctionName "Test-TmfPolicy" -String "TMF.TenantInformation" -StringValues $tenant.displayName, $tenant.Id
foreach ($resourceType in ($script:supportedResources.GetEnumerator() | Where-Object {$_.Value.testFunction -and $_.Name -in $policyResources} | Sort-Object {$_.Value.weight})) {
if ($script:desiredConfiguration[$resourceType.Name]) {
Write-PSFMessage -Level Host -FunctionName "Test-TmfPolicy" -String "TMF.StartingTestForResource" -StringValues $resourceType.Name
& $resourceType.Value["testFunction"] -Cmdlet $PSCmdlet | Beautify-TmfTestResult
}
}
}
end
{

}
}
Loading

0 comments on commit 73cd30d

Please sign in to comment.