Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AADGroup: Implements #2301 #2316

Merged
merged 11 commits into from
Sep 29, 2022
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

# UNRELEASED

* AADGroup
* Added properties MemberOf and AssignedToRole
Implements [#2301](https://github.com/microsoft/Microsoft365DSC/issues/2301)
* EXOMailContact
* Initial Release.
* EXOMailTips
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ function Get-TargetResource
[System.String[]]
$Members,

[Parameter()]
[System.String[]]
$MemberOf,

[Parameter()]
[System.String]
$Description,
Expand Down Expand Up @@ -53,6 +57,10 @@ function Get-TargetResource
[System.Boolean]
$IsAssignableToRole,

[Parameter()]
[System.String[]]
$AssignedToRole,

[Parameter()]
[ValidateSet('Public', 'Private', 'HiddenMembership')]
[System.String]
Expand Down Expand Up @@ -150,7 +158,7 @@ function Get-TargetResource
Write-Verbose -Message 'Found existing AzureAD Group'

# Owners
[Array]$owners = Get-MgGroupOwner -GroupId $Group.Id -All:$true
[Array]$owners = Get-MgGroupOwner -GroupId $Group.Id
$OwnersValues = @()
foreach ($owner in $owners)
{
Expand All @@ -164,7 +172,7 @@ function Get-TargetResource
if ($Group.MembershipRuleProcessingState -ne 'On')
{
# Members
[Array]$members = Get-MgGroupMember -GroupId $Group.Id -All:$true
[Array]$members = Get-MgGroupMember -GroupId $Group.Id
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we removing the -All here? It should be included to make sure we catch all members

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-ALL is apparently no longer supported by Get-MgGroup* cmdlets in v1.12.0 of the Microsoft.Graph.Groups module.
Running the unit-tests with -All parameters resulted in a number of 'caught throws' where the text in the corresponding M365DSC eventlog stated that "A parameter cannot be found that matches parameter name 'All'". The only cmdlets in the Get-TargetResource function that used -All were the Get-MgGroup* cmdlets. Removing -All everywhere resulted in a passed unit-test.

The question of whether this should have been brought to the attention of the folks responsible for Graph-modules is another matter. I guess you could have gotten them to update the relevant module in a day or two as something similar has happened before, but I really just wanted to get the change under wraps.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was an intermediate release. Graph modules at up to 1.12.1 now and do allow the -all parameter

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See e01d15b

$MembersValues = @()
foreach ($member in $members)
{
Expand All @@ -175,7 +183,36 @@ function Get-TargetResource
}
}

# MemberOf
[Array]$memberOf = Get-MgGroupMemberOf -GroupId $Group.Id # result also used for/by AssignedToRole
$MemberOfValues = @()
# Note: only process security-groups that this group is a member of and not directory roles (if any)
foreach ($member in ($memberOf | Where-Object -FilterScript {$_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.group"}))
{
if ($null -ne $member.AdditionalProperties.displayName)
{
$MemberOfValues += $member.AdditionalProperties.displayName
}
}

# AssignedToRole
$AssignedToRoleValues = $null
if ($Group.IsAssignableToRole -eq $true)
{
$AssignedToRoleValues = @()
# Note: only process directory roles and not group membership (if any)
foreach ($role in $($memberOf | Where-Object -FilterScript {$_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.directoryRole"}))
{
if ($null -ne $role.AdditionalProperties.displayName)
{
$AssignedToRoleValues += $role.AdditionalProperties.displayName
}
}
}


# Licenses
$assignedLicensesValues = $null
$assignedLicensesRequest = Invoke-MgGraphRequest -Method 'GET' `
-Uri "https://graph.microsoft.com/v1.0/groups/$($Group.Id)/assignedLicenses"

Expand All @@ -190,13 +227,15 @@ function Get-TargetResource
Id = $Group.Id
Owners = $OwnersValues
Members = $MembersValues
MemberOf = $MemberOfValues
Description = $Group.Description
GroupTypes = [System.String[]]$Group.GroupTypes
MembershipRule = $Group.MembershipRule
MembershipRuleProcessingState = $Group.MembershipRuleProcessingState
SecurityEnabled = $Group.SecurityEnabled
MailEnabled = $Group.MailEnabled
IsAssignableToRole = $Group.IsAssignableToRole
AssignedToRole = $AssignedToRoleValues
MailNickname = $Group.MailNickname
Visibility = $Group.Visibility
AssignedLicenses = $assignedLicensesValues
Expand Down Expand Up @@ -259,6 +298,10 @@ function Set-TargetResource
[System.String[]]
$Members,

[Parameter()]
[System.String[]]
$MemberOf,

[Parameter()]
[System.String]
$Description,
Expand Down Expand Up @@ -288,6 +331,10 @@ function Set-TargetResource
[System.Boolean]
$IsAssignableToRole,

[Parameter()]
[System.string[]]
$AssignedToRole,

[Parameter()]
[ValidateSet('Public', 'Private', 'HiddenMembership')]
[System.String]
Expand Down Expand Up @@ -352,8 +399,12 @@ function Set-TargetResource
$currentParameters.Remove('ManagedIdentity') | Out-Null
$backCurrentOwners = $currentGroup.Owners
$backCurrentMembers = $currentGroup.Members
$backCurrentMemberOf = $currentGroup.MemberOf
$backCurrentAssignedToRole = $currentGroup.AssignedToRole
$currentParameters.Remove('Owners') | Out-Null
$currentParameters.Remove('Members') | Out-Null
$currentParameters.Remove('MemberOf') | Out-Null
$currentParameters.Remove('AssignedToRole') | Out-Null

if ($Ensure -eq 'Present' -and `
($null -ne $GroupTypes -and $GroupTypes.Contains('Unified')) -and `
Expand Down Expand Up @@ -607,6 +658,122 @@ function Set-TargetResource
{
Write-Verbose -Message 'Ignoring membership since this is a dynamic group.'
}

#MemberOf
$currentMemberOfValue = @()
if ($currentParameters.MemberOf.Length -ne 0)
{
$currentMemberOfValue = $backCurrentMemberOf
}
$desiredMemberOfValue = @()
if ($MemberOf.Length -ne 0)
{
$desiredMemberOfValue = $MemberOf
}
if ($null -eq $backCurrentMemberOf)
{
$backCurrentMemberOf = @()
}
$memberOfDiff = Compare-Object -ReferenceObject $backCurrentMemberOf -DifferenceObject $desiredMemberOfValue
foreach ($diff in $memberOfDiff)
{
try
{
$memberOfGroup = Get-MgGroup -Filter "DisplayName -eq '$($diff.InputObject)'" -ErrorAction Stop
}
catch
{
$memberOfGroup = $null
}
if ($null -eq $memberOfGroup)
{
throw "Security-group or directory role '$($diff.InputObject)' does not exist"
}
else
{
if ($diff.SideIndicator -eq '=>')
{
# see if memberOfGroup contains property SecurityEnabled (it can be true or false)
if ($memberOfgroup.psobject.Typenames -match 'Group')
{
Write-Verbose -Message "Adding AAD group {$($currentGroup.DisplayName)} as member of AAD group {$($memberOfGroup.DisplayName)}"
#$memberOfObject = @{
# "@odata.id"= "https://graph.microsoft.com/v1.0/groups/{$($group.Id)}"
#}
New-MgGroupMember -GroupId ($memberOfGroup.Id) -DirectoryObject ($currentGroup.Id) | Out-Null
}
else
{
Throw "Cannot add AAD group {$($currentGroup.DisplayName)} to {$($memberOfGroup.DisplayName)} as it is not a security-group"
}

}
elseif ($diff.SideIndicator -eq '<=')
{
if ($memberOfgroup.psobject.Typenames -match 'Group')
{
Write-Verbose -Message "Removing AAD Group {$($currentGroup.DisplayName)} from AAD group {$($memberOfGroup.DisplayName)}"
Remove-MgGroupMemberByRef -GroupId ($memberOfGroup.Id) -DirectoryObjectId ($currentGroup.Id) |Out-Null
}
else
{
Throw "Cannot remove AAD group {$($currentGroup.DisplayName)} from {$($memberOfGroup.DisplayName)} as it is not a security-group"
}
}
}
}

if ($currentGroup.IsAssignableToRole -eq $true)
{
#AssignedToRole
$currentAssignedToRoleValue = @()
if ($currentParameters.AssignedToRole.Length -ne 0)
{
$currentAssignedToRoleValue = $backCurrentAssignedToRole
}
$desiredAssignedToRoleValue = @()
if ($AssignedToRole.Length -ne 0)
{
$desiredAssignedToRoleValue = $AssignedToRole
}
if ($null -eq $backCurrentAssignedToRole)
{
$backCurrentAssignedToRole = @()
}
$assignedToRoleDiff = Compare-Object -ReferenceObject $backCurrentAssignedToRole -DifferenceObject $desiredAssignedToRoleValue
foreach ($diff in $assignedToRoleDiff)
{
try
{
$role = Get-MgDirectoryRole -Filter "DisplayName -eq '$($diff.InputObject)'" -ErrorAction Stop
}
catch
{
$role = $null
}
if ($null -eq $role)
{
throw "Directory Role '$($diff.InputObject)' does not exist or is not enabled"
}
else
{
if ($diff.SideIndicator -eq '=>')
{
Write-Verbose -Message "Assigning AAD group {$($currentGroup.DisplayName)} to Directory Role {$($diff.InputObject)}"
$DirObject = @{
"@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($currentGroup.Id)"
}
New-MgDirectoryRoleMemberByRef -DirectoryRoleId ($role.Id) -BodyParameter $DirObject | Out-null

}
elseif ($diff.SideIndicator -eq '<=')
{
Write-Verbose -Message "Removing AAD group {$($currentGroup.DisplayName)} from Directory Role {$($role.DisplayName)}"
Remove-MgDirectoryRoleMemberByRef -DirectoryRoleId ($role.Id) -DirectoryObjectId ($currentGroup.Id) | Out-null
}
}
}
}
}
}

Expand Down Expand Up @@ -636,6 +803,10 @@ function Test-TargetResource
[System.String[]]
$Members,

[Parameter()]
[System.String[]]
$MemberOf,

[Parameter()]
[System.String]
$Description,
Expand Down Expand Up @@ -665,6 +836,10 @@ function Test-TargetResource
[System.Boolean]
$IsAssignableToRole,

[Parameter()]
[System.String[]]
$AssignedToRole,

[Parameter()]
[ValidateSet('Public', 'Private', 'HiddenMembership')]
[System.String]
Expand Down Expand Up @@ -842,7 +1017,7 @@ function Export-TargetResource

try
{
[array] $groups = Get-MgGroup -Filter $Filter -All:$true -ErrorAction Stop
[array] $groups = Get-MgGroup -Filter $Filter -ErrorAction Stop
$i = 1
$dscContent = ''
Write-Host "`r`n" -NoNewline
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ class MSFT_AADGroup : OMI_BaseResource
[Write, Description("Specifies an ID for the group.")] String Id;
[Write, Description("User Service Principal values for the group's owners.")] String Owners[];
[Write, Description("User Service Principal values for the group's members.")] String Members[];
[Write, Description("DisplayName values for the groups that this group is a member of.")] String MemberOf[];
[Write, Description("Specifies that the group is a dynamic group. To create a dynamic group, specify a value of DynamicMembership.")] String GroupTypes[];
[Write, Description("Specifies the membership rule for a dynamic group.")] String MembershipRule;
[Write, Description("Specifies the rule processing state. The acceptable values for this parameter are: On. Process the group rule or Paused. Stop processing the group rule."), ValueMap{"On","Paused"}, Values{"On","Paused"}] String MembershipRuleProcessingState;
[Write, Description("Specifies whether the group is security enabled. For security groups, this value must be $True.")] Boolean SecurityEnabled;
[Write, Description("Specifies whether this group is mail enabled. Currently, you cannot create mail enabled groups in Azure AD.")] Boolean MailEnabled;
[Write, Description("Specifies whether this group can be assigned a role. Only available when creating a group and can't be modified after group is created.")] Boolean IsAssignableToRole;
[Write, Description("DisplayName values for the roles that the group is assigned to.")] String AssignedToRole[];
[Write, Description("This parameter determines the visibility of the group's content and members list."), ValueMap{"Public","Private","HiddenMembership"}, Values{"Public","Private","HiddenMembership"}] String Visibility;
[Write, Description("List of Licenses assigned to the group."),EmbeddedInstance("MSFT_AADGroupLicense")] String AssignedLicenses[];
[Write, Description("Specify if the Azure AD Group should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<#
This example is used to test new resources and showcase the usage of new resources being worked on.
It is not meant to use as a production baseline.
#>

Configuration Example
{
param(
[Parameter(Mandatory = $true)]
[PSCredential]
$credsGlobalAdmin
)
Import-DscResource -ModuleName Microsoft365DSC

node localhost
{
AADGroup 'MyGroups'
{
DisplayName = "DSCGroup"
Description = "Microsoft DSC Group"
SecurityEnabled = $True
MailEnabled = $False
GroupTypes = @()
MailNickname = "DSCGroup"
Ensure = "Present"
IsAssignableToRole = $True
AssignedToRole = "Identity Governance Administrator"
Credential = $credsGlobalAdmin
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<#
This example is used to test new resources and showcase the usage of new resources being worked on.
It is not meant to use as a production baseline.
#>

Configuration Example
{
param(
[Parameter(Mandatory = $true)]
[PSCredential]
$credsGlobalAdmin
)
Import-DscResource -ModuleName Microsoft365DSC

node localhost
{
AADGroup 'MyGroups1'
{
DisplayName = "DSCGroup"
Description = "Microsoft DSC Group"
SecurityEnabled = $True
GroupTypes = @()
MailNickname = "M365DSCG"
Ensure = "Present"
Credential = $credsGlobalAdmin
}
AADGroup 'MyGroups2'
{
DisplayName = "DSCMemberGroup"
Description = "Microsoft DSC Editor"
SecurityEnabled = $True
GroupTypes = @()
MailNickname = "M365DSCMG"
Ensure = "Present"
MemberOf = @("DSCGroup")
Credential = $credsGlobalAdmin
}
}
}
Loading