diff --git a/CHANGELOG.md b/CHANGELOG.md index e496cf335e..95bafcf5ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ * Renamed from O365User * Added support for Roles. FIXES [#2288](https://github.com/microsoft/Microsoft365DSC/issues/2288) +* AADGroup + * Added properties MemberOf and AssignedToRole + Implements [#2301](https://github.com/microsoft/Microsoft365DSC/issues/2301) * EXOATPPolicyForO365 * [BREAKING] Removed the deprecated BlockURLs, AllowClickThrough, EnableSafeLinksForO365Clients and TrackClicks parameters. * EXOMailContact diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.psm1 index f4f553556e..cbd5c7d18a 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.psm1 @@ -24,6 +24,10 @@ function Get-TargetResource [System.String[]] $Members, + [Parameter()] + [System.String[]] + $MemberOf, + [Parameter()] [System.String] $Description, @@ -53,6 +57,10 @@ function Get-TargetResource [System.Boolean] $IsAssignableToRole, + [Parameter()] + [System.String[]] + $AssignedToRole, + [Parameter()] [ValidateSet('Public', 'Private', 'HiddenMembership')] [System.String] @@ -175,7 +183,36 @@ function Get-TargetResource } } + # MemberOf + [Array]$memberOf = Get-MgGroupMemberOf -GroupId $Group.Id -All # 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" @@ -190,6 +227,7 @@ function Get-TargetResource Id = $Group.Id Owners = $OwnersValues Members = $MembersValues + MemberOf = $MemberOfValues Description = $Group.Description GroupTypes = [System.String[]]$Group.GroupTypes MembershipRule = $Group.MembershipRule @@ -197,6 +235,7 @@ function Get-TargetResource SecurityEnabled = $Group.SecurityEnabled MailEnabled = $Group.MailEnabled IsAssignableToRole = $Group.IsAssignableToRole + AssignedToRole = $AssignedToRoleValues MailNickname = $Group.MailNickname Visibility = $Group.Visibility AssignedLicenses = $assignedLicensesValues @@ -259,6 +298,10 @@ function Set-TargetResource [System.String[]] $Members, + [Parameter()] + [System.String[]] + $MemberOf, + [Parameter()] [System.String] $Description, @@ -288,6 +331,10 @@ function Set-TargetResource [System.Boolean] $IsAssignableToRole, + [Parameter()] + [System.string[]] + $AssignedToRole, + [Parameter()] [ValidateSet('Public', 'Private', 'HiddenMembership')] [System.String] @@ -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 ` @@ -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 + } + } + } + } } } @@ -636,6 +803,10 @@ function Test-TargetResource [System.String[]] $Members, + [Parameter()] + [System.String[]] + $MemberOf, + [Parameter()] [System.String] $Description, @@ -665,6 +836,10 @@ function Test-TargetResource [System.Boolean] $IsAssignableToRole, + [Parameter()] + [System.String[]] + $AssignedToRole, + [Parameter()] [ValidateSet('Public', 'Private', 'HiddenMembership')] [System.String] diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.schema.mof index 3fb92e54d9..c9a76cf899 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADGroup/MSFT_AADGroup.schema.mof @@ -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; diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADGroup/1-ConfigureAADMSGroups.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADGroup/1-ConfigureAADGroups.ps1 similarity index 100% rename from Modules/Microsoft365DSC/Examples/Resources/AADGroup/1-ConfigureAADMSGroups.ps1 rename to Modules/Microsoft365DSC/Examples/Resources/AADGroup/1-ConfigureAADGroups.ps1 diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADGroup/2-ConfigureAADGroups.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADGroup/2-ConfigureAADGroups.ps1 new file mode 100644 index 0000000000..8ff211e7be --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADGroup/2-ConfigureAADGroups.ps1 @@ -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 + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADGroup/3-ConfigureAADGroups.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADGroup/3-ConfigureAADGroups.ps1 new file mode 100644 index 0000000000..be2e268fd7 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADGroup/3-ConfigureAADGroups.ps1 @@ -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 + } + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADGroup.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADGroup.Tests.ps1 index e7cfc14d88..a384823fbc 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADGroup.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADGroup.Tests.ps1 @@ -41,6 +41,22 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } + Mock -CommandName Get-MgGroupMember -MockWith { + + } + + Mock -CommandName Get-MgGroupMemberOf -MockWith { + + } + + Mock -CommandName Get-MgGroupOwner -MockWith { + + } + + Mock -CommandName Invoke-MgGraphRequest -MockWith { + + } + Mock -CommandName Update-MgGroup -MockWith { } @@ -52,6 +68,30 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Mock -CommandName New-MgGroup -MockWith { } + + Mock -CommandName New-MgGroupOwnerByRef -MockWith { + + } + + Mock -CommandName New-MgGroupMember -MockWith { + + } + + Mock -CommandName New-MgDirectoryRoleMemberByRef -MockWith { + + } + + Mock -CommandName Remove-MgGroupOwnerByRef -MockWith { + + } + + Mock -CommandName Remove-MgGroupMemberByRef -MockWith { + + } + + Mock -CommandName Remove-MgDirectoryRoleMemberByRef -MockWith { + + } } # Test contexts Context -Name "The Group should exist but it DOES NOT" -Fixture { @@ -109,8 +149,8 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Mock -CommandName Get-MgGroup -MockWith { return @{ - DisplayName = "DSCGroup" - ID = "12345-12345-12345-12345-12345" + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345-12345" } } } @@ -120,7 +160,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Should -Invoke -CommandName "Get-MgGroup" -Exactly 1 } - It 'Should return true from the Test method' { + It 'Should return false from the Test method' { Test-TargetResource @testParams | Should -Be $false } @@ -129,6 +169,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Should -Invoke -CommandName "Remove-MgGroup" -Exactly 1 } } + Context -Name "The Group Exists and Values are already in the desired state" -Fixture { BeforeAll { $testParams = @{ @@ -159,7 +200,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { MailNickname = "M365DSC" GroupTypes = @("Unified") Visibility = "Private" - Ensure = "Present" } } } @@ -174,6 +214,122 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } } + Context -Name "The Group Exists and is a member of another group. Values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345" + Description = "Microsoft DSC Group" + SecurityEnabled = $True + GroupTypes = @() + MailNickname = "M365DSC" + MemberOf = "DSCMemberOfGroup" + Ensure = "Present" + Credential = $Credential; + } + + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credential" + } + + Mock -CommandName Get-MgGroup -ParameterFilter {$Id -eq "12345-12345-12345-12345" -or $Filter -eq "DisplayName eq 'DSCGroup'"} -MockWith { + return @{ + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345" + Description = "Microsoft DSC Group" + SecurityEnabled = $True + MailNickname = "M365DSC" + GroupTypes = @() + } + } + Mock -CommandName Get-MgGroupMemberOf -MockWith { + return @{ + AdditionalProperties = @{ + '@odata.type' = "#microsoft.graph.group" + displayName = "DSCMemberOfGroup" + } + } + } + Mock -CommandName Get-MgGroup -ParameterFilter {$Id -eq '67890-67890-67890-67890' -or $Filter -eq "DisplayName -eq 'DSCMemberOfGroup'"} -MockWith { + $returnData = @{ + DisplayName = "DSCMemberOfGroup" + ID = "67890-67890-67890-67890" + Description = "Microsoft DSC MemberOf Group" + SecurityEnabled = $True + GroupTypes = @() + MailEnabled = $False + MailNickname = "M365DSCM" + } + # Set-TargetResource expects data-type of answer to contain 'group' + $returnData.psobject.TypeNames.insert(0, 'Group') + return $returnData + } + } + + It "Should return Values from the Get method" { + Get-TargetResource @testParams + Should -Invoke -CommandName "Get-MgGroup" -Exactly 1 + Should -Invoke -CommandName "Get-MgGroupMemberOf" -Exactly 1 + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name "The Group Exists and is assigned to the correct role. Values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345" + Description = "Microsoft DSC Group" + SecurityEnabled = $True + GroupTypes = @() + MailNickname = "M365DSC" + IsAssignableToRole = $true + AssignedToRole = "AADRole" + Ensure = "Present" + Credential = $Credential; + } + + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credential" + } + + Mock -CommandName Get-MgGroup -MockWith { + return @{ + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345" + Description = "Microsoft DSC Group" + SecurityEnabled = $True + GroupTypes = @() + MailNickname = "M365DSC" + IsAssignableToRole = $true + } + } + Mock -CommandName Get-MgGroupMemberOf -MockWith { + return @{ + AdditionalProperties = @{ + '@odata.type' = "#microsoft.graph.directoryRole" + displayName = "AADRole" + } + } + } + } + + It "Should return Values from the Get method" { + Get-TargetResource @testParams + Should -Invoke -CommandName "Get-MgGroup" -Exactly 1 + Should -Invoke -CommandName "Get-MgGroupMemberOf" -Exactly 1 + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + Context -Name "The Group exists and values are NOT in the desired state" -Fixture { BeforeAll { $testParams = @{ @@ -185,7 +341,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { GroupTypes = @("Unified") Visibility = "Private" Ensure = "Present" - Credential = $Credential; + Credential = $Credential; } Mock -CommandName New-M365DSCConnection -MockWith { @@ -221,6 +377,190 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } } + Context -Name "The Group Exists but is not a member of another group. Values are NOT in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345" + Description = "Microsoft DSC Group" + SecurityEnabled = $True + GroupTypes = @() + MailNickname = "M365DSC" + MemberOf = "DSCMemberOfGroup" + Ensure = "Present" + Credential = $Credential; + } + + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credential" + } + Mock -CommandName Get-MgGroup -ParameterFilter {$Id -eq "12345-12345-12345-12345" -or $Filter -eq "DisplayName eq 'DSCGroup'"} -MockWith { + return @{ + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345" + Description = "Microsoft DSC Group" + SecurityEnabled = $True + MailNickname = "M365DSC" + GroupTypes = @() + } + } + Mock -CommandName Get-MgGroup -ParameterFilter {$Id -eq '67890-67890-67890-67890' -or $Filter -eq "DisplayName -eq 'DSCMemberOfGroup'"} -MockWith { + $returnData = @{ + DisplayName = "DSCMemberOfGroup" + ID = "67890-67890-67890-67890" + Description = "Microsoft DSC MemberOf Group" + SecurityEnabled = $True + GroupTypes = @() + MailNickname = "M365DSCM" + } + # Set-TargetResource expects object-type of answer to contain 'group' + $returnData.psobject.TypeNames.insert(0, 'Group') + return $returnData + } + } + + It "Should return Values from the Get method" { + Get-TargetResource @testParams + Should -Invoke -CommandName "Get-MgGroup" -Exactly 1 + Should -Invoke -CommandName "Get-MgGroupMemberOf" -Exactly 1 + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It "Should call the Set method" { + Set-TargetResource @testParams + Should -Invoke -CommandName "Get-MgGroup" -Exactly 2 + Should -Invoke -CommandName 'New-MgGroupMember' -Exactly 1 + } + } + + Context -Name "The Group Exists but is not assigned to a role. Values are NOT in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345" + Description = "Microsoft DSC Group" + SecurityEnabled = $True + GroupTypes = @() + MailNickname = "M365DSC" + IsAssignableToRole = $true + AssignedToRole = "AADRole" + Ensure = "Present" + Credential = $Credential; + } + + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credential" + } + + Mock -CommandName Get-MgGroup -MockWith { + return @{ + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345" + Description = "Microsoft DSC Group" + SecurityEnabled = $True + GroupTypes = @() + MailNickname = "M365DSC" + IsAssignableToRole = $true + Ensure = "Present" + } + } + + Mock -CommandName Get-MgDirectoryRole -Mockwith { + return @{ + DisplayName = "AADRole" + ID = "12345-12345-12345-12345" + } + } + } + + It "Should return Values from the Get method" { + Get-TargetResource @testParams + Should -Invoke -CommandName "Get-MgGroup" -Exactly 1 + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It "Should call the Set method" { + Set-TargetResource @testParams + Should -Invoke -CommandName "Get-MgGroup" -Exactly 1 + Should -Invoke -CommandName 'Get-MgDirectoryRole' -Exactly 1 + Should -Invoke -CommandName 'New-MgDirectoryRoleMemberByRef' -Exactly 1 + } + } + + Context -Name "The Group Exists and is assigned to a role but it shouldn't be. Values are NOT in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345" + Description = "Microsoft DSC Group" + SecurityEnabled = $True + GroupTypes = @() + MailNickname = "M365DSC" + IsAssignableToRole = $true + AssignedToRole = @() + Ensure = "Present" + Credential = $Credential; + } + + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credential" + } + + Mock -CommandName Get-MgGroup -MockWith { + return @{ + DisplayName = "DSCGroup" + ID = "12345-12345-12345-12345" + Description = "Microsoft DSC Group" + SecurityEnabled = $True + GroupTypes = @() + MailNickname = "M365DSC" + IsAssignableToRole = $true + Ensure = "Present" + } + } + Mock -CommandName Get-MgGroupMemberOf -MockWith { + return @{ + AdditionalProperties = @{ + '@odata.type' = "#microsoft.graph.directoryRole" + displayName = "AADRole" + } + } + } + Mock -CommandName Get-MgDirectoryRole -Mockwith { + return @{ + DisplayName = "AADRole" + ID = "12345-12345-12345-12345" + } + } + } + + It "Should return Values from the Get method" { + Get-TargetResource @testParams + Should -Invoke -CommandName "Get-MgGroup" -Exactly 1 + Should -Invoke -CommandName "Get-MgGroupMemberOf" -Exactly 1 + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It "Should call the Set method" { + Set-TargetResource @testParams + Should -Invoke -CommandName "Get-MgGroup" -Exactly 1 + Should -Invoke -CommandName 'Get-MgDirectoryRole' -Exactly 1 + Should -Invoke -CommandName 'Remove-MgDirectoryRoleMemberByRef' -Exactly 1 + } + } + Context -Name "ReverseDSC Tests" -Fixture { BeforeAll { $Global:CurrentModeIsExport = $true