diff --git a/module/Entra/Microsoft.Entra/Groups/Update-EntraGroup.ps1 b/module/Entra/Microsoft.Entra/Groups/Update-EntraGroup.ps1 new file mode 100644 index 0000000000..836cf45149 --- /dev/null +++ b/module/Entra/Microsoft.Entra/Groups/Update-EntraGroup.ps1 @@ -0,0 +1,101 @@ +# ------------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All Rights Reserved. +# Licensed under the MIT License. See License in the project root for license information. +# ------------------------------------------------------------------------------ + +function Update-EntraGroup { + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Default')] + param ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = "The unique identifier for the group, such as its object ID.")] + [Alias("ObjectId", "Id")] + [ValidateNotNullOrEmpty()] + [string]$GroupId, + + [Parameter(HelpMessage = "The description of the group.")] + [string]$Description, + + [Parameter(HelpMessage = "The display name of the group.")] + [string]$DisplayName, + + [Parameter(HelpMessage = "Defines the group type and membership. Possible values: 'Unified' (Microsoft 365 group) or 'DynamicMembership'. If not 'Unified', the group is a security or distribution group.")] + [string[]]$GroupTypes, + + [Parameter(HelpMessage = "Specifies whether the group is mail-enabled.")] + [bool]$MailEnabled, + + [Parameter(HelpMessage = "The mail nickname for the group.")] + [string]$MailNickname, + + [Parameter(HelpMessage = "Specifies whether the group is a security group.")] + [bool]$SecurityEnabled, + + [Parameter(HelpMessage = "Indicates if the group can be assigned to a Microsoft Entra role (only for security groups). This setting is fixed at creation and cannot be changed.")] + [bool]$IsAssignableToRole, + + [Parameter(HelpMessage = "A list of SMTP proxy addresses for the group, including the primary SMTP address.")] + [string[]]$ProxyAddresses, + + [Parameter(HelpMessage = "Specifies whether the group is visible in the address list.")] + [string]$Visibility, + + [Parameter(Mandatory = $false, HelpMessage = "A hashtable of additional group properties to update.")] + [Alias("BodyParameter", "Body", "BodyParameters")] + [hashtable]$AdditionalProperties = @{} + ) + + begin { + + # Ensure connection to Microsoft Entra + if (-not (Get-EntraContext)) { + $errorMessage = "Not connected to Microsoft Graph. Use 'Connect-Entra -Scopes Group.ReadWrite.All' to authenticate." + Write-Error -Message $errorMessage -ErrorAction Stop + return + } + + } + + process { + $customHeaders = New-EntraCustomHeaders -Command $MyInvocation.MyCommand + + # Microsoft Graph API URL for updating group properties + $graphUri = "https://graph.microsoft.com/v1.0/groups/$GroupId" + + # Initialize hashtable for group properties + $GroupProperties = @{} + + $CommonParameters = @("Verbose", "Debug", "WarningAction", "WarningVariable", "ErrorAction", "ErrorVariable", "OutVariable", "OutBuffer", "WhatIf", "Confirm") + + # Merge individual parameters into GroupProperties + foreach ($param in $PSBoundParameters.Keys) { + if ($param -ne "GroupId" -and $param -ne "AdditionalProperties" -and $CommonParameters -notcontains $param) { + $GroupProperties[$param] = $PSBoundParameters[$param] + } + } + + # Merge AdditionalProperties if provided + foreach ($key in $AdditionalProperties.Keys) { + $GroupProperties[$key] = $AdditionalProperties[$key] + } + + if ($GroupProperties.Count -eq 0) { + Write-Warning "No properties provided for update. Exiting." + return + } + + # Convert final update properties to JSON + $bodyJson = $GroupProperties | ConvertTo-Json -Depth 2 + + + if ($PSCmdlet.ShouldProcess("Group with ID '$GroupId'", "Update the following properties: $($GroupProperties.Keys -join ', ')")) { + try { + # Invoke Microsoft Graph API Request + Invoke-MgGraphRequest -Uri $graphUri -Method PATCH -Body $bodyJson -Headers $customHeaders + Write-Verbose "Properties for group $GroupId updated successfully. Updated properties: $($GroupProperties | Out-String)" + } + catch { + Write-Debug "Error Details: $_" + Write-Error "Failed to update group properties: $_" + } + } + } +} diff --git a/test/Entra/Groups/Update-EntraGroup.Tests.ps1 b/test/Entra/Groups/Update-EntraGroup.Tests.ps1 new file mode 100644 index 0000000000..3af4a266a6 --- /dev/null +++ b/test/Entra/Groups/Update-EntraGroup.Tests.ps1 @@ -0,0 +1,65 @@ +# ------------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. +# ------------------------------------------------------------------------------ + +BeforeAll { + if((Get-Module -Name Microsoft.Entra.Groups) -eq $null){ + Import-Module Microsoft.Entra.Groups + } + Import-Module (Join-Path $PSScriptRoot "..\..\Common-Functions.ps1") -Force + + Mock -CommandName Invoke-MgGraphRequest -MockWith {} -ModuleName Microsoft.Entra.Groups + Mock -CommandName Get-EntraContext -MockWith { @{Scopes = @("Group.ReadWrite.All")} } -ModuleName Microsoft.Entra.Groups +} + +Describe "Update-EntraGroup" { + Context "Test for Update-EntraGroup" { + It "Should return empty object" { + $result = Update-EntraGroup -GroupId bbbbbbbb-1111-2222-3333-cccccccccccc -DisplayName "demo" -MailEnabled $false -SecurityEnabled $true -MailNickName "demoNickname" -Description "test" + $result | Should -BeNullOrEmpty + + Should -Invoke -CommandName Invoke-MgGraphRequest -ModuleName Microsoft.Entra.Groups -Times 1 + } + It "Should execute successfully with Alias" { + $result = Update-EntraGroup -Id bbbbbbbb-1111-2222-3333-cccccccccccc -DisplayName "demo" -MailEnabled $false -SecurityEnabled $true -MailNickName "demoNickname" -Description "test" + $result | Should -BeNullOrEmpty + + Should -Invoke -CommandName Invoke-MgGraphRequest -ModuleName Microsoft.Entra.Groups -Times 1 + } + It "Should fail when GroupId is invalid" { + { Update-EntraGroup -GroupId "" } | Should -Throw "Cannot validate argument on parameter 'GroupId'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." + } + It "Should fail when GroupId is empty" { + { Update-EntraGroup -GroupId } | Should -Throw "Missing an argument for parameter 'GroupId'*" + } + It "Should not execute Invoke-MgGraphRequest with WhatIf parameter" { + Update-EntraGroup -GroupId bbbbbbbb-1111-2222-3333-cccccccccccc -DisplayName "demo" -WhatIf + Should -Invoke -CommandName Invoke-MgGraphRequest -ModuleName Microsoft.Entra.Groups -Times 0 + } + It "Should contain 'User-Agent' header" { + $userAgentHeaderValue = "PowerShell/$psVersion EntraPowershell/$entraVersion Update-EntraGroup" + + Update-EntraGroup -Id bbbbbbbb-1111-2222-3333-cccccccccccc -DisplayName "demo" + + $userAgentHeaderValue = "PowerShell/$psVersion EntraPowershell/$entraVersion Update-EntraGroup" + Should -Invoke -CommandName Invoke-MgGraphRequest -ModuleName Microsoft.Entra.Groups -Times 1 -ParameterFilter { + $Headers.'User-Agent' | Should -Be $userAgentHeaderValue + $true + } + } + It "Should execute successfully without throwing an error " { + # Disable confirmation prompts + $originalDebugPreference = $DebugPreference + $DebugPreference = 'Continue' + + try { + # Act & Assert: Ensure the function doesn't throw an exception + { Update-EntraGroup -Id bbbbbbbb-1111-2222-3333-cccccccccccc } | Should -Not -Throw + } finally { + # Restore original confirmation preference + $DebugPreference = $originalDebugPreference + } + } + } +} +