From 7fce3eb98674093e0e477d881e8e350725662a1b Mon Sep 17 00:00:00 2001 From: "Katherine Hsu (from Dev Box)" Date: Tue, 8 Oct 2024 19:42:45 -0700 Subject: [PATCH 1/8] add intune mobile apps webapp structure --- .../MSFT_IntuneMobileAppsWebApp.mof | 14 + .../MSFT_IntuneMobileAppsWebApp.psm1 | 371 ++++++++++++++++++ .../MSFT_IntuneMobileAppsWebApp/readme.md | 6 + .../MSFT_IntuneMobileAppsWebApp/settings.json | 32 ++ .../Resources/ResourceName/1-Create.ps1 | 26 ++ .../Resources/ResourceName/2-Update.ps1 | 26 ++ .../Resources/ResourceName/3-Remove.ps1 | 26 ++ .../Microsoft365DSC.ResourceName.Tests.ps1 | 178 +++++++++ 8 files changed, 679 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/ResourceName/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/ResourceName/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/ResourceName/3-Remove.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.mof new file mode 100644 index 0000000000..fe8aabccae --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.mof @@ -0,0 +1,14 @@ +[ClassVersion("1.0.0.0"), FriendlyName("ResourceName")] +class MSFT_ResourceName : OMI_BaseResource +{ + [Key, Description("")] String PrimaryKey; + [Write, Description("")] String OtherProperties; + + [Write, Description("Present ensures the instance exists, absent ensures it is removed."), ValueMap{"Absent","Present"}, Values{"Absent","Present"}] string Ensure; + [Write, Description("Credentials of the workload's Admin"), 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("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; + [Write, Description("Access token used for authentication.")] String AccessTokens[]; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 new file mode 100644 index 0000000000..b3846eb047 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 @@ -0,0 +1,371 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + ##TODO - Replace the PrimaryKey + [Parameter(Mandatory = $true)] + [System.String] + $PrimaryKey, + + ##TODO - Add the list of Parameters + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + ##TODO - Replace the workload by the one associated to your resource + New-M365DSCConnection -Workload 'Workload' ` + -InboundParameters $PSBoundParameters | Out-Null + + #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 + + $nullResult = $PSBoundParameters + $nullResult.Ensure = 'Absent' + try + { + if ($null -ne $Script:exportedInstances -and $Script:ExportMode) + { + ##TODO - Replace the PrimaryKey in the Filter by the one for the resource + $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.PrimaryKey -eq $PrimaryKey} + } + else + { + ##TODO - Replace the cmdlet by the one to retrieve a specific instance. + $instance = Get-cmdlet -PrimaryKey $PrimaryKey -ErrorAction Stop + } + if ($null -eq $instance) + { + return $nullResult + } + + $results = @{ + ##TODO - Add the list of parameters to be returned + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + return [System.Collections.Hashtable] $results + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullResult + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + ##TODO - Replace the PrimaryKey + [Parameter(Mandatory = $true)] + [System.String] + $PrimaryKey, + + ##TODO - Add the list of Parameters + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + #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 + + $currentInstance = Get-TargetResource @PSBoundParameters + + $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + + # CREATE + if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + { + ##TODO - Replace by the New cmdlet for the resource + New-Cmdlet @SetParameters + } + # UPDATE + elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + { + ##TODO - Replace by the Update/Set cmdlet for the resource + Set-cmdlet @SetParameters + } + # REMOVE + elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + { + ##TODO - Replace by the Remove cmdlet for the resource + Remove-cmdlet @SetParameters + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + ##TODO - Replace the PrimaryKey + [Parameter(Mandatory = $true)] + [System.String] + $PrimaryKey, + + ##TODO - Add the list of Parameters + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + #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 + + $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" + + $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, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + ##TODO - Replace workload + $ConnectionMode = New-M365DSCConnection -Workload 'Workload' ` + -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 + + try + { + $Script:ExportMode = $true + ##TODO - Replace Get-Cmdlet by the cmdlet to retrieve all instances + [array] $Script:exportedInstances = Get-Cmdlet -ErrorAction Stop + + $i = 1 + $dscContent = '' + if ($Script:exportedInstances.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + foreach ($config in $Script:exportedInstances) + { + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + + $displayedKey = $config.Id + Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline + $params = @{ + ##TODO - Specify the Primary Key + #PrimaryKey = $config.PrimaryKey + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + + $Results = Get-TargetResource @Params + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + $dscContent += $currentDSCBlock + Save-M365DSCPartialExport -Content $currentDSCBlock ` + -FileName $Global:PartialExportFileName + $i++ + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + return $dscContent + } + 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_IntuneMobileAppsWebApp/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/readme.md new file mode 100644 index 0000000000..32e0e7fb27 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/readme.md @@ -0,0 +1,6 @@ + +# ResourceName + +## Description + +##TODO - Provide a short description of what the resource is set to configure. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/settings.json new file mode 100644 index 0000000000..edf14b05e4 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/settings.json @@ -0,0 +1,32 @@ +{ + "resourceName": "ResourceName", + "description": "Description of what the resource is about.", + "roles": { + "read": [ + "Role" + ], + "update": [ + "Role" + ] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [ + { + "name": "Permission for Monitoring and Export" + } + ], + "update": [ + { + "name": "Permission for deploying" + } + ] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/ResourceName/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/ResourceName/1-Create.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/ResourceName/1-Create.ps1 @@ -0,0 +1,26 @@ +<# +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()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/ResourceName/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/ResourceName/2-Update.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/ResourceName/2-Update.ps1 @@ -0,0 +1,26 @@ +<# +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()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/ResourceName/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/ResourceName/3-Remove.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/ResourceName/3-Remove.ps1 @@ -0,0 +1,26 @@ +<# +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()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 new file mode 100644 index 0000000000..780e0f343d --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 @@ -0,0 +1,178 @@ +[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) + +$CurrentScriptPath = $PSCommandPath.Split('\') +$CurrentScriptName = $CurrentScriptPath[$CurrentScriptPath.Length -1] +$ResourceName = $CurrentScriptName.Split('.')[1] +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource $ResourceName -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 (New-Guid | Out-String) -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential ('tenantadmin@mydomain.com', $secpasswd) + + Mock -CommandName Confirm-M365DSCDependencies -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credentials" + } + + ##TODO - Mock any Remove/Set/New cmdlets + + # Mock Write-Host to hide output during the tests + Mock -CommandName Write-Host -MockWith { + } + $Script:exportedInstances =$null + $Script:ExportMode = $false + } + # Test contexts + Context -Name "The instance should exist but it DOES NOT" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return $null + Mock -CommandName Get-Cmdlet -MockWith { + return $null + } + } + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' + } + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should create a new instance from the Set method' { + ##TODO - Replace the New-Cmdlet by the appropriate one + Set-TargetResource @testParams + Should -Invoke -CommandName New-Cmdlet -Exactly 1 + } + } + + Context -Name "The instance exists but it SHOULD NOT" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Absent' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return an instance + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should remove the instance from the Set method' { + Set-TargetResource @testParams + ##TODO - Replace the Remove-Cmdlet by the appropriate one + Should -Invoke -CommandName Remove-Cmdlet -Exactly 1 + } + } + + Context -Name "The instance exists and values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return the desired values + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name "The instance exists and values are NOT in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + ##TODO - Add Parameters + Ensure = 'Present' + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return a drift + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + + It 'Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should call the Set method' { + Set-TargetResource @testParams + ##TODO - Replace the Update-Cmdlet by the appropriate one + Should -Invoke -CommandName Update-Cmdlet -Exactly 1 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential; + } + + ##TODO - Mock the Get-Cmdlet to return an instance + Mock -CommandName Get-Cmdlet -MockWith { + return @{ + + } + } + } + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope From d0adf1d2d615bfc31c6faa49f0987cee667b6539 Mon Sep 17 00:00:00 2001 From: "Katherine Hsu (from Dev Box)" Date: Tue, 8 Oct 2024 19:47:59 -0700 Subject: [PATCH 2/8] update folder structure --- .../MSFT_IntuneMobileAppsWebApp.psm1 | 850 +++++++++++++++++- .../1-Create.ps1 | 0 .../2-Update.ps1 | 0 .../3-Remove.ps1 | 0 ...ft365DSC.IntuneMobileAppsWebApp.Tests.ps1} | 0 5 files changed, 801 insertions(+), 49 deletions(-) rename Modules/Microsoft365DSC/Examples/Resources/{ResourceName => IntuneMobileAppsWebApp}/1-Create.ps1 (100%) rename Modules/Microsoft365DSC/Examples/Resources/{ResourceName => IntuneMobileAppsWebApp}/2-Update.ps1 (100%) rename Modules/Microsoft365DSC/Examples/Resources/{ResourceName => IntuneMobileAppsWebApp}/3-Remove.ps1 (100%) rename Tests/Unit/Microsoft365DSC/{Microsoft365DSC.ResourceName.Tests.ps1 => Microsoft365DSC.IntuneMobileAppsWebApp.Tests.ps1} (100%) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 index b3846eb047..1561fcd9ca 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 @@ -4,12 +4,90 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( - ##TODO - Replace the PrimaryKey + #region Intune resource parameters + + [Parameter()] + [System.String] + $Id, + [Parameter(Mandatory = $true)] [System.String] - $PrimaryKey, + $DisplayName, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $Developer, + + [Parameter()] + [System.String] + $InformationUrl, - ##TODO - Add the list of Parameters + [Parameter()] + [System.Boolean] + $IsFeatured, + + [Parameter()] + [System.Boolean] + $IgnoreVersionDetection, + + [Parameter()] + [System.String] + $Notes, + + [Parameter()] + [System.String] + $Owner, + + [Parameter()] + [System.String] + $PrivacyInformationUrl, + + [Parameter()] + [System.String] + $Publisher, + + [Parameter()] + [System.String] + [ValidateSet('notPublished', 'processing','published')] + $PublishingState, + + [Parameter()] + [System.String[]] + $RoleScopeTagIds, + + [Parameter()] + [System.String] + $BundleId, + + [Parameter()] + [System.String] + $BuildNumber, + + [Parameter()] + [System.String] + $VersionNumber, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Categories, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Assignments, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $ChildApps, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $LargeIcon, + + #endregion [Parameter()] [ValidateSet('Present', 'Absent')] @@ -32,6 +110,10 @@ function Get-TargetResource [System.String] $CertificateThumbprint, + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + [Parameter()] [Switch] $ManagedIdentity, @@ -41,8 +123,7 @@ function Get-TargetResource $AccessTokens ) - ##TODO - Replace the workload by the one associated to your resource - New-M365DSCConnection -Workload 'Workload' ` + New-M365DSCConnection -Workload 'MicrosoftGraph' ` -InboundParameters $PSBoundParameters | Out-Null #Ensure the proper dependencies are installed in the current environment. @@ -61,31 +142,101 @@ function Get-TargetResource $nullResult.Ensure = 'Absent' try { - if ($null -ne $Script:exportedInstances -and $Script:ExportMode) - { - ##TODO - Replace the PrimaryKey in the Filter by the one for the resource - $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.PrimaryKey -eq $PrimaryKey} - } - else + $instance = Get-MgBetaDeviceAppManagementMobileApp ` + -Filter "(isof('microsoft.graph.macOSLobApp') and displayName eq '$DisplayName')" ` + -ExpandProperty "categories,assignments" ` + -ErrorAction SilentlyContinue | Where-Object ` + -FilterScript { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.macOSLobApp' } + + if ($null -eq $instance) { - ##TODO - Replace the cmdlet by the one to retrieve a specific instance. - $instance = Get-cmdlet -PrimaryKey $PrimaryKey -ErrorAction Stop + Write-Verbose -Message "No Mobile app with DisplayName {$DisplayName} was found. Search with DisplayName." + $instance = Get-MgBetaDeviceAppManagementMobileApp ` + -MobileAppId $Id ` + -ExpandProperty "categories,assignments" ` + -ErrorAction Stop | Where-Object ` + -FilterScript { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.macOSLobApp' } } + if ($null -eq $instance) { + Write-Verbose -Message "No Mobile app with {$Id} was found." return $nullResult } $results = @{ - ##TODO - Add the list of parameters to be returned + Id = $instance.Id + Description = $instance.Description + Developer = $instance.Developer + DisplayName = $instance.DisplayName + InformationUrl = $instance.InformationUrl + IsFeatured = $instance.IsFeatured + Notes = $instance.Notes + Owner = $instance.Owner + PrivacyInformationUrl = $instance.PrivacyInformationUrl + Publisher = $instance.Publisher + PublishingState = $instance.PublishingState.ToString() + RoleScopeTagIds = $instance.RoleScopeTagIds + BundleId = $instance.BundleId + BuildNumber = $instance.BuildNumber + VersionNumber = $instance.VersionNumber + IgnoreVersionDetection = $instance.AdditionalProperties.ignoreVersionDetection + Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint + ApplicationSecret = $ApplicationSecret ManagedIdentity = $ManagedIdentity.IsPresent AccessTokens = $AccessTokens } + + #region complex types + + #Categories + if($null -ne $instance.Categories) + { + $results.Add('Categories', $instance.Categories) + } + else { + $results.Add('Categories', "") + } + + #childApps + if($null -ne $instance.AdditionalProperties.childApps) + { + $results.Add('ChildApps', $instance.AdditionalProperties.childApps) + } + else { + $results.Add('ChildApps', "") + } + + #Assignments + $resultAssignments = @() + $appAssignments = Get-MgBetaDeviceAppManagementMobileAppAssignment -MobileAppId $instance.Id + if ($null -ne $appAssignments -and $appAssignments.count -gt 0) + { + $resultAssignments += ConvertFrom-IntuneMobileAppAssignment ` + -IncludeDeviceFilter:$true ` + -Assignments ($appAssignments) + + $results.Add('Assignments', $resultAssignments) + } + + #LargeIcon + # The large is returned only when Get cmdlet is called with Id parameter. The large icon is a base64 encoded string, so we need to convert it to a byte array. + $instanceWithLargeIcon = Get-MgBetaDeviceAppManagementMobileApp -MobileAppId $instance.Id + if($null -ne $instanceWithLargeIcon.LargeIcon) + { + $results.Add('LargeIcon', $instanceWithLargeIcon.LargeIcon) + } + else { + $results.Add('LargeIcon', "") + } + + #end region complex types + return [System.Collections.Hashtable] $results } catch @@ -106,12 +257,90 @@ function Set-TargetResource [CmdletBinding()] param ( - ##TODO - Replace the PrimaryKey + #region Intune resource parameters + + [Parameter()] + [System.String] + $Id, + [Parameter(Mandatory = $true)] [System.String] - $PrimaryKey, + $DisplayName, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $Developer, + + [Parameter()] + [System.String] + $InformationUrl, - ##TODO - Add the list of Parameters + [Parameter()] + [System.Boolean] + $IsFeatured, + + [Parameter()] + [System.Boolean] + $IgnoreVersionDetection, + + [Parameter()] + [System.String] + $Notes, + + [Parameter()] + [System.String] + $Owner, + + [Parameter()] + [System.String] + $PrivacyInformationUrl, + + [Parameter()] + [System.String] + $Publisher, + + [Parameter()] + [System.String] + [ValidateSet('notPublished', 'processing','published')] + $PublishingState, + + [Parameter()] + [System.String[]] + $RoleScopeTagIds, + + [Parameter()] + [System.String] + $BundleId, + + [Parameter()] + [System.String] + $BuildNumber, + + [Parameter()] + [System.String] + $VersionNumber, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Categories, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Assignments, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $ChildApps, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $LargeIcon, + + #endregion [Parameter()] [ValidateSet('Present', 'Absent')] @@ -134,6 +363,10 @@ function Set-TargetResource [System.String] $CertificateThumbprint, + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + [Parameter()] [Switch] $ManagedIdentity, @@ -156,26 +389,136 @@ function Set-TargetResource #endregion $currentInstance = Get-TargetResource @PSBoundParameters + $PSBoundParameters.Remove('Ensure') | Out-Null + $PSBoundParameters.Remove('Credential') | Out-Null + $PSBoundParameters.Remove('ApplicationId') | Out-Null + $PSBoundParameters.Remove('ApplicationSecret') | Out-Null + $PSBoundParameters.Remove('TenantId') | Out-Null + $PSBoundParameters.Remove('CertificateThumbprint') | Out-Null + $PSBoundParameters.Remove('ManagedIdentity') | Out-Null + $PSBoundParameters.Remove('AccessTokens') | Out-Null - $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + $CreateParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { - ##TODO - Replace by the New cmdlet for the resource - New-Cmdlet @SetParameters + Write-Host "Create MacOS app: $DisplayName" + + $CreateParameters = ([Hashtable]$PSBoundParameters).clone() + $CreateParameters = Rename-M365DSCCimInstanceParameter -Properties $CreateParameters + + $AdditionalProperties = Get-M365DSCIntuneMobileMocOSLobAppAdditionalProperties -Properties ($CreateParameters) + foreach ($key in $AdditionalProperties.keys) + { + if ($key -ne '@odata.type') + { + $keyName = $key.substring(0, 1).ToUpper() + $key.substring(1, $key.length - 1) + $CreateParameters.remove($keyName) + } + } + + $CreateParameters.remove('Id') | Out-Null + $CreateParameters.remove('Ensure') | Out-Null + $CreateParameters.remove('Categories') | Out-Null + $CreateParameters.remove('Assignments') | Out-Null + $CreateParameters.remove('childApps') | Out-Null + $CreateParameters.remove('IgnoreVersionDetection') | Out-Null + $CreateParameters.Remove('Verbose') | Out-Null + $CreateParameters.Remove('PublishingState') | Out-Null #Not allowed to update as it's a computed property + $CreateParameters.Remove('LargeIcon') | Out-Null + + foreach ($key in ($CreateParameters.clone()).Keys) + { + if ($CreateParameters[$key].getType().Fullname -like '*CimInstance*') + { + $CreateParameters[$key] = Convert-M365DSCDRGComplexTypeToHashtable -ComplexObject $CreateParameters[$key] + } + } + + if ($AdditionalProperties) + { + $CreateParameters.add('AdditionalProperties', $AdditionalProperties) + } + + #LargeIcon + if($LargeIcon) + { + [System.Object]$LargeIconValue = ConvertTo-M365DSCIntuneAppLargeIcon -LargeIcon $LargeIcon + $CreateParameters.Add('LargeIcon', $LargeIconValue) + } + + $app = New-MgBetaDeviceAppManagementMobileApp @CreateParameters + + #Assignments + $assignmentsHash = ConvertTo-IntuneMobileAppAssignment -IncludeDeviceFilter:$true -Assignments $Assignments + if ($app.id) + { + Update-MgBetaDeviceAppManagementMobileAppAssignment -MobileAppId $app.id ` + -Target $assignmentsHash ` + -Repository 'deviceAppManagement/mobileAppAssignments' + } } # UPDATE elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') { - ##TODO - Replace by the Update/Set cmdlet for the resource - Set-cmdlet @SetParameters + Write-Host "Update MacOS app: $DisplayName" + + $PSBoundParameters.Remove('Assignments') | Out-Null + $UpdateParameters = ([Hashtable]$PSBoundParameters).clone() + $UpdateParameters = Rename-M365DSCCimInstanceParameter -Properties $UpdateParameters + + $AdditionalProperties = Get-M365DSCIntuneMobileMocOSLobAppAdditionalProperties -Properties ($UpdateParameters) + foreach ($key in $AdditionalProperties.keys) + { + if ($key -ne '@odata.type') + { + $keyName = $key.substring(0, 1).ToUpper() + $key.substring(1, $key.length - 1) + #Remove additional keys, so that later they can be added as 'AdditionalProperties' + $UpdateParameters.Remove($keyName) + } + } + + $UpdateParameters.Remove('Id') | Out-Null + $UpdateParameters.Remove('Verbose') | Out-Null + $UpdateParameters.Remove('Categories') | Out-Null + $UpdateParameters.Remove('PublishingState') | Out-Null #Not allowed to update as it's a computed property + + foreach ($key in ($UpdateParameters.clone()).Keys) + { + if ($UpdateParameters[$key].getType().Fullname -like '*CimInstance*') + { + $value = Convert-M365DSCDRGComplexTypeToHashtable -ComplexObject $UpdateParameters[$key] + $UpdateParameters[$key] = $value + } + } + + if ($AdditionalProperties) + { + $UpdateParameters.Add('AdditionalProperties', $AdditionalProperties) + } + + #LargeIcon + if($LargeIcon) + { + [System.Object]$LargeIconValue = ConvertTo-M365DSCIntuneAppLargeIcon -LargeIcon $LargeIcon + $UpdateParameters.Add('LargeIcon', $LargeIconValue) + } + + Update-MgBetaDeviceAppManagementMobileApp -MobileAppId $currentInstance.Id @UpdateParameters + Write-Host "Updated MacOS App: $DisplayName." + + #Assignments + $assignmentsHash = ConvertTo-IntuneMobileAppAssignment -IncludeDeviceFilter:$true -Assignments $Assignments + Update-MgBetaDeviceAppManagementMobileAppAssignment -MobileAppId $currentInstance.id ` + -Target $assignmentsHash ` + -Repository 'deviceAppManagement/mobileAppAssignments' } # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') { - ##TODO - Replace by the Remove cmdlet for the resource - Remove-cmdlet @SetParameters + Write-Host "Remove MacOS app: $DisplayName" + Remove-MgBetaDeviceAppManagementMobileApp -MobileAppId $currentInstance.Id -Confirm:$false } } @@ -185,12 +528,90 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( - ##TODO - Replace the PrimaryKey + #region resource parameters + + [Parameter()] + [System.String] + $Id, + [Parameter(Mandatory = $true)] [System.String] - $PrimaryKey, + $DisplayName, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $Developer, + + [Parameter()] + [System.String] + $InformationUrl, - ##TODO - Add the list of Parameters + [Parameter()] + [System.Boolean] + $IsFeatured, + + [Parameter()] + [System.Boolean] + $IgnoreVersionDetection, + + [Parameter()] + [System.String] + $Notes, + + [Parameter()] + [System.String] + $Owner, + + [Parameter()] + [System.String] + $PrivacyInformationUrl, + + [Parameter()] + [System.String] + $Publisher, + + [Parameter()] + [System.String] + [ValidateSet('notPublished', 'processing','published')] + $PublishingState, + + [Parameter()] + [System.String[]] + $RoleScopeTagIds, + + [Parameter()] + [System.String] + $BundleId, + + [Parameter()] + [System.String] + $BuildNumber, + + [Parameter()] + [System.String] + $VersionNumber, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Categories, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Assignments, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $ChildApps, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $LargeIcon, + + #endregion [Parameter()] [ValidateSet('Present', 'Absent')] @@ -213,6 +634,10 @@ function Test-TargetResource [System.String] $CertificateThumbprint, + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + [Parameter()] [Switch] $ManagedIdentity, @@ -234,20 +659,60 @@ function Test-TargetResource Add-M365DSCTelemetryEvent -Data $data #endregion + Write-Verbose -Message "Testing configuration of Intune Mobile MacOS App: {$DisplayName}" + $CurrentValues = Get-TargetResource @PSBoundParameters - $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + if (-not (Test-M365DSCAuthenticationParameter -BoundParameters $CurrentValues)) + { + Write-Verbose "An error occured in Get-TargetResource, the app {$displayName} will not be processed" + throw "An error occured in Get-TargetResource, the app {$displayName} will not be processed. Refer to the event viewer logs for more information." + } + $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() + $ValuesToCheck = Remove-M365DSCAuthenticationParameter -BoundParameters $ValuesToCheck + $ValuesToCheck.Remove('Id') | Out-Null Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" - Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $PSBoundParameters)" + + if ($CurrentValues.Ensure -ne $Ensure) + { + Write-Verbose -Message "Test-TargetResource returned $false" + return $false + } + $testResult = $true + + #Compare Cim instances + foreach ($key in $PSBoundParameters.Keys) + { + $source = $PSBoundParameters.$key + $target = $CurrentValues.$key + if ($source.getType().Name -like '*CimInstance*') + { + $testResult = Compare-M365DSCComplexObject ` + -Source ($source) ` + -Target ($target) + + if (-Not $testResult) + { + $testResult = $false + break + } + + $ValuesToCheck.Remove($key) | Out-Null + } + } - $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` - -Source $($MyInvocation.MyCommand.Source) ` - -DesiredValues $PSBoundParameters ` - -ValuesToCheck $ValuesToCheck.Keys + if ($testResult) + { + $TestResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + } - Write-Verbose -Message "Test-TargetResource returned $testResult" + Write-Verbose -Message "Test-TargetResource returned $TestResult" - return $testResult + return $TestResult } function Export-TargetResource @@ -268,14 +733,14 @@ function Export-TargetResource [System.String] $TenantId, - [Parameter()] - [System.Management.Automation.PSCredential] - $ApplicationSecret, - [Parameter()] [System.String] $CertificateThumbprint, + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + [Parameter()] [Switch] $ManagedIdentity, @@ -285,8 +750,7 @@ function Export-TargetResource $AccessTokens ) - ##TODO - Replace workload - $ConnectionMode = New-M365DSCConnection -Workload 'Workload' ` + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` -InboundParameters $PSBoundParameters #Ensure the proper dependencies are installed in the current environment. @@ -304,12 +768,15 @@ function Export-TargetResource try { $Script:ExportMode = $true - ##TODO - Replace Get-Cmdlet by the cmdlet to retrieve all instances - [array] $Script:exportedInstances = Get-Cmdlet -ErrorAction Stop + [array] $Script:getInstances = Get-MgBetaDeviceAppManagementMobileApp ` + -Filter "isof('microsoft.graph.macOSLobApp')" ` + -ExpandProperty "categories,assignments" ` + -ErrorAction Stop | Where-Object ` + -FilterScript { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.macOSLobApp' } $i = 1 $dscContent = '' - if ($Script:exportedInstances.Length -eq 0) + if ($Script:getInstances.Length -eq 0) { Write-Host $Global:M365DSCEmojiGreenCheckMark } @@ -317,7 +784,8 @@ function Export-TargetResource { Write-Host "`r`n" -NoNewline } - foreach ($config in $Script:exportedInstances) + + foreach ($config in $Script:getInstances) { if ($null -ne $Global:M365DSCExportResourceInstancesCount) { @@ -325,33 +793,137 @@ function Export-TargetResource } $displayedKey = $config.Id - Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline + Write-Host " |---[$i/$($Script:getInstances.Count)] $displayedKey" -NoNewline + $params = @{ - ##TODO - Specify the Primary Key - #PrimaryKey = $config.PrimaryKey + Id = $config.Id + DisplayName = $config.DisplayName + Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint + ApplicationSecret = $ApplicationSecret ManagedIdentity = $ManagedIdentity.IsPresent AccessTokens = $AccessTokens } - $Results = Get-TargetResource @Params + $Results = Get-TargetResource @params $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results + if (-not (Test-M365DSCAuthenticationParameter -BoundParameters $Results)) + { + Write-Verbose "An error occured in Get-TargetResource, the app {$($params.displayName)} will not be processed." + throw "An error occured in Get-TargetResource, the app {$($params.displayName)} will not be processed. Refer to the event viewer logs for more information." + } + + #region complex types + + #Categories + if($null -ne $Results.Categories) + { + $Results.Categories = Get-M365DSCIntuneAppCategoriesAsString -Categories $Results.Categories + } + else { + $Results.Remove('Categories') | Out-Null + } + + #ChildApps + if($null -ne $Results.childApps) + { + $Results.childApps = Get-M365DSCIntuneAppChildAppsAsString -ChildApps $Results.childApps + } + else { + $Results.Remove('childApps') | Out-Null + } + + #Assignments + if ($null -ne $Results.Assignments) + { + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject ([Array]$Results.Assignments) -CIMInstanceName DeviceManagementMobileAppAssignment + + if ($complexTypeStringResult) + { + $Results.Assignments = $complexTypeStringResult + } + else + { + $Results.Remove('Assignments') | Out-Null + } + } + + #LargeIcon + if($null -ne $Results.LargeIcon) + { + $Results.LargeIcon = Get-M365DSCIntuneAppLargeIconAsString -LargeIcon $Results.LargeIcon + } + else + { + $Results.Remove('LargeIcon') | Out-Null + } + + #endregion complex types + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` -ConnectionMode $ConnectionMode ` -ModulePath $PSScriptRoot ` -Results $Results ` -Credential $Credential + + #region complex types + + #Categories + if ($null -ne $Results.Categories) + { + $isCIMArray = $false + if ($Results.Categories.getType().Fullname -like '*[[\]]') + { + $isCIMArray = $true + } + + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Categories' -IsCIMArray:$isCIMArray + } + + #ChildApps + if ($null -ne $Results.childApps) + { + $isCIMArray = $false + if ($Results.childApps.getType().Fullname -like '*[[\]]') + { + $isCIMArray = $true + } + + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'ChildApps' -IsCIMArray:$isCIMArray + } + + #Assignments + if ($null -ne $Results.Assignments) + { + $isCIMArray = $false + if ($Results.Assignments.getType().Fullname -like '*[[\]]') + { + $isCIMArray = $true + } + + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Assignments' -IsCIMArray:$isCIMArray + } + + #LargeIcon + if ($null -ne $Results.LargeIcon) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'LargeIcon' -IsCIMArray:$false + } + + #endregion complex types + $dscContent += $currentDSCBlock Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName $i++ Write-Host $Global:M365DSCEmojiGreenCheckMark } + return $dscContent } catch @@ -368,4 +940,184 @@ function Export-TargetResource } } +#region Helper functions + +function Get-M365DSCIntuneAppCategoriesAsString +{ + [CmdletBinding()] + [OutputType([System.String])] + param( + [Parameter(Mandatory = $true)] + [System.Object[]] + $Categories + ) + + $StringContent = '@(' + $space = ' ' + $indent = ' ' + + $i = 1 + foreach ($category in $Categories) + { + if ($Categories.Count -gt 1) + { + $StringContent += "`r`n" + $StringContent += "$space" + } + + #Only export the displayName, not Id + $StringContent += "MSFT_DeviceManagementMobileAppCategory { `r`n" + $StringContent += "$($space)$($indent)displayName = '" + $category.displayName + "'`r`n" + $StringContent += "$space}" + + $i++ + } + + $StringContent += ')' + + return $StringContent +} + +function Get-M365DSCIntuneAppChildAppsAsString +{ + [CmdletBinding()] + [OutputType([System.String])] + param( + [Parameter(Mandatory = $true)] + [System.Object[]] + $ChildApps + ) + + $StringContent = '@(' + $space = ' ' + $indent = ' ' + + $i = 1 + foreach ($childApp in $ChildApps) + { + if ($ChildApps.Count -gt 1) + { + $StringContent += "`r`n" + $StringContent += "$space" + } + + $StringContent += "MSFT_DeviceManagementMobileAppChildApp { `r`n" + $StringContent += "$($space)$($indent)bundleId = '" + $childApp.bundleId + "'`r`n" + $StringContent += "$($space)$($indent)buildNumber = '" + $childApp.buildNumber + "'`r`n" + $StringContent += "$($space)$($indent)versionNumber = '" + $childApp.versionNumber + "'`r`n" + $StringContent += "$space}" + + $i++ + } + + $StringContent += ')' + + return $StringContent +} + +function Get-M365DSCIntuneMobileMocOSLobAppAdditionalProperties +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = 'true')] + [System.Collections.Hashtable] + $Properties + ) + + $additionalProperties = @( + 'IgnoreVersionDetection' + 'ChildApps' + ) + + $results = @{'@odata.type' = '#microsoft.graph.macOSLobApp' } + $cloneProperties = $Properties.clone() + foreach ($property in $cloneProperties.Keys) + { + if ($property -in $additionalProperties) + { + $propertyName = $property[0].ToString().ToLower() + $property.Substring(1, $property.Length - 1) + if ($properties.$property -and $properties.$property.getType().FullName -like '*CIMInstance*') + { + if ($properties.$property.getType().FullName -like '*[[\]]') + { + $array = @() + foreach ($item in $properties.$property) + { + $array += Convert-M365DSCDRGComplexTypeToHashtable -ComplexObject $item + + } + $propertyValue = $array + } + else + { + $propertyValue = Convert-M365DSCDRGComplexTypeToHashtable -ComplexObject $properties.$property + } + + } + else + { + $propertyValue = $properties.$property + } + + $results.Add($propertyName, $propertyValue) + } + } + + if ($results.Count -eq 1) + { + return $null + } + return $results +} + +function Get-M365DSCIntuneAppLargeIconAsString #Get and Export +{ + [CmdletBinding()] + [OutputType([System.String])] + param( + [Parameter(Mandatory = $true)] + [System.Object] + $LargeIcon + ) + + $space = ' ' + $indent = ' ' + + if ($null -ne $LargeIcon.Value) + { + $StringContent += "`r`n" + $StringContent += "$space" + + $base64String = [System.Convert]::ToBase64String($LargeIcon.Value) # This exports the base64 string (blob) of the byte array, same as we see in Graph API response + + $StringContent += "MSFT_DeviceManagementMimeContent { `r`n" + $StringContent += "$($space)$($indent)type = '" + $LargeIcon.Type + "'`r`n" + $StringContent += "$($space)$($indent)value = '" + $base64String + "'`r`n" + $StringContent += "$space}" + } + + return $StringContent + } + +function ConvertTo-M365DSCIntuneAppLargeIcon #set +{ + [OutputType([System.Object])] + param( + [Parameter(Mandatory = $true)] + [System.Object] + $LargeIcon + ) + + $result = @{ + type = $LargeIcon.Type + value = $iconValue + } + + return $result +} + +#endregion Helper functions + Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/Examples/Resources/ResourceName/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/IntuneMobileAppsWebApp/1-Create.ps1 similarity index 100% rename from Modules/Microsoft365DSC/Examples/Resources/ResourceName/1-Create.ps1 rename to Modules/Microsoft365DSC/Examples/Resources/IntuneMobileAppsWebApp/1-Create.ps1 diff --git a/Modules/Microsoft365DSC/Examples/Resources/ResourceName/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/IntuneMobileAppsWebApp/2-Update.ps1 similarity index 100% rename from Modules/Microsoft365DSC/Examples/Resources/ResourceName/2-Update.ps1 rename to Modules/Microsoft365DSC/Examples/Resources/IntuneMobileAppsWebApp/2-Update.ps1 diff --git a/Modules/Microsoft365DSC/Examples/Resources/ResourceName/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/IntuneMobileAppsWebApp/3-Remove.ps1 similarity index 100% rename from Modules/Microsoft365DSC/Examples/Resources/ResourceName/3-Remove.ps1 rename to Modules/Microsoft365DSC/Examples/Resources/IntuneMobileAppsWebApp/3-Remove.ps1 diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneMobileAppsWebApp.Tests.ps1 similarity index 100% rename from Tests/Unit/Microsoft365DSC/Microsoft365DSC.ResourceName.Tests.ps1 rename to Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneMobileAppsWebApp.Tests.ps1 From 6aa06cc639cf01665f1f206b27c79fbd73d70a73 Mon Sep 17 00:00:00 2001 From: "Katherine Hsu (from Dev Box)" Date: Tue, 8 Oct 2024 19:51:41 -0700 Subject: [PATCH 3/8] replace to webapp --- .../MSFT_IntuneMobileAppsWebApp.psm1 | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 index 1561fcd9ca..e70046b07a 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 @@ -143,10 +143,10 @@ function Get-TargetResource try { $instance = Get-MgBetaDeviceAppManagementMobileApp ` - -Filter "(isof('microsoft.graph.macOSLobApp') and displayName eq '$DisplayName')" ` + -Filter "(isof('microsoft.graph.webApp') and displayName eq '$DisplayName')" ` -ExpandProperty "categories,assignments" ` -ErrorAction SilentlyContinue | Where-Object ` - -FilterScript { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.macOSLobApp' } + -FilterScript { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.webApp' } if ($null -eq $instance) { @@ -155,7 +155,7 @@ function Get-TargetResource -MobileAppId $Id ` -ExpandProperty "categories,assignments" ` -ErrorAction Stop | Where-Object ` - -FilterScript { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.macOSLobApp' } + -FilterScript { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.webApp' } } if ($null -eq $instance) @@ -403,7 +403,7 @@ function Set-TargetResource # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { - Write-Host "Create MacOS app: $DisplayName" + Write-Host "Create web app: $DisplayName" $CreateParameters = ([Hashtable]$PSBoundParameters).clone() $CreateParameters = Rename-M365DSCCimInstanceParameter -Properties $CreateParameters @@ -462,7 +462,7 @@ function Set-TargetResource # UPDATE elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') { - Write-Host "Update MacOS app: $DisplayName" + Write-Host "Update web app: $DisplayName" $PSBoundParameters.Remove('Assignments') | Out-Null $UpdateParameters = ([Hashtable]$PSBoundParameters).clone() @@ -506,7 +506,7 @@ function Set-TargetResource } Update-MgBetaDeviceAppManagementMobileApp -MobileAppId $currentInstance.Id @UpdateParameters - Write-Host "Updated MacOS App: $DisplayName." + Write-Host "Updated web App: $DisplayName." #Assignments $assignmentsHash = ConvertTo-IntuneMobileAppAssignment -IncludeDeviceFilter:$true -Assignments $Assignments @@ -517,7 +517,7 @@ function Set-TargetResource # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') { - Write-Host "Remove MacOS app: $DisplayName" + Write-Host "Remove web app: $DisplayName" Remove-MgBetaDeviceAppManagementMobileApp -MobileAppId $currentInstance.Id -Confirm:$false } } @@ -659,7 +659,7 @@ function Test-TargetResource Add-M365DSCTelemetryEvent -Data $data #endregion - Write-Verbose -Message "Testing configuration of Intune Mobile MacOS App: {$DisplayName}" + Write-Verbose -Message "Testing configuration of Intune Mobile web App: {$DisplayName}" $CurrentValues = Get-TargetResource @PSBoundParameters if (-not (Test-M365DSCAuthenticationParameter -BoundParameters $CurrentValues)) @@ -769,10 +769,10 @@ function Export-TargetResource { $Script:ExportMode = $true [array] $Script:getInstances = Get-MgBetaDeviceAppManagementMobileApp ` - -Filter "isof('microsoft.graph.macOSLobApp')" ` + -Filter "isof('microsoft.graph.webApp')" ` -ExpandProperty "categories,assignments" ` -ErrorAction Stop | Where-Object ` - -FilterScript { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.macOSLobApp' } + -FilterScript { $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.webApp' } $i = 1 $dscContent = '' @@ -1031,7 +1031,7 @@ function Get-M365DSCIntuneMobileMocOSLobAppAdditionalProperties 'ChildApps' ) - $results = @{'@odata.type' = '#microsoft.graph.macOSLobApp' } + $results = @{'@odata.type' = '#microsoft.graph.webApp' } $cloneProperties = $Properties.clone() foreach ($property in $cloneProperties.Keys) { From 510ee139ddc5e664f075ffca46fce9ecdaf6f569 Mon Sep 17 00:00:00 2001 From: "Katherine Hsu (from Dev Box)" Date: Tue, 8 Oct 2024 21:59:19 -0700 Subject: [PATCH 4/8] adjust parameters --- .../MSFT_IntuneMobileAppsWebApp.psm1 | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 index e70046b07a..363121d5c1 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 @@ -30,10 +30,6 @@ function Get-TargetResource [System.Boolean] $IsFeatured, - [Parameter()] - [System.Boolean] - $IgnoreVersionDetection, - [Parameter()] [System.String] $Notes, @@ -60,32 +56,52 @@ function Get-TargetResource $RoleScopeTagIds, [Parameter()] - [System.String] - $BundleId, + [Microsoft.Management.Infrastructure.CimInstance[]] + $Categories, [Parameter()] - [System.String] - $BuildNumber, + [Microsoft.Management.Infrastructure.CimInstance[]] + $Assignments, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $LargeIcon, [Parameter()] [System.String] - $VersionNumber, + $AppUrl, [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance[]] - $Categories, + [System.Int32] + $SupersededAppCount, [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance[]] - $Assignments, + [System.Int32] + $SupersedingAppCount, [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance[]] - $ChildApps, + [System.Int32] + $DependentAppCount, [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance] - $LargeIcon, + [System.Boolean] + $IsAssigned, + + [Parameter()] + [System.Int32] + $UploadState, + + [Parameter()] + [System.DateTime] + $CreatedDateTime, + + [Parameter()] + [System.DateTime] + $LastModifiedDateTime, + + [Parameter()] + [System.Boolean] + $useManagedBrowser, #endregion @@ -283,10 +299,6 @@ function Set-TargetResource [System.Boolean] $IsFeatured, - [Parameter()] - [System.Boolean] - $IgnoreVersionDetection, - [Parameter()] [System.String] $Notes, @@ -312,18 +324,6 @@ function Set-TargetResource [System.String[]] $RoleScopeTagIds, - [Parameter()] - [System.String] - $BundleId, - - [Parameter()] - [System.String] - $BuildNumber, - - [Parameter()] - [System.String] - $VersionNumber, - [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Categories, @@ -332,14 +332,22 @@ function Set-TargetResource [Microsoft.Management.Infrastructure.CimInstance[]] $Assignments, - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance[]] - $ChildApps, - [Parameter()] [Microsoft.Management.Infrastructure.CimInstance] $LargeIcon, + [Parameter()] + [System.String] + $AppUrl, + + [Parameter()] + [System.Boolean] + $IsAssigned, + + [Parameter()] + [System.Boolean] + $useManagedBrowser, + #endregion [Parameter()] @@ -554,10 +562,6 @@ function Test-TargetResource [System.Boolean] $IsFeatured, - [Parameter()] - [System.Boolean] - $IgnoreVersionDetection, - [Parameter()] [System.String] $Notes, @@ -583,18 +587,6 @@ function Test-TargetResource [System.String[]] $RoleScopeTagIds, - [Parameter()] - [System.String] - $BundleId, - - [Parameter()] - [System.String] - $BuildNumber, - - [Parameter()] - [System.String] - $VersionNumber, - [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $Categories, @@ -603,14 +595,22 @@ function Test-TargetResource [Microsoft.Management.Infrastructure.CimInstance[]] $Assignments, - [Parameter()] - [Microsoft.Management.Infrastructure.CimInstance[]] - $ChildApps, - [Parameter()] [Microsoft.Management.Infrastructure.CimInstance] $LargeIcon, + [Parameter()] + [System.String] + $AppUrl, + + [Parameter()] + [System.Boolean] + $IsAssigned, + + [Parameter()] + [System.Boolean] + $useManagedBrowser + #endregion [Parameter()] From 1d3d24cbc9e4452d5d34da650ef58ddbfe60bd60 Mon Sep 17 00:00:00 2001 From: "Katherine Hsu (from Dev Box)" Date: Tue, 8 Oct 2024 23:04:54 -0700 Subject: [PATCH 5/8] update mof file --- .../MSFT_IntuneMobileAppsWebApp.mof | 79 +++++++++++++++++-- .../MSFT_IntuneMobileAppsWebApp.psm1 | 8 +- 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.mof index fe8aabccae..a02fcc50dd 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.mof @@ -1,14 +1,81 @@ -[ClassVersion("1.0.0.0"), FriendlyName("ResourceName")] -class MSFT_ResourceName : OMI_BaseResource +[ClassVersion("1.0.0"), FriendlyName("IntuneMobileAppsWebApp")] +class MSFT_IntuneMobileAppsWebApp : OMI_BaseResource { - [Key, Description("")] String PrimaryKey; - [Write, Description("")] String OtherProperties; + [Key, Description("The admin provided or imported title of the app. Inherited from mobileApp.")] String DisplayName; + [Write, Description("The unique identifier for an entity. Read-only. Inherited from mobileApp object.")] String Id; - [Write, Description("Present ensures the instance exists, absent ensures it is removed."), ValueMap{"Absent","Present"}, Values{"Absent","Present"}] string Ensure; - [Write, Description("Credentials of the workload's Admin"), EmbeddedInstance("MSFT_Credential")] string Credential; + [Write, Description("The description of the app. Inherited from mobileApp.")] String Description; + [Write, Description("The dewveloper of the app. Inherited from mobileApp.")] String Developer; + [Write, Description("The InformationUrl of the app. Inherited from mobileApp.")] String InformationUrl; + [Write, Description("The value indicating whether the app is marked as featured by the admin. Inherited from mobileApp.")] Boolean IsFeatured; + [Write, Description("Notes for the app. Inherited from mobileApp.")] String Notes; + [Write, Description("The owner of the app. Inherited from mobileApp.")] String Owner; + [Write, Description("The privacy statement Url. Inherited from mobileApp.")] String PrivacyInformationUrl; + [Write, Description("The publisher of the app. Inherited from mobileApp.")] String Publisher; + [Write, Description("The publishing state for the app. The app cannot be assigned unless the app is published. Inherited from mobileApp."), ValueMap{"notPublished", "processing","published"}, Values{"notPublished", "processing", "published"}] String PublishingState; + [Write, Description("List of Scope Tag IDs for mobile app.")] String RoleScopeTagIds[]; + [Write, Description("The list of categories for this app."), EmbeddedInstance("MSFT_DeviceManagementMobileAppCategory")] String Categories[]; + [Write, Description("The list of assignments for this app."), EmbeddedInstance("MSFT_DeviceManagementMobileAppAssignment")] String Assignments[]; + [Write, Description("The icon for this app."), EmbeddedInstance("MSFT_DeviceManagementMimeContent")] String LargeIcon; + [Write, Description("The URL of the web app.")] String AppUrl; + [Write, Description("The number of apps this app has superseded. Read-only.")] UInt32 SupersededAppCount; + [Write, Description("The number of apps that supersede this app. Read-only.")] UInt32 SupersedingAppCount; + [Write, Description("The number of apps that depend on this app. Read-only.")] UInt32 DependentAppCount; + [Write, Description("Indicates whether the app is assigned to any groups.")] Boolean IsAssigned; + [Write, Description("The current upload state of the app. Read-only.")] UInt32 UploadState; + [Write, Description("The date and time the app was created. Read-only.")] DateTime CreatedDateTime; + [Write, Description("The date and time the app was last modified. Read-only.")] DateTime LastModifiedDateTime; + [Write, Description("Whether or not to use a managed browser for this app. Applicable only for Android and iOS.")] Boolean UseManagedBrowser; + + [Write, Description("Present ensures the policy exists, absent ensures it is removed."), ValueMap{"Present", "Absent"}, Values{"Present", "Absent"}] String Ensure; + [Write, Description("Credentials of the Admin"), 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 tenant used for authentication."), 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; [Write, Description("Access token used for authentication.")] String AccessTokens[]; }; + +class MSFT_DeviceManagementMimeContent +{ + [Write, Description("Indicates the type of content mime.")] String type; + [Write, Description("The byte array that contains the actual content.")] String value[]; +}; + +class MSFT_DeviceManagementMobileAppCategory +{ + [Key, Description("The name of the app category.")] String displayName; + [Write, Description("The unique identifier for an entity. Read-only.")] String id; +}; + +class MSFT_DeviceManagementMobileAppChildApp +{ + [Write, Description("The bundleId of the app.")] String bundleId; + [Write, Description("The build number of the app.")] String buildNumber; + [Write, Description("The version number of the app.")] String versionNumber; +}; + +class MSFT_DeviceManagementMobileAppAssignment +{ + [Write, Description("The type of the target assignment."), + ValueMap{"#microsoft.graph.groupAssignmentTarget","#microsoft.graph.allLicensedUsersAssignmentTarget","#microsoft.graph.allDevicesAssignmentTarget","#microsoft.graph.exclusionGroupAssignmentTarget", "#microsoft.graph.mobileAppAssignment"}, + Values{"#microsoft.graph.groupAssignmentTarget","#microsoft.graph.allLicensedUsersAssignmentTarget","#microsoft.graph.allDevicesAssignmentTarget","#microsoft.graph.exclusionGroupAssignmentTarget", "#microsoft.graph.mobileAppAssignment"}] + String dataType; + + [Write, Description("The Id of the filter for the target assignment.")] String deviceAndAppManagementAssignmentFilterId; + [Write, Description("The type of filter of the target assignment i.e. Exclude or Include. Possible values are: none, include, exclude."), + ValueMap{"none", "include", "exclude"}, + Values{"none", "include", "exclude"}] + String deviceAndAppManagementAssignmentFilterType; + + [Write, Description("The group Id that is the target of the assignment.")] String groupId; + [Write, Description("The group Display Name that is the target of the assignment.")] String groupDisplayName; + + [Write, Description("Possible values for the install intent chosen by the admin."), + ValueMap{"available", "required", "uninstall", "availableWithoutEnrollment"}, + Values{"available", "required", "uninstall", "availableWithoutEnrollment"}] + String intent; + + [Write, Description("The source of this assignment.")] String source; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 index 363121d5c1..840f88fea8 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 @@ -6,14 +6,14 @@ function Get-TargetResource ( #region Intune resource parameters - [Parameter()] - [System.String] - $Id, - [Parameter(Mandatory = $true)] [System.String] $DisplayName, + [Parameter()] + [System.String] + $Id, + [Parameter()] [System.String] $Description, From fc01437f26ff141466abde579bd5304a5b026e00 Mon Sep 17 00:00:00 2001 From: "Katherine Hsu (from Dev Box)" Date: Tue, 8 Oct 2024 23:09:22 -0700 Subject: [PATCH 6/8] update readme and settings json file --- .../MSFT_IntuneMobileAppsWebApp/readme.md | 4 +-- .../MSFT_IntuneMobileAppsWebApp/settings.json | 28 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/readme.md index 32e0e7fb27..88b3fa5981 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/readme.md +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/readme.md @@ -1,6 +1,6 @@ -# ResourceName +# IntuneMobileAppsWebApp ## Description -##TODO - Provide a short description of what the resource is set to configure. +This resource configures an Intune mobile app of WebApp type. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/settings.json index edf14b05e4..6e76e91c3b 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/settings.json @@ -1,29 +1,29 @@ { - "resourceName": "ResourceName", - "description": "Description of what the resource is about.", - "roles": { - "read": [ - "Role" - ], - "update": [ - "Role" - ] - }, + "resourceName": "IntuneMobileAppsWebApp", + "description": "This resource configures an Intune mobile app.", "permissions": { "graph": { "delegated": { - "read": [], - "update": [] + "read": [ + { + "name": "DeviceManagementApps.Read.All" + } + ], + "update": [ + { + "name": "DeviceManagementApps.ReadWrite.All" + } + ] }, "application": { "read": [ { - "name": "Permission for Monitoring and Export" + "name": "DeviceManagementApps.Read.All" } ], "update": [ { - "name": "Permission for deploying" + "name": "DeviceManagementApps.ReadWrite.All" } ] } From 3bc915ac2e8058dc68574854c0241b5c1e417495 Mon Sep 17 00:00:00 2001 From: "Katherine Hsu (from Dev Box)" Date: Tue, 8 Oct 2024 23:18:08 -0700 Subject: [PATCH 7/8] update helper functions --- .../MSFT_IntuneMobileAppsWebApp.psm1 | 78 ++++++------------- 1 file changed, 22 insertions(+), 56 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 index 840f88fea8..18ab806e57 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsWebApp/MSFT_IntuneMobileAppsWebApp.psm1 @@ -101,7 +101,7 @@ function Get-TargetResource [Parameter()] [System.Boolean] - $useManagedBrowser, + $UseManagedBrowser, #endregion @@ -193,10 +193,15 @@ function Get-TargetResource Publisher = $instance.Publisher PublishingState = $instance.PublishingState.ToString() RoleScopeTagIds = $instance.RoleScopeTagIds - BundleId = $instance.BundleId - BuildNumber = $instance.BuildNumber - VersionNumber = $instance.VersionNumber - IgnoreVersionDetection = $instance.AdditionalProperties.ignoreVersionDetection + AppUrl = $instance.AppUrl + SupersededAppCount = $instance.SupersededAppCount + SupersedingAppCount = $instance.SupersedingAppCount + DependentAppCount = $instance.DependentAppCount + IsAssigned = $instance.IsAssigned + UploadState = $instance.UploadState + CreatedDateTime = $instance.CreatedDateTime + LastModifiedDateTime = $instance.LastModifiedDateTime + UseManagedBrowser = $instance.UseManagedBrowser Ensure = 'Present' Credential = $Credential @@ -219,15 +224,6 @@ function Get-TargetResource $results.Add('Categories', "") } - #childApps - if($null -ne $instance.AdditionalProperties.childApps) - { - $results.Add('ChildApps', $instance.AdditionalProperties.childApps) - } - else { - $results.Add('ChildApps', "") - } - #Assignments $resultAssignments = @() $appAssignments = Get-MgBetaDeviceAppManagementMobileAppAssignment -MobileAppId $instance.Id @@ -416,7 +412,7 @@ function Set-TargetResource $CreateParameters = ([Hashtable]$PSBoundParameters).clone() $CreateParameters = Rename-M365DSCCimInstanceParameter -Properties $CreateParameters - $AdditionalProperties = Get-M365DSCIntuneMobileMocOSLobAppAdditionalProperties -Properties ($CreateParameters) + $AdditionalProperties = Get-M365DSCIntuneMobileWebAppAdditionalProperties -Properties ($CreateParameters) foreach ($key in $AdditionalProperties.keys) { if ($key -ne '@odata.type') @@ -476,7 +472,7 @@ function Set-TargetResource $UpdateParameters = ([Hashtable]$PSBoundParameters).clone() $UpdateParameters = Rename-M365DSCCimInstanceParameter -Properties $UpdateParameters - $AdditionalProperties = Get-M365DSCIntuneMobileMocOSLobAppAdditionalProperties -Properties ($UpdateParameters) + $AdditionalProperties = Get-M365DSCIntuneMobileWebAppAdditionalProperties -Properties ($UpdateParameters) foreach ($key in $AdditionalProperties.keys) { if ($key -ne '@odata.type') @@ -978,44 +974,7 @@ function Get-M365DSCIntuneAppCategoriesAsString return $StringContent } -function Get-M365DSCIntuneAppChildAppsAsString -{ - [CmdletBinding()] - [OutputType([System.String])] - param( - [Parameter(Mandatory = $true)] - [System.Object[]] - $ChildApps - ) - - $StringContent = '@(' - $space = ' ' - $indent = ' ' - - $i = 1 - foreach ($childApp in $ChildApps) - { - if ($ChildApps.Count -gt 1) - { - $StringContent += "`r`n" - $StringContent += "$space" - } - - $StringContent += "MSFT_DeviceManagementMobileAppChildApp { `r`n" - $StringContent += "$($space)$($indent)bundleId = '" + $childApp.bundleId + "'`r`n" - $StringContent += "$($space)$($indent)buildNumber = '" + $childApp.buildNumber + "'`r`n" - $StringContent += "$($space)$($indent)versionNumber = '" + $childApp.versionNumber + "'`r`n" - $StringContent += "$space}" - - $i++ - } - - $StringContent += ')' - - return $StringContent -} - -function Get-M365DSCIntuneMobileMocOSLobAppAdditionalProperties +function Get-M365DSCIntuneMobileWebAppAdditionalProperties { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] @@ -1027,8 +986,15 @@ function Get-M365DSCIntuneMobileMocOSLobAppAdditionalProperties ) $additionalProperties = @( - 'IgnoreVersionDetection' - 'ChildApps' + 'AppUrl' + 'SupersededAppCount' + 'SupersedingAppCount' + 'DependentAppCount' + 'IsAssigned' + 'UploadState' + 'CreatedDateTime' + 'LastModifiedDateTime' + 'UseManagedBrowser' ) $results = @{'@odata.type' = '#microsoft.graph.webApp' } From e67c2ed79616d5b26be218f950fa1e9e353be1d6 Mon Sep 17 00:00:00 2001 From: "Katherine Hsu (from Dev Box)" Date: Tue, 8 Oct 2024 23:44:35 -0700 Subject: [PATCH 8/8] update web app test --- ...oft365DSC.IntuneMobileAppsWebApp.Tests.ps1 | 175 ++++++++++-------- 1 file changed, 95 insertions(+), 80 deletions(-) diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneMobileAppsWebApp.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneMobileAppsWebApp.Tests.ps1 index 780e0f343d..a33a1bf65a 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneMobileAppsWebApp.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneMobileAppsWebApp.Tests.ps1 @@ -35,124 +35,122 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { return "Credentials" } - ##TODO - Mock any Remove/Set/New cmdlets + Mock -CommandName Get-MgBetaDeviceAppManagementMobileApp -MockWith { + } + Mock -CommandName New-MgBetaDeviceAppManagementMobileApp -MockWith { + } + Mock -CommandName Update-MgBetaDeviceAppManagementMobileApp -MockWith { + } + Mock -CommandName Remove-MgBetaDeviceAppManagementMobileApp -MockWith { + } + + Mock -CommandName Update-MgBetaDeviceAppManagementMobileAppAssignment -MockWith{} # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { } + $Script:exportedInstances =$null $Script:ExportMode = $false } + # Test contexts - Context -Name "The instance should exist but it DOES NOT" -Fixture { + Context -Name "1. The instance should exist but it DOES NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + Id = "8d027f94-0682-431e-97c1-827d1879fa79" + Description = "Sample Web App" + Developer = "Contoso" + DisplayName = "SampleWebApp" + InformationUrl = "" + IsFeatured = $False + Notes = "" + Owner = "" + PrivacyInformationUrl = "" + Publisher = "Contoso" + PublishingState = "published" + RoleScopeTagIds = @() + Ensure = 'Present' + Credential = $Credential } - ##TODO - Mock the Get-Cmdlet to return $null - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-MgBetaDeviceAppManagementMobileApp -MockWith { return $null } } - It 'Should return Values from the Get method' { - (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' - } - It 'Should return false from the Test method' { - Test-TargetResource @testParams | Should -Be $false - } - It 'Should create a new instance from the Set method' { - ##TODO - Replace the New-Cmdlet by the appropriate one - Set-TargetResource @testParams - Should -Invoke -CommandName New-Cmdlet -Exactly 1 - } - } - - Context -Name "The instance exists but it SHOULD NOT" -Fixture { - BeforeAll { - $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Absent' - Credential = $Credential; - } - - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { - return @{ - - } - } - } - It 'Should return Values from the Get method' { - (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + It '1.1 Should return Values from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' } - It 'Should return false from the Test method' { + It '1.2 Should return false from the Test method' { Test-TargetResource @testParams | Should -Be $false } - - It 'Should remove the instance from the Set method' { + It '1.3 Should create a new instance from the Set method' { Set-TargetResource @testParams - ##TODO - Replace the Remove-Cmdlet by the appropriate one - Should -Invoke -CommandName Remove-Cmdlet -Exactly 1 + Should -Invoke -CommandName New-MgBetaDeviceAppManagementMobileApp -Exactly 1 } } - Context -Name "The instance exists and values are already in the desired state" -Fixture { + Context -Name "2. The instance exists but it SHOULD NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + Id = "ad027f94-0682-431e-97c1-827d1879fa79" + Description = "Sample Web App" + Developer = "Contoso" + DisplayName = "SampleWebApp" + InformationUrl = "" + IsFeatured = $False + Notes = "" + Owner = "" + PrivacyInformationUrl = "" + Publisher = "Contoso" + PublishingState = "published" + RoleScopeTagIds = @() + IgnoreVersionDetection = $True + Ensure = 'Absent' + Credential = $Credential } - ##TODO - Mock the Get-Cmdlet to return the desired values - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-MgBetaDeviceAppManagementMobileApp -MockWith { return @{ - + Id = "ad027f94-0682-431e-97c1-827d1879fa79" + Description = "Sample Web App" + Developer = "Contoso" + DisplayName = "SampleWebApp" + InformationUrl = "" + IsFeatured = $False + Notes = "" + Owner = "" + PrivacyInformationUrl = "" + Publisher = "Contoso" + PublishingState = "published" + RoleScopeTagIds = @() + IgnoreVersionDetection = $True + AdditionalProperties = @{ + '@odata.type' = '#microsoft.graph.webApp' + } + Ensure = 'Present' } } - } - It 'Should return true from the Test method' { - Test-TargetResource @testParams | Should -Be $true - } - } - - Context -Name "The instance exists and values are NOT in the desired state" -Fixture { - BeforeAll { - $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; - } - - ##TODO - Mock the Get-Cmdlet to return a drift - Mock -CommandName Get-Cmdlet -MockWith { - return @{ - - } + Mock -CommandName Get-MgBetaDeviceAppManagementMobileAppAssignment -MockWith{ + return $null } } - It 'Should return Values from the Get method' { + It '2.1 Should return Values from the Get method' { (Get-TargetResource @testParams).Ensure | Should -Be 'Present' } - - It 'Should return false from the Test method' { + It '2.2 Should return false from the Test method' { Test-TargetResource @testParams | Should -Be $false } - - It 'Should call the Set method' { + It '2.3 Should remove the instance from the Set method' { Set-TargetResource @testParams - ##TODO - Replace the Update-Cmdlet by the appropriate one - Should -Invoke -CommandName Update-Cmdlet -Exactly 1 + Should -Invoke -CommandName Remove-MgBetaDeviceAppManagementMobileApp -Exactly 1 } } - Context -Name 'ReverseDSC Tests' -Fixture { + Context -Name '5. ReverseDSC Tests' -Fixture { BeforeAll { $Global:CurrentModeIsExport = $true $Global:PartialExportFileName = "$(New-Guid).partial.ps1" @@ -160,14 +158,31 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-MgBetaDeviceAppManagementMobileApp -MockWith { return @{ - + Id = "8d027f94-0682-431e-97c1-827d1879fa79" + Description = "Sample Web App" + Developer = "Contoso" + DisplayName = "SampleWebApp" + InformationUrl = "" + IsFeatured = $False + Notes = "" + Owner = "" + PrivacyInformationUrl = "" + Publisher = "Contoso" + PublishingState = "published" + RoleScopeTagIds = @() + AdditionalProperties = @{ + '@odata.type' = '#microsoft.graph.webApp' + } } } + Mock -CommandName Get-MgBetaDeviceAppManagementMobileAppAssignment -MockWith{ + return $null + } } - It 'Should Reverse Engineer resource from the Export method' { + + It '5.0 Should Reverse Engineer resource from the Export method' { $result = Export-TargetResource @testParams $result | Should -Not -BeNullOrEmpty }