From 8940515351ee5d64a4ae30b19bb6e371cc99592f Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 1 Nov 2023 16:45:18 -0400 Subject: [PATCH 1/5] Fixes #3842 --- CHANGELOG.md | 6 ++++++ .../MSFT_O365OrgSettings/MSFT_O365OrgSettings.psm1 | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3697a33a6b..efec24833c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change log for Microsoft365DSC +# UNRELEASED + +* O365OrgSettings + * Force register the Office on the Web ServicePrincipal is it is not present. + FIXES [#3842](https://github.com/microsoft/Microsoft365DSC/issues/3842) + # 1.23.1101.1 * AADRoleEligibilityScheduleRequest diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_O365OrgSettings/MSFT_O365OrgSettings.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_O365OrgSettings/MSFT_O365OrgSettings.psm1 index e81751c228..7aae81cc87 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_O365OrgSettings/MSFT_O365OrgSettings.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_O365OrgSettings/MSFT_O365OrgSettings.psm1 @@ -196,7 +196,14 @@ function Get-TargetResource try { $OfficeOnlineId = 'c1f33bc0-bdb4-4248-ba9b-096807ddb43e' - $M365WebEnableUsersToOpenFilesFrom3PStorageValue = Get-MgServicePrincipal -Filter "appId eq '$OfficeOnlineId'" -Property 'AccountEnabled' + $M365WebEnableUsersToOpenFilesFrom3PStorageValue = Get-MgServicePrincipal -Filter "appId eq '$OfficeOnlineId'" -Property 'AccountEnabled' -ErrorAction SilentlyContinue + if ($null -eq $M365WebEnableUsersToOpenFilesFrom3PStorageValue) + { + Write-Verbose -Message "Registering the Office on the web Service Principal" + New-MgServicePrincipal -AppId 'c1f33bc0-bdb4-4248-ba9b-096807ddb43e' -ErrorAction Stop | Out-Null + $M365WebEnableUsersToOpenFilesFrom3PStorageValue = Get-MgServicePrincipal -Filter "appId eq '$OfficeOnlineId'" -Property 'AccountEnabled' -ErrorAction SilentlyContinue + } + if ($null -ne $M365WebEnableUsersToOpenFilesFrom3PStorageValue) { $results += @{ From a862621bb9582159f82903f317b90b3a7b9bb949 Mon Sep 17 00:00:00 2001 From: Philippe Kernevez Date: Tue, 7 Nov 2023 11:26:15 +0100 Subject: [PATCH 2/5] Fix issue 3759 --- CHANGELOG.md | 3 +++ .../DSCResources/MSFT_TeamsTeam/MSFT_TeamsTeam.psm1 | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efec24833c..0cc2740a8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ * O365OrgSettings * Force register the Office on the Web ServicePrincipal is it is not present. FIXES [#3842](https://github.com/microsoft/Microsoft365DSC/issues/3842) +* TeamsTeam + * Fixes incomplete import due to error "Cannot index into a null array" + FIXES: [#3759](https://github.com/microsoft/Microsoft365DSC/issues/3759) # 1.23.1101.1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_TeamsTeam/MSFT_TeamsTeam.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_TeamsTeam/MSFT_TeamsTeam.psm1 index c48baa95a0..98f85fa961 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_TeamsTeam/MSFT_TeamsTeam.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_TeamsTeam/MSFT_TeamsTeam.psm1 @@ -174,13 +174,17 @@ function Get-TargetResource Write-Verbose -Message "Getting Team {$DisplayName} Owners" [array]$Owners = Get-TeamUser -GroupId $team.GroupId | Where-Object { $_.Role -eq 'owner' } + if ($null -eq $Owners) { # Without Users, Get-TeamUser return null instead on empty array + $Owners = @() + } + Write-Verbose -Message "Found Team $($team.DisplayName)." $result = @{ DisplayName = $team.DisplayName GroupID = $team.GroupId Description = $team.Description - Owner = $Owners[0].User.ToString() + Owner = [array]$Owners.User MailNickName = $team.MailNickName Visibility = $team.Visibility AllowAddRemoveApps = $team.AllowAddRemoveApps From 331ab32fcfcc00cf3d87f67a416689ab6a8cce2c Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 7 Nov 2023 12:47:40 -0500 Subject: [PATCH 3/5] AADExternalIdentity Initial Release --- CHANGELOG.md | 2 + .../MSFT_AADExternalIdentityPolicy.psm1 | 355 ++++++++++++++++++ .../MSFT_AADExternalIdentityPolicy.schema.mof | 13 + .../MSFT_AADExternalIdentityPolicy/Readme.md | 5 + .../settings.json | 26 ++ .../1-ConfigureExternalIdentityPolicy.ps1 | 27 ++ ...365DSC.AADExternalIdentityPolicy.tests.ps1 | 137 +++++++ Tests/Unit/Stubs/Microsoft365.psm1 | 334 ++++++++++++++++ 8 files changed, 899 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/MSFT_AADExternalIdentityPolicy.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/MSFT_AADExternalIdentityPolicy.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/Readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADExternalIdentityPolicy/1-ConfigureExternalIdentityPolicy.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADExternalIdentityPolicy.tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index efec24833c..a7492c5ff4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ # UNRELEASED +* AADExternalIdentityPolicy + * Initial release. * O365OrgSettings * Force register the Office on the Web ServicePrincipal is it is not present. FIXES [#3842](https://github.com/microsoft/Microsoft365DSC/issues/3842) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/MSFT_AADExternalIdentityPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/MSFT_AADExternalIdentityPolicy.psm1 new file mode 100644 index 0000000000..889b914f14 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/MSFT_AADExternalIdentityPolicy.psm1 @@ -0,0 +1,355 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $AllowDeletedIdentitiesDataRemoval, + + [Parameter(Mandatory = $true)] + [System.Boolean] + $AllowExternalIdentitiesToLeave, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity + ) + + Write-Verbose -Message 'Getting configuration of External Identity Policy' + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName -replace 'MSFT_', '' + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $nullReturn = @{ + IsSingleInstance = 'Yes' + } + + try + { + $Policy = Get-MgBetaPolicyExternalIdentityPolicy -ErrorAction Stop + } + catch + { + $message = 'Could not find existing external identity policy' + + New-M365DSCLogEntry -Message $message ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullReturn + } + + if ($null -eq $Policy) + { + $message = 'Existing External Identity Policy was not found' + + New-M365DSCLogEntry -Message $message ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullReturn + } + else + { + $result = @{ + IsSingleInstance = 'Yes' + AllowDeletedIdentitiesDataRemoval = $Policy.allowDeletedIdentitiesDataRemoval + AllowExternalIdentitiesToLeave = $Policy.allowExternalIdentitiesToLeave + Credential = $Credential + ApplicationSecret = $ApplicationSecret + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + Managedidentity = $ManagedIdentity.IsPresent + } + + Write-Verbose -Message "Get-TargetResource Result: `n $(Convert-M365DscHashtableToString -Hashtable $result)" + return $result + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $AllowDeletedIdentitiesDataRemoval, + + [Parameter(Mandatory = $true)] + [System.Boolean] + $AllowExternalIdentitiesToLeave, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity + ) + Write-Verbose -Message 'Setting configuration for External Identity Policy' + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName -replace 'MSFT_', '' + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $desiredParameters = ([hashtable]$PSBoundParameters).Clone() + $desiredParameters.Remove('IsSingleInstance') | Out-Null + $desiredParameters.Remove('ApplicationId') | Out-Null + $desiredParameters.Remove('TenantId') | Out-Null + $desiredParameters.Remove('CertificateThumbprint') | Out-Null + $desiredParameters.Remove('ApplicationSecret') | Out-Null + $desiredParameters.Remove('Credential') | Out-Null + $desiredParameters.Remove('ManagedIdentity') | Out-Null + + try + { + Write-Verbose -Message "Updating existing authorization policy with values: $(Convert-M365DscHashtableToString -Hashtable $desiredParameters)" + Update-MgBetaPolicyExternalIdentityPolicy @desiredParameters -ErrorAction Stop | Out-Null + } + catch + { + New-M365DSCLogEntry -Message 'Error updating data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + Write-Verbose -Message "Set-Targetresource: Failed change policy $DisplayName" + throw $_ + } + Write-Verbose -Message "Set-Targetresource: finished processing Policy $Displayname" +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + [ValidateSet('Yes')] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $AllowDeletedIdentitiesDataRemoval, + + [Parameter(Mandatory = $true)] + [System.Boolean] + $AllowExternalIdentitiesToLeave, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity + ) + + Write-Verbose -Message 'Testing configuration of External Identity Policy' + + $CurrentValues = Get-TargetResource @PSBoundParameters + + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" + + $ValuesToCheck = $PSBoundParameters + + $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + + Write-Verbose -Message "Test-TargetResource returned $TestResult" + + return $TestResult +} + +function Export-TargetResource +{ + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName -replace 'MSFT_', '' + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + try + { + $params = @{ + IsSingleInstance = 'Yes' + AllowExternalIdentitiesToLeave = $true + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + ApplicationSecret = $ApplicationSecret + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity + } + $Results = Get-TargetResource @Params + + if ($Results -is [System.Collections.Hashtable] -and $Results.Count -gt 1) + { + Write-Host "`r`n" -NoNewline + Write-Host " |---[1/1] External Identity Policy" -NoNewline + $results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $results + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $results ` + -Credential $Credential + Save-M365DSCPartialExport -Content $currentDSCBlock ` + -FileName $Global:PartialExportFileName + + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host $Global:M365DSCEmojiRedX + } + + return $currentDSCBlock + } + catch + { + Write-Host $Global:M365DSCEmojiRedX + + New-M365DSCLogEntry -Message 'Error during Export:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return '' + } +} + +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/MSFT_AADExternalIdentityPolicy.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/MSFT_AADExternalIdentityPolicy.schema.mof new file mode 100644 index 0000000000..f087841590 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/MSFT_AADExternalIdentityPolicy.schema.mof @@ -0,0 +1,13 @@ +[ClassVersion("1.0.0.0"), FriendlyName("AADExternalIdentityPolicy")] +class MSFT_AADExternalIdentityPolicy : OMI_BaseResource +{ + [Key, Description("Only valid value is 'Yes'."), ValueMap{"Yes"}, Values{"Yes"}] String IsSingleInstance; + [Write, Description("Reserved for future use.")] Boolean AllowDeletedIdentitiesDataRemoval; + [Required, Description("Defines whether external users can leave the guest tenant. If set to false, self-service controls are disabled, and the admin of the guest tenant must manually remove the external user from the guest tenant. When the external user leaves the tenant, their data in the guest tenant is first soft-deleted then permanently deleted in 30 days.")] Boolean allowExternalIdentitiesToLeave; + [Write, Description("Credentials for the Microsoft Graph delegated permissions."), EmbeddedInstance("MSFT_Credential")] String Credential; + [Write, Description("Id of the Azure Active Directory application to authenticate with.")] String ApplicationId; + [Write, Description("Id of the Azure Active Directory tenant used for authentication.")] String TenantId; + [Write, Description("Secret of the Azure Active Directory application to authenticate with."), EmbeddedInstance("MSFT_Credential")] String ApplicationSecret; + [Write, Description("Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication.")] String CertificateThumbprint; + [Write, Description("Managed ID being used for authentication.")] Boolean ManagedIdentity; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/Readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/Readme.md new file mode 100644 index 0000000000..386532c142 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/Readme.md @@ -0,0 +1,5 @@ +# AADExternalIdentityPolicy + +## Description + +Represents the tenant-wide policy that controls whether external users can leave the guest Microsoft Entra tenant via self-service controls. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/settings.json new file mode 100644 index 0000000000..87d132f336 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADExternalIdentityPolicy/settings.json @@ -0,0 +1,26 @@ +{ + "resourceName": "AADExternalIdentityPolicy", + "description": "Represents the tenant-wide policy that controls whether external users can leave the guest Microsoft Entra tenant via self-service controls.", + "roles": { + "read": [], + "update": [ + "Privileged Role Administrator" + ] + }, + "permissions": { + "graph": { + "application": { + "read": [ + { + "name": "Policy.Read.All" + } + ], + "update": [ + { + "name": "Policy.ReadWrite.ExternalIdentities" + } + ] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADExternalIdentityPolicy/1-ConfigureExternalIdentityPolicy.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADExternalIdentityPolicy/1-ConfigureExternalIdentityPolicy.ps1 new file mode 100644 index 0000000000..69bcc10cc8 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADExternalIdentityPolicy/1-ConfigureExternalIdentityPolicy.ps1 @@ -0,0 +1,27 @@ +<# +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] + $credsAdmin + ) + + Import-DscResource -ModuleName Microsoft365DSC + + node localhost + { + AADExternalIdentityPolicy "AADExternalIdentityPolicy" + { + AllowDeletedIdentitiesDataRemoval = $False; + AllowExternalIdentitiesToLeave = $True; + Credential = $CredsAdmin; + IsSingleInstance = "Yes"; + } + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADExternalIdentityPolicy.tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADExternalIdentityPolicy.tests.ps1 new file mode 100644 index 0000000000..87621b125b --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADExternalIdentityPolicy.tests.ps1 @@ -0,0 +1,137 @@ +[CmdletBinding()] +param( +) +$M365DSCTestFolder = Join-Path -Path $PSScriptRoot ` + -ChildPath '..\..\Unit' ` + -Resolve +$CmdletModule = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Microsoft365.psm1' ` + -Resolve) +$GenericStubPath = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\Stubs\Generic.psm1' ` + -Resolve) +Import-Module -Name (Join-Path -Path $M365DSCTestFolder ` + -ChildPath '\UnitTestHelper.psm1' ` + -Resolve) + +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource 'AADExternalIdentityPolicy' -GenericStubModule $GenericStubPath +Describe -Name $Global:DscHelper.DescribeHeader -Fixture { + InModuleScope -ModuleName $Global:DscHelper.ModuleName -ScriptBlock { + Invoke-Command -ScriptBlock $Global:DscHelper.InitializeScript -NoNewScope + BeforeAll { + $secpasswd = ConvertTo-SecureString 'test@password1' -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) + + $Global:PartialExportFileName = 'c:\TestPath' + + Mock -CommandName Save-M365DSCPartialExport -MockWith { + } + + Mock -CommandName Get-PSSession -MockWith { + + } + + Mock -CommandName Remove-PSSession -MockWith { + + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return 'Credentials' + } + + # Mock Write-Host to hide output during the tests + Mock -CommandName Write-Host -MockWith { + } + Mock -CommandName Update-MgBetaPolicyExternalIdentityPolicy -MockWith { + } + } + + Context -Name 'The instance exists and values are already in the desired state' -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = 'Yes' + AllowDeletedIdentitiesDataRemoval = $False; + AllowExternalIdentitiesToLeave = $True; + Credential = $Credential + } + Mock -CommandName Get-MgBetaPolicyExternalIdentityPolicy -MockWith { + return @{ + Id = 'externalidentitypolicy' + DisplayName = 'External Identity Policy' + AllowDeletedIdentitiesDataRemoval = $False; + AllowExternalIdentitiesToLeave = $True; + } + } + } + + It 'Should return Values from the get method' { + Get-TargetResource @testParams + Should -Invoke -CommandName 'Get-MgBetaPolicyExternalIdentityPolicy' -Exactly 1 + } + + It 'Should return true from the test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name 'Values are not in the desired state' -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = 'Yes' + AllowDeletedIdentitiesDataRemoval = $False; + AllowExternalIdentitiesToLeave = $True; + Credential = $Credential + } + Mock -CommandName Get-MgBetaPolicyExternalIdentityPolicy -MockWith { + return @{ + Id = 'externalidentitypolicy' + DisplayName = 'External Identity Policy' + AllowDeletedIdentitiesDataRemoval = $True; #drift + AllowExternalIdentitiesToLeave = $True; + } + } + } + + It 'Should return values from the get method' { + Get-TargetResource @testParams + Should -Invoke -CommandName 'Get-MgBetaPolicyExternalIdentityPolicy' -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 'Update-MgBetaPolicyExternalIdentityPolicy' -Exactly 1 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential + } + + Mock -CommandName Get-MgBetaPolicyExternalIdentityPolicy -MockWith { + return @{ + Id = 'externalidentitypolicy' + DisplayName = 'External Identity Policy' + AllowDeletedIdentitiesDataRemoval = $True; #drift + AllowExternalIdentitiesToLeave = $True; + } + } + } + + It 'Should reverse engineer resource from the export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index db2d079c8b..495ef266fa 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -30359,6 +30359,83 @@ function Get-MgBetaIdentityConditionalAccessPolicy $HttpPipelineAppend ) } +function Get-MgBetaIdentityProvider +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $IdentityProviderBaseId, + + [Parameter()] + [System.String[]] + $Property, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Int32] + $PageSize, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.Int32] + $Skip, + + [Parameter()] + [System.Int32] + $Top, + + [Parameter()] + [System.String] + $CountVariable, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.String[]] + $Sort, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $All, + + [Parameter()] + [System.String] + $Filter, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.String] + $Search, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.String[]] + $ExpandProperty, + + [Parameter()] + [PSObject] + $HttpPipelineAppend + ) +} function Get-MgBetaOauth2PermissionGrant { [CmdletBinding()] @@ -30855,6 +30932,43 @@ function Get-MgBetaPolicyCrossTenantAccessPolicyPartner $HttpPipelineAppend ) } +function Get-MgBetaPolicyExternalIdentityPolicy +{ + [CmdletBinding()] + param( + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.String[]] + $ExpandProperty, + + [Parameter()] + [System.String[]] + $Property, + + [Parameter()] + [PSObject] + $HttpPipelineAppend, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break + ) +} function Get-MgBetaPolicyIdentitySecurityDefaultEnforcementPolicy { [CmdletBinding()] @@ -31338,6 +31452,55 @@ function New-MgBetaIdentityConditionalAccessPolicy $HttpPipelineAppend ) } +function New-MgBetaIdentityProvider +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [PSObject] + $HttpPipelineAppend + ) +} function New-MgBetaPolicyAuthenticationMethodPolicyAuthenticationMethodConfiguration { [CmdletBinding()] @@ -31810,6 +31973,55 @@ function Remove-MgBetaIdentityConditionalAccessPolicy $HttpPipelineAppend ) } +function Remove-MgBetaIdentityProvider +{ + [CmdletBinding()] + param( + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $PassThru, + + [Parameter()] + [System.String] + $IfMatch, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [PSObject] + $HttpPipelineAppend, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.String] + $IdentityProviderBaseId, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break + ) +} function Remove-MgBetaPolicyAuthenticationMethodPolicy { [CmdletBinding()] @@ -32307,6 +32519,63 @@ function Update-MgBetaIdentityConditionalAccessPolicy $HttpPipelineAppend ) } +function Update-MgBetaIdentityProvider +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.String] + $IdentityProviderBaseId, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [PSObject] + $HttpPipelineAppend, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break + ) +} function Update-MgBetaPolicyAuthenticationMethodPolicy { [CmdletBinding()] @@ -32951,6 +33220,71 @@ function Update-MgBetaPolicyCrossTenantAccessPolicyPartner $HttpPipelineAppend ) } +function Update-MgBetaPolicyExternalIdentityPolicy +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $AllowExternalIdentitiesToLeave, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $AllowDeletedIdentitiesDataRemoval, + + [Parameter()] + [System.DateTime] + $DeletedDateTime, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [PSObject] + $HttpPipelineAppend + ) +} function Update-MgBetaPolicyIdentitySecurityDefaultEnforcementPolicy { [CmdletBinding()] From dad70137542f00458b1fc893a7e53b1ab770e770 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 7 Nov 2023 17:58:49 +0000 Subject: [PATCH 4/5] Updated Resources and Cmdlet documentation pages --- .../azure-ad/AADExternalIdentityPolicy.md | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 docs/docs/resources/azure-ad/AADExternalIdentityPolicy.md diff --git a/docs/docs/resources/azure-ad/AADExternalIdentityPolicy.md b/docs/docs/resources/azure-ad/AADExternalIdentityPolicy.md new file mode 100644 index 0000000000..1a74ebc223 --- /dev/null +++ b/docs/docs/resources/azure-ad/AADExternalIdentityPolicy.md @@ -0,0 +1,78 @@ +# AADExternalIdentityPolicy + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **IsSingleInstance** | Key | String | Only valid value is 'Yes'. | `Yes` | +| **AllowDeletedIdentitiesDataRemoval** | Write | Boolean | Reserved for future use. | | +| **allowExternalIdentitiesToLeave** | Required | Boolean | Defines whether external users can leave the guest tenant. If set to false, self-service controls are disabled, and the admin of the guest tenant must manually remove the external user from the guest tenant. When the external user leaves the tenant, their data in the guest tenant is first soft-deleted then permanently deleted in 30 days. | | +| **Credential** | Write | PSCredential | Credentials for the Microsoft Graph delegated permissions. | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **ApplicationSecret** | Write | PSCredential | Secret of the Azure Active Directory application to authenticate with. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | + +## Description + +Represents the tenant-wide policy that controls whether external users can leave the guest Microsoft Entra tenant via self-service controls. + +## Permissions + +### Microsoft Graph + +To authenticate with the Microsoft Graph API, this resource required the following permissions: + +#### Delegated permissions + +- **Read** + + - None + +- **Update** + + - None + +#### Application permissions + +- **Read** + + - Policy.Read.All + +- **Update** + + - Policy.ReadWrite.ExternalIdentities + +## Examples + +### Example 1 + +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. + +```powershell +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [PSCredential] + $credsAdmin + ) + + Import-DscResource -ModuleName Microsoft365DSC + + node localhost + { + AADExternalIdentityPolicy "AADExternalIdentityPolicy" + { + AllowDeletedIdentitiesDataRemoval = $False; + AllowExternalIdentitiesToLeave = $True; + Credential = $CredsAdmin; + IsSingleInstance = "Yes"; + } + } +} +``` + From c0648ddc5db607a6a26b91521cc8f8685d052655 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Wed, 8 Nov 2023 10:18:01 -0500 Subject: [PATCH 5/5] Release 1.23.1108.1 --- CHANGELOG.md | 2 +- Modules/Microsoft365DSC/Microsoft365DSC.psd1 | 24 ++++++++------------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3e90d5857..5a4fdcc01a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change log for Microsoft365DSC -# UNRELEASED +# 1.23.1108.1 * AADExternalIdentityPolicy * Initial release. diff --git a/Modules/Microsoft365DSC/Microsoft365DSC.psd1 b/Modules/Microsoft365DSC/Microsoft365DSC.psd1 index 83383dfbf0..9bd50c5aba 100644 --- a/Modules/Microsoft365DSC/Microsoft365DSC.psd1 +++ b/Modules/Microsoft365DSC/Microsoft365DSC.psd1 @@ -3,7 +3,7 @@ # # Generated by: Microsoft Corporation # -# Generated on: 2023-11-01 +# Generated on: 2023-11-08 @{ @@ -11,7 +11,7 @@ # RootModule = '' # Version number of this module. - ModuleVersion = '1.23.1101.1' + ModuleVersion = '1.23.1108.1' # Supported PSEditions # CompatiblePSEditions = @() @@ -140,18 +140,14 @@ IconUri = 'https://github.com/microsoft/Microsoft365DSC/blob/Dev/Modules/Microsoft365DSC/Dependencies/Images/Logo.png?raw=true' # ReleaseNotes of this module - ReleaseNotes = '* AADRoleEligibilityScheduleRequest - * Fixes how the Get method retrieves existing instances for Groups. - FIXES [#3787](https://github.com/microsoft/Microsoft365DSC/issues/3787) - * SCSecurityFilter - * Fixes an issue because Region could be empty - FIXES: [#3854](https://github.com/microsoft/Microsoft365DSC/issues/3854) - * SPOSharingSettings - * Fixes parameter validation of ExternalUserExpireInDays and ExternalUserExpirationRequired. - FIXES [#3856](https://github.com/microsoft/Microsoft365DSC/issues/3856) - * TeamsComplianceRecordingPolicy - * Fix an issue where the Compliance Application ID was not properly retrieved. - FIXES [#3848](https://github.com/microsoft/Microsoft365DSC/issues/3848)' + ReleaseNotes = '* AADExternalIdentityPolicy + * Initial release. + * O365OrgSettings + * Force register the Office on the Web ServicePrincipal is it is not present. + FIXES [#3842](https://github.com/microsoft/Microsoft365DSC/issues/3842) + * TeamsTeam + * Fixes incomplete import due to error "Cannot index into a null array" + FIXES: [#3759](https://github.com/microsoft/Microsoft365DSC/issues/3759)' # Flag to indicate whether the module requires explicit user acceptance for install/update # RequireLicenseAcceptance = $false