Public/Tests/New-IntuneAppGroup.Tests.ps1
|
BeforeAll { $env:TCS_CONFIG_ROOT = Join-Path -Path $TestDrive -ChildPath 'config' $env:TCS_SKIP_UPDATE_CHECK = '1' $env:TCS_TELEMETRY_OPTOUT = '1' $ModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent Import-Module -Name (Join-Path -Path $ModuleRoot -ChildPath 'tcs.azure.psd1') -Force # Pester can only mock commands that exist. When the Microsoft Entra module is not installed, # define stand-ins with the parameters New-IntuneAppGroup uses. $script:CreatedStubs = @() if (-not (Get-Command -Name Get-EntraGroup -ErrorAction SilentlyContinue)) { function global:Get-EntraGroup { [CmdletBinding()] param([string]$Filter) $null = $PSBoundParameters } $script:CreatedStubs += 'Get-EntraGroup' } if (-not (Get-Command -Name New-EntraGroup -ErrorAction SilentlyContinue)) { function global:New-EntraGroup { [CmdletBinding()] param([string]$DisplayName, [string]$MailNickname, [string]$Description, [bool]$MailEnabled, [bool]$SecurityEnabled) $null = $PSBoundParameters } $script:CreatedStubs += 'New-EntraGroup' } } AfterAll { foreach ($stub in $script:CreatedStubs) { Remove-Item -Path "Function:\$stub" -ErrorAction SilentlyContinue } Remove-Module -Name tcs.azure -Force -ErrorAction SilentlyContinue } Describe 'New-IntuneAppGroup' { BeforeEach { Mock -ModuleName tcs.azure Get-EntraGroup { } Mock -ModuleName tcs.azure New-EntraGroup { [pscustomobject]@{ DisplayName = $DisplayName; MailNickname = $MailNickname } } } It 'Creates an Available and a Required app group for each name' { $result = New-IntuneAppGroup -Name 'App1', 'App2' -Confirm:$false $result.DisplayName | Should -Be @('Intune-AG-App1-Available', 'Intune-AG-App1-Required', 'Intune-AG-App2-Available', 'Intune-AG-App2-Required') Should -Invoke -ModuleName tcs.azure New-EntraGroup -Times 4 -Exactly } It 'Creates security groups that are not mail enabled, with a description' { $null = New-IntuneAppGroup -Name 'App1' -Confirm:$false Should -Invoke -ModuleName tcs.azure New-EntraGroup -Times 1 -Exactly -ParameterFilter { $DisplayName -eq 'Intune-AG-App1-Required' -and $SecurityEnabled -eq $true -and $MailEnabled -eq $false -and $Description -eq 'Intune App Group for "App1", this is an Required install.' } } It 'Uses the ACG prefix with -Collection' { $result = New-IntuneAppGroup -Name 'Office Apps' -Collection -Confirm:$false $result.DisplayName | Should -Be @('Intune-ACG-OfficeApps-Available', 'Intune-ACG-OfficeApps-Required') } It 'Converts names to PascalCase and tolerates repeated spaces' { $result = New-IntuneAppGroup -Name 'company portal ' -Confirm:$false $result[0].DisplayName | Should -BeExactly 'Intune-AG-CompanyPortal-Available' } It 'Removes characters that are not valid in a mail nickname' { $result = New-IntuneAppGroup -Name 'App(x64)' -Confirm:$false $result[0].DisplayName | Should -BeExactly 'Intune-AG-App(x64)-Available' $result[0].MailNickname | Should -BeExactly 'Intune-AG-Appx64-Available' } It 'Escapes single quotes in the OData filter' { $null = New-IntuneAppGroup -Name "O'Brien" -Confirm:$false Should -Invoke -ModuleName tcs.azure Get-EntraGroup -ParameterFilter { $Filter -eq "DisplayName eq 'Intune-AG-O''Brien-Available'" } -Times 1 -Exactly } It 'Skips groups that already exist with a warning' { Mock -ModuleName tcs.azure Get-EntraGroup { [pscustomobject]@{ DisplayName = 'existing' } } -ParameterFilter { $Filter -like '*-Available*' } $result = New-IntuneAppGroup -Name 'App1' -Confirm:$false -WarningVariable warnings -WarningAction SilentlyContinue @($result).Count | Should -Be 1 $warnings | Should -Match 'already exists' Should -Invoke -ModuleName tcs.azure New-EntraGroup -Times 1 -Exactly } It 'Creates nothing with -WhatIf' { $null = New-IntuneAppGroup -Name 'App1' -WhatIf Should -Invoke -ModuleName tcs.azure New-EntraGroup -Times 0 -Exactly Should -Invoke -ModuleName tcs.azure Get-EntraGroup -Times 0 -Exactly } It 'Writes a non-terminating error and continues when a group cannot be created' { Mock -ModuleName tcs.azure New-EntraGroup { throw 'Insufficient privileges' } -ParameterFilter { $DisplayName -like '*-Available' } $result = New-IntuneAppGroup -Name 'App1' -Confirm:$false -ErrorVariable errors -ErrorAction SilentlyContinue ($errors | Out-String) | Should -Match 'Insufficient privileges' @($result).Count | Should -Be 1 $result.DisplayName | Should -Be 'Intune-AG-App1-Required' } It 'Stops on the first failure with -ErrorAction Stop' { Mock -ModuleName tcs.azure New-EntraGroup { throw 'Insufficient privileges' } { New-IntuneAppGroup -Name 'App1' -Confirm:$false -ErrorAction Stop } | Should -Throw '*Insufficient privileges*' Should -Invoke -ModuleName tcs.azure New-EntraGroup -Times 1 -Exactly } It 'Writes an error for a whitespace-only name' { $null = New-IntuneAppGroup -Name ' ' -Confirm:$false -ErrorVariable errors -ErrorAction SilentlyContinue @($errors).Count | Should -Be 1 Should -Invoke -ModuleName tcs.azure New-EntraGroup -Times 0 -Exactly } It 'Records start and end telemetry' { Mock -ModuleName tcs.azure Invoke-TelemetryCollection { } $null = New-IntuneAppGroup -Name 'App1' -Confirm:$false Should -Invoke -ModuleName tcs.azure Invoke-TelemetryCollection -ParameterFilter { $Stage -eq 'Start' } -Times 1 -Exactly Should -Invoke -ModuleName tcs.azure Invoke-TelemetryCollection -ParameterFilter { $Stage -eq 'End' -and -not $Failed } -Times 1 -Exactly } It 'Records failed end telemetry when a group cannot be created' { Mock -ModuleName tcs.azure Invoke-TelemetryCollection { } Mock -ModuleName tcs.azure New-EntraGroup { throw 'Insufficient privileges' } $null = New-IntuneAppGroup -Name 'App1' -Confirm:$false -ErrorAction SilentlyContinue Should -Invoke -ModuleName tcs.azure Invoke-TelemetryCollection -ParameterFilter { $Stage -eq 'End' -and $Failed } -Times 1 -Exactly } It 'Requires a name' { (Get-Command -Name New-IntuneAppGroup).Parameters['Name'].Attributes.Where({ $_ -is [System.Management.Automation.ParameterAttribute] }).Mandatory | Should -BeTrue } It 'Supports -WhatIf and -Confirm' { $command = Get-Command -Name New-IntuneAppGroup $command.Parameters.Keys | Should -Contain 'WhatIf' $command.Parameters.Keys | Should -Contain 'Confirm' } } Describe 'New-IntuneAppGroup without the Microsoft Entra module' { It 'Throws a helpful error when the Entra commands are missing' { Mock -ModuleName tcs.azure Get-Command { } -ParameterFilter { $Name -in 'Get-EntraGroup', 'New-EntraGroup' } { New-IntuneAppGroup -Name 'App1' -Confirm:$false } | Should -Throw '*Install-Module Microsoft.Entra.Groups*' } It 'Records failed end telemetry when the Entra commands are missing' { Mock -ModuleName tcs.azure Invoke-TelemetryCollection { } Mock -ModuleName tcs.azure Get-Command { } -ParameterFilter { $Name -in 'Get-EntraGroup', 'New-EntraGroup' } { New-IntuneAppGroup -Name 'App1' -Confirm:$false } | Should -Throw Should -Invoke -ModuleName tcs.azure Invoke-TelemetryCollection -ParameterFilter { $Stage -eq 'Start' } -Times 1 -Exactly Should -Invoke -ModuleName tcs.azure Invoke-TelemetryCollection -ParameterFilter { $Stage -eq 'End' -and $Failed } -Times 1 -Exactly } } |