diff --git a/CHANGELOG.md b/CHANGELOG.md index 477ba930a3..90d305f9f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ * IntuneDeviceConfigurationEndpointProtectionPolicyWindows10 * Initial release FIXES [#2834](https://github.com/microsoft/Microsoft365DSC/issues/2834) +* IntuneDeviceConfigurationIdentityProtectionPolicyWindows10 + * Initial release + FIXES [#2831](https://github.com/microsoft/Microsoft365DSC/issues/2831) * SCDLPCompliancePolicy * Added support or Endpoint, On-Premises, PowerBI and ThirdPartyApps locations and exceptions. FIXES [#3023](https://github.com/microsoft/Microsoft365DSC/issues/3023) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10.psm1 new file mode 100644 index 0000000000..63b2cfb5fd --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10.psm1 @@ -0,0 +1,1537 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + #region resource generator code + [Parameter()] + [System.Boolean] + $EnhancedAntiSpoofingForFacialFeaturesEnabled, + + [Parameter()] + [System.Int32] + $PinExpirationInDays, + + [Parameter()] + [ValidateSet('blocked','required','allowed','notConfigured')] + [System.String] + $PinLowercaseCharactersUsage, + + [Parameter()] + [System.Int32] + $PinMaximumLength, + + [Parameter()] + [System.Int32] + $PinMinimumLength, + + [Parameter()] + [System.Int32] + $PinPreviousBlockCount, + + [Parameter()] + [System.Boolean] + $PinRecoveryEnabled, + + [Parameter()] + [ValidateSet('blocked','required','allowed','notConfigured')] + [System.String] + $PinSpecialCharactersUsage, + + [Parameter()] + [ValidateSet('blocked','required','allowed','notConfigured')] + [System.String] + $PinUppercaseCharactersUsage, + + [Parameter()] + [System.Boolean] + $SecurityDeviceRequired, + + [Parameter()] + [System.Boolean] + $UnlockWithBiometricsEnabled, + + [Parameter()] + [System.Boolean] + $UseCertificatesForOnPremisesAuthEnabled, + + [Parameter()] + [System.Boolean] + $UseSecurityKeyForSignin, + + [Parameter()] + [System.Boolean] + $WindowsHelloForBusinessBlocked, + + [Parameter()] + [System.String] + $Description, + + [Parameter(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter()] + [System.Boolean] + $SupportsScopeTags, + + [Parameter(Mandatory = $true)] + [System.String] + $Id, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Assignments, + #endregion + + [Parameter()] + [System.String] + [ValidateSet('Absent', 'Present')] + $Ensure = 'Present', + + [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 + ) + + try + { + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters ` + -ProfileName 'beta' + + #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' + + $getValue = $null + #region resource generator code + $getValue = Get-MgDeviceManagementDeviceConfiguration -DeviceConfigurationId $Id -ErrorAction SilentlyContinue + + if ($null -eq $getValue) + { + Write-Verbose -Message "Could not find an Intune Device Configuration Identity Protection Policy for Windows10 with Id {$Id}" + + if(-Not [string]::IsNullOrEmpty($DisplayName)) + { + $getValue = Get-MgDeviceManagementDeviceConfiguration ` + -Filter "DisplayName eq '$DisplayName'" ` + -ErrorAction SilentlyContinue + } + } + #endregion + if ($null -eq $getValue) + { + Write-Verbose -Message "Could not find an Intune Device Configuration Identity Protection Policy for Windows10 with DisplayName {$DisplayName}" + return $nullResult + } + $Id = $getValue.Id + Write-Verbose -Message "An Intune Device Configuration Identity Protection Policy for Windows10 with Id {$Id} and DisplayName {$DisplayName} was found." + + #region resource generator code + $enumPinLowercaseCharactersUsage = $null + if ($null -ne $getValue.AdditionalProperties.pinLowercaseCharactersUsage) + { + $enumPinLowercaseCharactersUsage = $getValue.AdditionalProperties.pinLowercaseCharactersUsage.ToString() + } + + $enumPinSpecialCharactersUsage = $null + if ($null -ne $getValue.AdditionalProperties.pinSpecialCharactersUsage) + { + $enumPinSpecialCharactersUsage = $getValue.AdditionalProperties.pinSpecialCharactersUsage.ToString() + } + + $enumPinUppercaseCharactersUsage = $null + if ($null -ne $getValue.AdditionalProperties.pinUppercaseCharactersUsage) + { + $enumPinUppercaseCharactersUsage = $getValue.AdditionalProperties.pinUppercaseCharactersUsage.ToString() + } + + #endregion + + $results = @{ + #region resource generator code + EnhancedAntiSpoofingForFacialFeaturesEnabled = $getValue.AdditionalProperties.enhancedAntiSpoofingForFacialFeaturesEnabled + PinExpirationInDays = $getValue.AdditionalProperties.pinExpirationInDays + PinLowercaseCharactersUsage = $enumPinLowercaseCharactersUsage + PinMaximumLength = $getValue.AdditionalProperties.pinMaximumLength + PinMinimumLength = $getValue.AdditionalProperties.pinMinimumLength + PinPreviousBlockCount = $getValue.AdditionalProperties.pinPreviousBlockCount + PinRecoveryEnabled = $getValue.AdditionalProperties.pinRecoveryEnabled + PinSpecialCharactersUsage = $enumPinSpecialCharactersUsage + PinUppercaseCharactersUsage = $enumPinUppercaseCharactersUsage + SecurityDeviceRequired = $getValue.AdditionalProperties.securityDeviceRequired + UnlockWithBiometricsEnabled = $getValue.AdditionalProperties.unlockWithBiometricsEnabled + UseCertificatesForOnPremisesAuthEnabled = $getValue.AdditionalProperties.useCertificatesForOnPremisesAuthEnabled + UseSecurityKeyForSignin = $getValue.AdditionalProperties.useSecurityKeyForSignin + WindowsHelloForBusinessBlocked = $getValue.AdditionalProperties.windowsHelloForBusinessBlocked + Description = $getValue.Description + DisplayName = $getValue.DisplayName + SupportsScopeTags = $getValue.SupportsScopeTags + Id = $getValue.Id + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + ApplicationSecret = $ApplicationSecret + CertificateThumbprint = $CertificateThumbprint + Managedidentity = $ManagedIdentity.IsPresent + #endregion + } + $assignmentsValues = Get-MgDeviceManagementDeviceConfigurationAssignment -DeviceConfigurationId $Id + $assignmentResult = @() + foreach ($assignmentEntry in $AssignmentsValues) + { + $assignmentValue = @{ + dataType = $assignmentEntry.Target.AdditionalProperties.'@odata.type' + deviceAndAppManagementAssignmentFilterType = $(if($null -ne $assignmentEntry.Target.DeviceAndAppManagementAssignmentFilterType) + {$assignmentEntry.Target.DeviceAndAppManagementAssignmentFilterType.ToString()}) + deviceAndAppManagementAssignmentFilterId = $assignmentEntry.Target.DeviceAndAppManagementAssignmentFilterId + groupId = $assignmentEntry.Target.AdditionalProperties.groupId + } + $assignmentResult += $assignmentValue + } + $results.Add('Assignments', $assignmentResult) + + return [System.Collections.Hashtable] $results + } + catch + { + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId ` + -Credential $Credential + + return $nullResult + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + #region resource generator code + [Parameter()] + [System.Boolean] + $EnhancedAntiSpoofingForFacialFeaturesEnabled, + + [Parameter()] + [System.Int32] + $PinExpirationInDays, + + [Parameter()] + [ValidateSet('blocked','required','allowed','notConfigured')] + [System.String] + $PinLowercaseCharactersUsage, + + [Parameter()] + [System.Int32] + $PinMaximumLength, + + [Parameter()] + [System.Int32] + $PinMinimumLength, + + [Parameter()] + [System.Int32] + $PinPreviousBlockCount, + + [Parameter()] + [System.Boolean] + $PinRecoveryEnabled, + + [Parameter()] + [ValidateSet('blocked','required','allowed','notConfigured')] + [System.String] + $PinSpecialCharactersUsage, + + [Parameter()] + [ValidateSet('blocked','required','allowed','notConfigured')] + [System.String] + $PinUppercaseCharactersUsage, + + [Parameter()] + [System.Boolean] + $SecurityDeviceRequired, + + [Parameter()] + [System.Boolean] + $UnlockWithBiometricsEnabled, + + [Parameter()] + [System.Boolean] + $UseCertificatesForOnPremisesAuthEnabled, + + [Parameter()] + [System.Boolean] + $UseSecurityKeyForSignin, + + [Parameter()] + [System.Boolean] + $WindowsHelloForBusinessBlocked, + + [Parameter()] + [System.String] + $Description, + + [Parameter(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter()] + [System.Boolean] + $SupportsScopeTags, + + [Parameter(Mandatory = $true)] + [System.String] + $Id, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Assignments, + #endregion + + [Parameter()] + [System.String] + [ValidateSet('Absent', 'Present')] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $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('Verbose') | Out-Null + + if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + { + Write-Verbose -Message "Creating an Intune Device Configuration Identity Protection Policy for Windows10 with DisplayName {$DisplayName}" + $PSBoundParameters.Remove("Assignments") | Out-Null + + $CreateParameters = ([Hashtable]$PSBoundParameters).clone() + $CreateParameters = Rename-M365DSCCimInstanceParameter -Properties $CreateParameters + $CreateParameters.Remove('Id') | Out-Null + + $keys=(([Hashtable]$CreateParameters).clone()).Keys + foreach($key in $keys) + { + if($null -ne $CreateParameters.$key -and $CreateParameters.$key.getType().Name -like "*cimInstance*") + { + $CreateParameters.$key= Convert-M365DSCDRGComplexTypeToHashtable -ComplexObject $CreateParameters.$key + } + } + #region resource generator code + $CreateParameters.Add("@odata.type", "#microsoft.graph.windowsIdentityProtectionConfiguration") + $policy=New-MgDeviceManagementDeviceConfiguration -BodyParameter $CreateParameters + $assignmentsHash=@() + foreach($assignment in $Assignments) + { + $assignmentsHash+=Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $Assignment + } + + if($policy.id) + { + Update-DeviceConfigurationPolicyAssignment -DeviceConfigurationPolicyId $policy.id ` + -Targets $assignmentsHash ` + -Repository 'deviceManagement/deviceConfigurations' + } + #endregion + } + elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + { + Write-Verbose -Message "Updating the Intune Device Configuration Identity Protection Policy for Windows10 with Id {$($currentInstance.Id)}" + $PSBoundParameters.Remove("Assignments") | Out-Null + + $UpdateParameters = ([Hashtable]$PSBoundParameters).clone() + $UpdateParameters = Rename-M365DSCCimInstanceParameter -Properties $UpdateParameters + + $UpdateParameters.Remove('Id') | Out-Null + + $keys=(([Hashtable]$UpdateParameters).clone()).Keys + foreach($key in $keys) + { + if($null -ne $UpdateParameters.$key -and $UpdateParameters.$key.getType().Name -like "*cimInstance*") + { + $UpdateParameters.$key= Convert-M365DSCDRGComplexTypeToHashtable -ComplexObject $UpdateParameters.$key + } + } + #region resource generator code + $UpdateParameters.Add("@odata.type", "#microsoft.graph.windowsIdentityProtectionConfiguration") + Update-MgDeviceManagementDeviceConfiguration ` + -DeviceConfigurationId $currentInstance.Id ` + -BodyParameter $UpdateParameters + $assignmentsHash=@() + foreach($assignment in $Assignments) + { + $assignmentsHash+=Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $Assignment + } + Update-DeviceConfigurationPolicyAssignment -DeviceConfigurationPolicyId $currentInstance.id ` + -Targets $assignmentsHash ` + -Repository 'deviceManagement/deviceConfigurations' + #endregion + } + elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + { + Write-Verbose -Message "Removing the Intune Device Configuration Identity Protection Policy for Windows10 with Id {$($currentInstance.Id)}" + #region resource generator code + Remove-MgDeviceManagementDeviceConfiguration -DeviceConfigurationId $currentInstance.Id + #endregion + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + #region resource generator code + [Parameter()] + [System.Boolean] + $EnhancedAntiSpoofingForFacialFeaturesEnabled, + + [Parameter()] + [System.Int32] + $PinExpirationInDays, + + [Parameter()] + [ValidateSet('blocked','required','allowed','notConfigured')] + [System.String] + $PinLowercaseCharactersUsage, + + [Parameter()] + [System.Int32] + $PinMaximumLength, + + [Parameter()] + [System.Int32] + $PinMinimumLength, + + [Parameter()] + [System.Int32] + $PinPreviousBlockCount, + + [Parameter()] + [System.Boolean] + $PinRecoveryEnabled, + + [Parameter()] + [ValidateSet('blocked','required','allowed','notConfigured')] + [System.String] + $PinSpecialCharactersUsage, + + [Parameter()] + [ValidateSet('blocked','required','allowed','notConfigured')] + [System.String] + $PinUppercaseCharactersUsage, + + [Parameter()] + [System.Boolean] + $SecurityDeviceRequired, + + [Parameter()] + [System.Boolean] + $UnlockWithBiometricsEnabled, + + [Parameter()] + [System.Boolean] + $UseCertificatesForOnPremisesAuthEnabled, + + [Parameter()] + [System.Boolean] + $UseSecurityKeyForSignin, + + [Parameter()] + [System.Boolean] + $WindowsHelloForBusinessBlocked, + + [Parameter()] + [System.String] + $Description, + + [Parameter(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter()] + [System.Boolean] + $SupportsScopeTags, + + [Parameter(Mandatory = $true)] + [System.String] + $Id, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Assignments, + #endregion + + [Parameter()] + [System.String] + [ValidateSet('Absent', 'Present')] + $Ensure = 'Present', + + [Parameter()] + [System.Management.Automation.PSCredential] + $Credential, + + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ApplicationSecret, + + [Parameter()] + [System.String] + $CertificateThumbprint, + + [Parameter()] + [Switch] + $ManagedIdentity + ) + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + Write-Verbose -Message "Testing configuration of the Intune Device Configuration Identity Protection Policy for Windows10 with Id {$Id} and DisplayName {$DisplayName}" + + $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() + + if ($CurrentValues.Ensure -ne $PSBoundParameters.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*') + { + $source = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $source + + $testResult = Compare-M365DSCComplexObject ` + -Source ($source) ` + -Target ($target) + + if (-Not $testResult) + { + $testResult = $false + break; + } + + $ValuesToCheck.Remove($key) | Out-Null + + } + } + + $ValuesToCheck.Remove('Credential') | Out-Null + $ValuesToCheck.Remove('ApplicationId') | Out-Null + $ValuesToCheck.Remove('TenantId') | Out-Null + $ValuesToCheck.Remove('ApplicationSecret') | Out-Null + + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" + Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" + + if ($testResult) + { + $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 + ) + + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters ` + -ProfileName 'beta' + + #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 + { + #region resource generator code + [array]$getValue = Get-MgDeviceManagementDeviceConfiguration ` + -All ` + -ErrorAction Stop | Where-Object ` + -FilterScript { ` + $_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.windowsIdentityProtectionConfiguration' ` + } + #endregion + + $i = 1 + $dscContent = '' + if ($getValue.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + foreach ($config in $getValue) + { + $displayedKey = $config.Id + if (-not [String]::IsNullOrEmpty($config.displayName)) + { + $displayedKey = $config.displayName + } + Write-Host " |---[$i/$($getValue.Count)] $displayedKey" -NoNewline + $params = @{ + Id = $config.Id + DisplayName = $config.displayName + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + ApplicationSecret = $ApplicationSecret + CertificateThumbprint = $CertificateThumbprint + Managedidentity = $ManagedIdentity.IsPresent + } + + $Results = Get-TargetResource @Params + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + if($Results.Assignments) + { + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString -ComplexObject $Results.Assignments -CIMInstanceName DeviceManagementConfigurationPolicyAssignments + if ($complexTypeStringResult) + { + $Results.Assignments = $complexTypeStringResult + } + else + { + $Results.Remove('Assignments') | Out-Null + } + } + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + if ($Results.Assignments) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName "Assignments" -isCIMArray:$true + } + + $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 '' + } +} +function Update-DeviceConfigurationPolicyAssignment +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param ( + [Parameter(Mandatory = 'true')] + [System.String] + $DeviceConfigurationPolicyId, + + [Parameter()] + [Array] + $Targets, + + [Parameter()] + [System.String] + $Repository='deviceManagement/configurationPolicies', + + [Parameter()] + [ValidateSet('v1.0','beta')] + [System.String] + $APIVersion='beta' + ) + try + { + $deviceManagementPolicyAssignments=@() + $Uri="https://graph.microsoft.com/$APIVersion/$Repository/$DeviceConfigurationPolicyId/assign" + + foreach($target in $targets) + { + $formattedTarget=@{"@odata.type"=$target.dataType} + if($target.groupId) + { + $formattedTarget.Add('groupId',$target.groupId) + } + if($target.collectionId) + { + $formattedTarget.Add('collectionId',$target.collectionId) + } + if($target.deviceAndAppManagementAssignmentFilterType) + { + $formattedTarget.Add('deviceAndAppManagementAssignmentFilterType',$target.deviceAndAppManagementAssignmentFilterType) + } + if($target.deviceAndAppManagementAssignmentFilterId) + { + $formattedTarget.Add('deviceAndAppManagementAssignmentFilterId',$target.deviceAndAppManagementAssignmentFilterId) + } + $deviceManagementPolicyAssignments+=@{'target'= $formattedTarget} + } + $body=@{'assignments'=$deviceManagementPolicyAssignments}|ConvertTo-Json -Depth 20 + #write-verbose -Message $body + Invoke-MgGraphRequest -Method POST -Uri $Uri -Body $body -ErrorAction Stop + } + catch + { + New-M365DSCLogEntry -Message 'Error updating data:' + -Exception $_ + -Source $($MyInvocation.MyCommand.Source) + -TenantId $TenantId + -Credential $Credential + + return $null + } +} + +function Rename-M365DSCCimInstanceParameter +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable],[System.Collections.Hashtable[]])] + param( + [Parameter(Mandatory = 'true')] + $Properties + ) + + $keyToRename=@{ + "odataType"="@odata.type" + } + + $result=$Properties + + $type=$Properties.getType().FullName + + #region Array + if ($type -like '*[[\]]') + { + $values = @() + foreach ($item in $Properties) + { + $values += Rename-M365DSCCimInstanceParameter $item + } + $result=$values + + return ,$result + } + #endregion + + #region Single + if($type -like "*Hashtable") + { + $result=([Hashtable]$Properties).clone() + } + if($type -like '*CimInstance*' -or $type -like '*Hashtable*'-or $type -like '*Object*') + { + $hashProperties = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $result + $keys=($hashProperties.clone()).keys + foreach($key in $keys) + { + $keyName=$key.substring(0,1).tolower()+$key.substring(1,$key.length-1) + if ($key -in $keyToRename.Keys) + { + $keyName=$keyToRename.$key + } + + $property=$hashProperties.$key + if($null -ne $property) + { + $hashProperties.Remove($key) + $hashProperties.add($keyName,(Rename-M365DSCCimInstanceParameter $property)) + } + } + $result = $hashProperties + } + + return $result + #endregion +} + +function Get-M365DSCDRGComplexTypeToHashtable +{ + [CmdletBinding()] + [OutputType([hashtable],[hashtable[]])] + param( + [Parameter()] + $ComplexObject + ) + + if($null -eq $ComplexObject) + { + return $null + } + + if($ComplexObject.gettype().fullname -like "*[[\]]") + { + $results=@() + + foreach($item in $ComplexObject) + { + if($item) + { + $hash = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $item + $results+=$hash + } + } + + # PowerShell returns all non-captured stream output, not just the argument of the return statement. + #An empty array is mangled into $null in the process. + #However, an array can be preserved on return by prepending it with the array construction operator (,) + return ,[hashtable[]]$results + } + + if($ComplexObject.getType().fullname -like '*Dictionary*') + { + $results = @{} + + $ComplexObject=[hashtable]::new($ComplexObject) + $keys=$ComplexObject.Keys + foreach ($key in $keys) + { + if($null -ne $ComplexObject.$key) + { + $keyName = $key + + $keyType=$ComplexObject.$key.gettype().fullname + + if($keyType -like "*CimInstance*" -or $keyType -like "*Dictionary*" -or $keyType -like "Microsoft.Graph.PowerShell.Models.*" -or $keyType -like "*[[\]]") + { + $hash = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $ComplexObject.$key + + $results.Add($keyName, $hash) + } + else + { + $results.Add($keyName, $ComplexObject.$key) + } + } + } + return [hashtable]$results + } + + $results = @{} + + if($ComplexObject.getType().Fullname -like "*hashtable") + { + $keys = $ComplexObject.keys + } + else + { + $keys = $ComplexObject | Get-Member | Where-Object -FilterScript {$_.MemberType -eq 'Property'} + } + + foreach ($key in $keys) + { + $keyName=$key + if($ComplexObject.getType().Fullname -notlike "*hashtable") + { + $keyName=$key.Name + } + + if($null -ne $ComplexObject.$keyName) + { + $keyType=$ComplexObject.$keyName.gettype().fullname + if($keyType -like "*CimInstance*" -or $keyType -like "*Dictionary*" -or $keyType -like "Microsoft.Graph.PowerShell.Models.*" ) + { + $hash = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $ComplexObject.$keyName + + $results.Add($keyName, $hash) + } + else + { + $results.Add($keyName, $ComplexObject.$keyName) + } + } + } + + return [hashtable]$results +} + +<# + Use ComplexTypeMapping to overwrite the type of nested CIM + Example + $complexMapping=@( + @{ + Name="ApprovalStages" + CimInstanceName="MSFT_MicrosoftGraphapprovalstage1" + IsRequired=$false + } + @{ + Name="PrimaryApprovers" + CimInstanceName="MicrosoftGraphuserset" + IsRequired=$false + } + @{ + Name="EscalationApprovers" + CimInstanceName="MicrosoftGraphuserset" + IsRequired=$false + } + ) + With + Name: the name of the parameter to be overwritten + CimInstanceName: The type of the CIM instance (can include or not the prefix MSFT_) + IsRequired: If isRequired equals true, an empty hashtable or array will be returned. Some of the Graph parameters are required even though they are empty +#> +function Get-M365DSCDRGComplexTypeToString +{ + [CmdletBinding()] + param( + [Parameter()] + $ComplexObject, + + [Parameter(Mandatory = $true)] + [System.String] + $CIMInstanceName, + + [Parameter()] + [Array] + $ComplexTypeMapping, + + [Parameter()] + [System.String] + $Whitespace='', + + [Parameter()] + [System.uint32] + $IndentLevel=3, + + [Parameter()] + [switch] + $isArray=$false + ) + + if ($null -eq $ComplexObject) + { + return $null + } + + $indent='' + for ($i = 0; $i -lt $IndentLevel ; $i++) + { + $indent+=' ' + } + #If ComplexObject is an Array + if ($ComplexObject.GetType().FullName -like "*[[\]]") + { + $currentProperty=@() + $IndentLevel++ + foreach ($item in $ComplexObject) + { + $splat=@{ + 'ComplexObject'=$item + 'CIMInstanceName'=$CIMInstanceName + 'IndentLevel'=$IndentLevel + } + if ($ComplexTypeMapping) + { + $splat.add('ComplexTypeMapping',$ComplexTypeMapping) + } + + $currentProperty += Get-M365DSCDRGComplexTypeToString -isArray:$true @splat + } + + # PowerShell returns all non-captured stream output, not just the argument of the return statement. + #An empty array is mangled into $null in the process. + #However, an array can be preserved on return by prepending it with the array construction operator (,) + return ,$currentProperty + } + + $currentProperty='' + if($isArray) + { + $currentProperty += "`r`n" + $currentProperty += $indent + } + + $CIMInstanceName=$CIMInstanceName.replace("MSFT_","") + $currentProperty += "MSFT_$CIMInstanceName{`r`n" + $IndentLevel++ + $indent='' + for ($i = 0; $i -lt $IndentLevel ; $i++) + { + $indent+=' ' + } + $keyNotNull = 0 + + if ($ComplexObject.Keys.count -eq 0) + { + return $null + } + + foreach ($key in $ComplexObject.Keys) + { + if ($null -ne $ComplexObject.$key) + { + $keyNotNull++ + if ($ComplexObject.$key.GetType().FullName -like "Microsoft.Graph.PowerShell.Models.*" -or $key -in $ComplexTypeMapping.Name) + { + $hashPropertyType=$ComplexObject[$key].GetType().Name.tolower() + + $isArray=$false + if($ComplexObject[$key].GetType().FullName -like "*[[\]]") + { + $isArray=$true + } + #overwrite type if object defined in mapping complextypemapping + if($key -in $ComplexTypeMapping.Name) + { + $hashPropertyType=([Array]($ComplexTypeMapping|Where-Object -FilterScript {$_.Name -eq $key}).CimInstanceName)[0] + $hashProperty=$ComplexObject[$key] + } + else + { + $hashProperty=Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $ComplexObject[$key] + } + + if(-not $isArray) + { + $currentProperty += $indent + $key + ' = ' + } + + if($isArray -and $key -in $ComplexTypeMapping.Name ) + { + if($ComplexObject.$key.count -gt 0) + { + $currentProperty += $indent + $key + ' = ' + $currentProperty += "@(" + } + } + + if ($isArray) + { + $IndentLevel++ + foreach ($item in $ComplexObject[$key]) + { + if ($ComplexObject.$key.GetType().FullName -like "Microsoft.Graph.PowerShell.Models.*") + { + $item=Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $item + } + $nestedPropertyString = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $item ` + -CIMInstanceName $hashPropertyType ` + -IndentLevel $IndentLevel ` + -ComplexTypeMapping $ComplexTypeMapping ` + -IsArray:$true + if([string]::IsNullOrWhiteSpace($nestedPropertyString)) + { + $nestedPropertyString = "@()`r`n" + } + $currentProperty += $nestedPropertyString + } + $IndentLevel-- + } + else + { + $nestedPropertyString = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $hashProperty ` + -CIMInstanceName $hashPropertyType ` + -IndentLevel $IndentLevel ` + -ComplexTypeMapping $ComplexTypeMapping + if([string]::IsNullOrWhiteSpace($nestedPropertyString)) + { + $nestedPropertyString = "`$null`r`n" + } + $currentProperty += $nestedPropertyString + } + if($isArray) + { + if($ComplexObject.$key.count -gt 0) + { + $currentProperty += $indent + $currentProperty += ')' + $currentProperty += "`r`n" + } + } + $isArray=$PSBoundParameters.IsArray + } + else + { + $currentProperty += Get-M365DSCDRGSimpleObjectTypeToString -Key $key -Value $ComplexObject[$key] -Space ($indent) + } + } + else + { + $mappedKey=$ComplexTypeMapping|where-object -filterscript {$_.name -eq $key} + + if($mappedKey -and $mappedKey.isRequired) + { + if($mappedKey.isArray) + { + $currentProperty += "$indent$key = @()`r`n" + } + else + { + $currentProperty += "$indent$key = `$null`r`n" + } + } + } + } + $indent='' + for ($i = 0; $i -lt $IndentLevel-1 ; $i++) + { + $indent+=' ' + } + $currentProperty += "$indent}" + if($isArray -or $IndentLevel -gt 4) + { + $currentProperty += "`r`n" + } + + #Indenting last parenthese when the cim instance is an array + if($IndentLevel -eq 5) + { + $indent='' + for ($i = 0; $i -lt $IndentLevel-2 ; $i++) + { + $indent+=' ' + } + $currentProperty += $indent + } + + $emptyCIM=$currentProperty.replace(" ","").replace("`r`n","") + if($emptyCIM -eq "MSFT_$CIMInstanceName{}") + { + $currentProperty=$null + } + + return $currentProperty +} + +Function Get-M365DSCDRGSimpleObjectTypeToString +{ + [CmdletBinding()] + [OutputType([System.String])] + param( + [Parameter(Mandatory = 'true')] + [System.String] + $Key, + + [Parameter(Mandatory = 'true')] + $Value, + + [Parameter()] + [System.String] + $Space=" " + + ) + + $returnValue="" + switch -Wildcard ($Value.GetType().Fullname ) + { + "*.Boolean" + { + $returnValue= $Space + $Key + " = `$" + $Value.ToString() + "`r`n" + } + "*.String" + { + if($key -eq '@odata.type') + { + $key='odataType' + } + $returnValue= $Space + $Key + " = '" + $Value + "'`r`n" + } + "*.DateTime" + { + $returnValue= $Space + $Key + " = '" + $Value + "'`r`n" + } + "*[[\]]" + { + $returnValue= $Space + $key + " = @(" + $whitespace="" + $newline="" + if($Value.count -gt 1) + { + $returnValue += "`r`n" + $whitespace=$Space+" " + $newline="`r`n" + } + foreach ($item in ($Value | Where-Object -FilterScript {$null -ne $_ })) + { + switch -Wildcard ($item.GetType().Fullname ) + { + "*.String" + { + $returnValue += "$whitespace'$item'$newline" + } + "*.DateTime" + { + $returnValue += "$whitespace'$item'$newline" + } + Default + { + $returnValue += "$whitespace$item$newline" + } + } + } + if($Value.count -gt 1) + { + $returnValue += "$Space)`r`n" + } + else + { + $returnValue += ")`r`n" + + } + } + Default + { + $returnValue= $Space + $Key + " = " + $Value + "`r`n" + } + } + return $returnValue +} + +function Compare-M365DSCComplexObject +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param( + [Parameter()] + $Source, + [Parameter()] + $Target + ) + + #Comparing full objects + if($null -eq $Source -and $null -eq $Target) + { + return $true + } + + $sourceValue="" + $targetValue="" + if (($null -eq $Source) -xor ($null -eq $Target)) + { + if($null -eq $Source) + { + $sourceValue="Source is null" + } + + if($null -eq $Target) + { + $targetValue="Target is null" + } + Write-Verbose -Message "Configuration drift - Complex object: {$sourceValue$targetValue}" + return $false + } + + if($Source.getType().FullName -like "*CimInstance[[\]]" -or $Source.getType().FullName -like "*Hashtable[[\]]") + { + if($source.count -ne $target.count) + { + Write-Verbose -Message "Configuration drift - The complex array have different number of items: Source {$($source.count)} Target {$($target.count)}" + return $false + } + if($source.count -eq 0) + { + return $true + } + + foreach($item in $Source) + { + + $hashSource=Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $item + foreach($targetItem in $Target) + { + $compareResult= Compare-M365DSCComplexObject ` + -Source $hashSource ` + -Target $targetItem + + if ($compareResult) + { + break + } + } + + if(-not $compareResult) + { + Write-Verbose -Message "Configuration drift - The complex array items are not identical" + return $false + } + } + return $true + } + + $keys= $Source.Keys|Where-Object -FilterScript {$_ -ne "PSComputerName"} + foreach ($key in $keys) + { + #Matching possible key names between Source and Target + $skey=$key + $tkey=$key + + $sourceValue=$Source.$key + $targetValue=$Target.$tkey + #One of the item is null and not the other + if (($null -eq $Source.$key) -xor ($null -eq $Target.$tkey)) + { + + if($null -eq $Source.$key) + { + $sourceValue="null" + } + + if($null -eq $Target.$tkey) + { + $targetValue="null" + } + + #Write-Verbose -Message "Configuration drift - key: $key Source {$sourceValue} Target {$targetValue}" + return $false + } + + #Both keys aren't null or empty + if(($null -ne $Source.$key) -and ($null -ne $Target.$tkey)) + { + if($Source.$key.getType().FullName -like "*CimInstance*" -or $Source.$key.getType().FullName -like "*hashtable*" ) + { + #Recursive call for complex object + $compareResult= Compare-M365DSCComplexObject ` + -Source (Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $Source.$key) ` + -Target $Target.$tkey + + if(-not $compareResult) + { + + #Write-Verbose -Message "Configuration drift - complex object key: $key Source {$sourceValue} Target {$targetValue}" + return $false + } + } + else + { + #Simple object comparison + $referenceObject=$Target.$tkey + $differenceObject=$Source.$key + + #Identifying date from the current values + $targetType=($Target.$tkey.getType()).Name + if($targetType -like "*Date*") + { + $compareResult=$true + $sourceDate= [DateTime]$Source.$key + if($sourceDate -ne $targetType) + { + $compareResult=$null + } + } + else + { + $compareResult = Compare-Object ` + -ReferenceObject ($referenceObject) ` + -DifferenceObject ($differenceObject) + } + + if ($null -ne $compareResult) + { + #Write-Verbose -Message "Configuration drift - simple object key: $key Source {$sourceValue} Target {$targetValue}" + return $false + } + } + } + } + + return $true +} +function Convert-M365DSCDRGComplexTypeToHashtable +{ + [CmdletBinding()] + [OutputType([hashtable],[hashtable[]])] + param( + [Parameter(Mandatory = 'true')] + $ComplexObject + ) + + + if($ComplexObject.getType().Fullname -like "*[[\]]") + { + $results=@() + foreach($item in $ComplexObject) + { + $hash=Convert-M365DSCDRGComplexTypeToHashtable -ComplexObject $item + $results+=$hash + } + + #Write-Verbose -Message ("Convert-M365DSCDRGComplexTypeToHashtable >>> results: "+(convertTo-JSON $results -Depth 20)) + # PowerShell returns all non-captured stream output, not just the argument of the return statement. + #An empty array is mangled into $null in the process. + #However, an array can be preserved on return by prepending it with the array construction operator (,) + return ,[hashtable[]]$results + } + $hashComplexObject = Get-M365DSCDRGComplexTypeToHashtable -ComplexObject $ComplexObject + + if($null -ne $hashComplexObject) + { + + $results=$hashComplexObject.clone() + $keys=$hashComplexObject.Keys|Where-Object -FilterScript {$_ -ne 'PSComputerName'} + foreach ($key in $keys) + { + if($hashComplexObject[$key] -and $hashComplexObject[$key].getType().Fullname -like "*CimInstance*") + { + $results[$key]=Convert-M365DSCDRGComplexTypeToHashtable -ComplexObject $hashComplexObject[$key] + } + else + { + $propertyName = $key[0].ToString().ToLower() + $key.Substring(1, $key.Length - 1) + $propertyValue=$results[$key] + $results.remove($key)|out-null + $results.add($propertyName,$propertyValue) + } + } + } + return [hashtable]$results +} + +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10.schema.mof new file mode 100644 index 0000000000..7c7f470872 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10.schema.mof @@ -0,0 +1,40 @@ +[ClassVersion("1.0.0.0")] +class MSFT_DeviceManagementConfigurationPolicyAssignments +{ + [Write, Description("The type of the target assignment."), ValueMap{"#microsoft.graph.groupAssignmentTarget","#microsoft.graph.allLicensedUsersAssignmentTarget","#microsoft.graph.allDevicesAssignmentTarget","#microsoft.graph.exclusionGroupAssignmentTarget","#microsoft.graph.configurationManagerCollectionAssignmentTarget"}, Values{"#microsoft.graph.groupAssignmentTarget","#microsoft.graph.allLicensedUsersAssignmentTarget","#microsoft.graph.allDevicesAssignmentTarget","#microsoft.graph.exclusionGroupAssignmentTarget","#microsoft.graph.configurationManagerCollectionAssignmentTarget"}] String dataType; + [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 Id of the filter for the target assignment.")] String deviceAndAppManagementAssignmentFilterId; + [Write, Description("The group Id that is the target of the assignment.")] String groupId; + [Write, Description("The collection Id that is the target of the assignment.(ConfigMgr)")] String collectionId; +}; + +[ClassVersion("1.0.0.0"), FriendlyName("IntuneDeviceConfigurationIdentityProtectionPolicyWindows10")] +class MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10 : OMI_BaseResource +{ + [Write, Description("Boolean value used to enable enhanced anti-spoofing for facial feature recognition on Windows Hello face authentication.")] Boolean EnhancedAntiSpoofingForFacialFeaturesEnabled; + [Write, Description("Integer value specifies the period (in days) that a PIN can be used before the system requires the user to change it. Valid values are 0 to 730 inclusive. Valid values 0 to 730")] UInt32 PinExpirationInDays; + [Write, Description("This value configures the use of lowercase characters in the Windows Hello for Business PIN. Possible values are: blocked, required, allowed, notConfigured."), ValueMap{"blocked","required","allowed","notConfigured"}, Values{"blocked","required","allowed","notConfigured"}] String PinLowercaseCharactersUsage; + [Write, Description("Integer value that sets the maximum number of characters allowed for the work PIN. Valid values are 4 to 127 inclusive and greater than or equal to the value set for the minimum PIN. Valid values 4 to 127")] UInt32 PinMaximumLength; + [Write, Description("Integer value that sets the minimum number of characters required for the Windows Hello for Business PIN. Valid values are 4 to 127 inclusive and less than or equal to the value set for the maximum PIN. Valid values 4 to 127")] UInt32 PinMinimumLength; + [Write, Description("Controls the ability to prevent users from using past PINs. This must be set between 0 and 50, inclusive, and the current PIN of the user is included in that count. If set to 0, previous PINs are not stored. PIN history is not preserved through a PIN reset. Valid values 0 to 50")] UInt32 PinPreviousBlockCount; + [Write, Description("Boolean value that enables a user to change their PIN by using the Windows Hello for Business PIN recovery service.")] Boolean PinRecoveryEnabled; + [Write, Description("Controls the ability to use special characters in the Windows Hello for Business PIN. Possible values are: blocked, required, allowed, notConfigured."), ValueMap{"blocked","required","allowed","notConfigured"}, Values{"blocked","required","allowed","notConfigured"}] String PinSpecialCharactersUsage; + [Write, Description("This value configures the use of uppercase characters in the Windows Hello for Business PIN. Possible values are: blocked, required, allowed, notConfigured."), ValueMap{"blocked","required","allowed","notConfigured"}, Values{"blocked","required","allowed","notConfigured"}] String PinUppercaseCharactersUsage; + [Write, Description("Controls whether to require a Trusted Platform Module (TPM) for provisioning Windows Hello for Business. A TPM provides an additional security benefit in that data stored on it cannot be used on other devices. If set to False, all devices can provision Windows Hello for Business even if there is not a usable TPM.")] Boolean SecurityDeviceRequired; + [Write, Description("Controls the use of biometric gestures, such as face and fingerprint, as an alternative to the Windows Hello for Business PIN. If set to False, biometric gestures are not allowed. Users must still configure a PIN as a backup in case of failures.")] Boolean UnlockWithBiometricsEnabled; + [Write, Description("Boolean value that enables Windows Hello for Business to use certificates to authenticate on-premise resources.")] Boolean UseCertificatesForOnPremisesAuthEnabled; + [Write, Description("Boolean value used to enable the Windows Hello security key as a logon credential.")] Boolean UseSecurityKeyForSignin; + [Write, Description("Boolean value that blocks Windows Hello for Business as a method for signing into Windows.")] Boolean WindowsHelloForBusinessBlocked; + [Write, Description("Admin provided description of the Device Configuration.")] String Description; + [Required, Description("Admin provided name of the device configuration.")] String DisplayName; + [Write, Description("Indicates whether or not the underlying Device Configuration supports the assignment of scope tags. Assigning to the ScopeTags property is not allowed when this value is false and entities will not be visible to scoped users. This occurs for Legacy policies created in Silverlight and can be resolved by deleting and recreating the policy in the Azure Portal. This property is read-only.")] Boolean SupportsScopeTags; + [Key, Description("The unique identifier for an entity. Read-only.")] String Id; + [Write, Description("Represents the assignment to the Intune policy."), EmbeddedInstance("MSFT_DeviceManagementConfigurationPolicyAssignments")] String Assignments[]; + [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; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/readme.md new file mode 100644 index 0000000000..183e36703d --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/readme.md @@ -0,0 +1,6 @@ + +# IntuneDeviceConfigurationIdentityProtectionPolicyWindows10 + +## Description + +Intune Device Configuration Identity Protection Policy for Windows10 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/settings.json new file mode 100644 index 0000000000..ffc2c606d7 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/settings.json @@ -0,0 +1,33 @@ +{ + "resourceName": "IntuneDeviceConfigurationIdentityProtectionPolicyWindows10", + "description": "This resource configures an Intune Device Configuration Identity Protection Policy for Windows10.", + "permissions": { + "graph": { + "delegated": { + "read": [ + { + "name": "DeviceManagementConfiguration.Read.All" + } + ], + "update": [ + { + "name": "DeviceManagementConfiguration.ReadWrite.All" + } + ] + }, + "application": { + "read": [ + { + "name": "DeviceManagementConfiguration.Read.All" + } + ], + "update": [ + { + "name": "DeviceManagementConfiguration.ReadWrite.All" + } + ] + } + } +} + +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/1-IntuneDeviceConfigurationIdentityProtectionPolicyWindows10-Example.ps1 b/Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/1-IntuneDeviceConfigurationIdentityProtectionPolicyWindows10-Example.ps1 new file mode 100644 index 0000000000..77e0f9faf3 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/IntuneDeviceConfigurationIdentityProtectionPolicyWindows10/1-IntuneDeviceConfigurationIdentityProtectionPolicyWindows10-Example.ps1 @@ -0,0 +1,46 @@ +<# +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. +#> + +Configuration Example +{ + param( + [Parameter(Mandatory = $true)] + [PSCredential] + $Credscredential + ) + Import-DscResource -ModuleName Microsoft365DSC + + node localhost + { + IntuneDeviceConfigurationIdentityProtectionPolicyWindows10 'Example' + { + Assignments = @( + MSFT_DeviceManagementConfigurationPolicyAssignments{ + deviceAndAppManagementAssignmentFilterType = 'none' + dataType = '#microsoft.graph.allLicensedUsersAssignmentTarget' + } + ); + Credential = $Credscredential; + DisplayName = "identity protection"; + EnhancedAntiSpoofingForFacialFeaturesEnabled = $True; + Ensure = "Present"; + Id = "e0f7e513-6b34-4a74-8d90-fe7648c0ce30"; + PinExpirationInDays = 5; + PinLowercaseCharactersUsage = "allowed"; + PinMaximumLength = 4; + PinMinimumLength = 4; + PinPreviousBlockCount = 3; + PinRecoveryEnabled = $True; + PinSpecialCharactersUsage = "allowed"; + PinUppercaseCharactersUsage = "allowed"; + SecurityDeviceRequired = $True; + SupportsScopeTags = $True; + UnlockWithBiometricsEnabled = $True; + UseCertificatesForOnPremisesAuthEnabled = $True; + UseSecurityKeyForSignin = $True; + WindowsHelloForBusinessBlocked = $False; + } + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceConfigurationIdentityProtectionPolicyWindows10.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceConfigurationIdentityProtectionPolicyWindows10.Tests.ps1 new file mode 100644 index 0000000000..be960b0c79 --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneDeviceConfigurationIdentityProtectionPolicyWindows10.Tests.ps1 @@ -0,0 +1,321 @@ +[CmdletBinding()] +param( +) +$M365DSCTestFolder = Join-Path -Path $PSScriptRoot ` + -ChildPath "..\..\Unit" ` + -Resolve +$CmdletModule = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath "\Stubs\Microsoft365.psm1" ` + -Resolve) +$GenericStubPath = (Join-Path -Path $M365DSCTestFolder ` + -ChildPath "\Stubs\Generic.psm1" ` + -Resolve) +Import-Module -Name (Join-Path -Path $M365DSCTestFolder ` + -ChildPath "\UnitTestHelper.psm1" ` + -Resolve) + +$Global:DscHelper = New-M365DscUnitTestHelper -StubModule $CmdletModule ` + -DscResource "IntuneDeviceConfigurationIdentityProtectionPolicyWindows10" -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 "f@kepassword1" -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential ("tenantadmin@mydomain.com", $secpasswd) + + Mock -CommandName Confirm-M365DSCDependencies -MockWith { + } + + Mock -CommandName Get-PSSession -MockWith { + } + + Mock -CommandName Remove-PSSession -MockWith { + } + + Mock -CommandName Update-MgDeviceManagementDeviceConfiguration -MockWith { + } + + Mock -CommandName New-MgDeviceManagementDeviceConfiguration -MockWith { + } + + Mock -CommandName Remove-MgDeviceManagementDeviceConfiguration -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credentials" + } + + Mock -CommandName Get-MgDeviceManagementDeviceConfigurationAssignment -MockWith { + } + + } + # Test contexts + Context -Name "The IntuneDeviceConfigurationIdentityProtectionPolicyWindows10 should exist but it DOES NOT" -Fixture { + BeforeAll { + $testParams = @{ + Description = "FakeStringValue" + DisplayName = "FakeStringValue" + EnhancedAntiSpoofingForFacialFeaturesEnabled = $True + Id = "FakeStringValue" + PinExpirationInDays = 25 + PinLowercaseCharactersUsage = "blocked" + PinMaximumLength = 25 + PinMinimumLength = 25 + PinPreviousBlockCount = 25 + PinRecoveryEnabled = $True + PinSpecialCharactersUsage = "blocked" + PinUppercaseCharactersUsage = "blocked" + SecurityDeviceRequired = $True + SupportsScopeTags = $True + UnlockWithBiometricsEnabled = $True + UseCertificatesForOnPremisesAuthEnabled = $True + UseSecurityKeyForSignin = $True + WindowsHelloForBusinessBlocked = $True + Ensure = "Present" + Credential = $Credential; + } + + Mock -CommandName Get-MgDeviceManagementDeviceConfiguration -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 the group from the Set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName New-MgDeviceManagementDeviceConfiguration -Exactly 1 + } + } + + Context -Name "The IntuneDeviceConfigurationIdentityProtectionPolicyWindows10 exists but it SHOULD NOT" -Fixture { + BeforeAll { + $testParams = @{ + Description = "FakeStringValue" + DisplayName = "FakeStringValue" + EnhancedAntiSpoofingForFacialFeaturesEnabled = $True + Id = "FakeStringValue" + PinExpirationInDays = 25 + PinLowercaseCharactersUsage = "blocked" + PinMaximumLength = 25 + PinMinimumLength = 25 + PinPreviousBlockCount = 25 + PinRecoveryEnabled = $True + PinSpecialCharactersUsage = "blocked" + PinUppercaseCharactersUsage = "blocked" + SecurityDeviceRequired = $True + SupportsScopeTags = $True + UnlockWithBiometricsEnabled = $True + UseCertificatesForOnPremisesAuthEnabled = $True + UseSecurityKeyForSignin = $True + WindowsHelloForBusinessBlocked = $True + Ensure = "Absent" + Credential = $Credential; + } + + Mock -CommandName Get-MgDeviceManagementDeviceConfiguration -MockWith { + return @{ + AdditionalProperties = @{ + pinRecoveryEnabled = $True + pinExpirationInDays = 25 + pinMinimumLength = 25 + securityDeviceRequired = $True + useCertificatesForOnPremisesAuthEnabled = $True + unlockWithBiometricsEnabled = $True + '@odata.type' = "#microsoft.graph.windowsIdentityProtectionConfiguration" + pinLowercaseCharactersUsage = "blocked" + pinPreviousBlockCount = 25 + windowsHelloForBusinessBlocked = $True + useSecurityKeyForSignin = $True + pinSpecialCharactersUsage = "blocked" + pinMaximumLength = 25 + enhancedAntiSpoofingForFacialFeaturesEnabled = $True + pinUppercaseCharactersUsage = "blocked" + } + Description = "FakeStringValue" + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + SupportsScopeTags = $True + + } + } + } + + It "Should return Values from the Get method" { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should Remove the group from the Set method' { + Set-TargetResource @testParams + Should -Invoke -CommandName Remove-MgDeviceManagementDeviceConfiguration -Exactly 1 + } + } + Context -Name "The IntuneDeviceConfigurationIdentityProtectionPolicyWindows10 Exists and Values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + Description = "FakeStringValue" + DisplayName = "FakeStringValue" + EnhancedAntiSpoofingForFacialFeaturesEnabled = $True + Id = "FakeStringValue" + PinExpirationInDays = 25 + PinLowercaseCharactersUsage = "blocked" + PinMaximumLength = 25 + PinMinimumLength = 25 + PinPreviousBlockCount = 25 + PinRecoveryEnabled = $True + PinSpecialCharactersUsage = "blocked" + PinUppercaseCharactersUsage = "blocked" + SecurityDeviceRequired = $True + SupportsScopeTags = $True + UnlockWithBiometricsEnabled = $True + UseCertificatesForOnPremisesAuthEnabled = $True + UseSecurityKeyForSignin = $True + WindowsHelloForBusinessBlocked = $True + Ensure = "Present" + Credential = $Credential; + } + + Mock -CommandName Get-MgDeviceManagementDeviceConfiguration -MockWith { + return @{ + AdditionalProperties = @{ + pinRecoveryEnabled = $True + pinExpirationInDays = 25 + pinMinimumLength = 25 + securityDeviceRequired = $True + useCertificatesForOnPremisesAuthEnabled = $True + unlockWithBiometricsEnabled = $True + '@odata.type' = "#microsoft.graph.windowsIdentityProtectionConfiguration" + pinLowercaseCharactersUsage = "blocked" + pinPreviousBlockCount = 25 + windowsHelloForBusinessBlocked = $True + useSecurityKeyForSignin = $True + pinSpecialCharactersUsage = "blocked" + pinMaximumLength = 25 + enhancedAntiSpoofingForFacialFeaturesEnabled = $True + pinUppercaseCharactersUsage = "blocked" + } + Description = "FakeStringValue" + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + SupportsScopeTags = $True + + } + } + } + + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name "The IntuneDeviceConfigurationIdentityProtectionPolicyWindows10 exists and values are NOT in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + Description = "FakeStringValue" + DisplayName = "FakeStringValue" + EnhancedAntiSpoofingForFacialFeaturesEnabled = $True + Id = "FakeStringValue" + PinExpirationInDays = 25 + PinLowercaseCharactersUsage = "blocked" + PinMaximumLength = 25 + PinMinimumLength = 25 + PinPreviousBlockCount = 25 + PinRecoveryEnabled = $True + PinSpecialCharactersUsage = "blocked" + PinUppercaseCharactersUsage = "blocked" + SecurityDeviceRequired = $True + SupportsScopeTags = $True + UnlockWithBiometricsEnabled = $True + UseCertificatesForOnPremisesAuthEnabled = $True + UseSecurityKeyForSignin = $True + WindowsHelloForBusinessBlocked = $True + Ensure = "Present" + Credential = $Credential; + } + + Mock -CommandName Get-MgDeviceManagementDeviceConfiguration -MockWith { + return @{ + AdditionalProperties = @{ + '@odata.type' = "#microsoft.graph.windowsIdentityProtectionConfiguration" + pinUppercaseCharactersUsage = "blocked" + pinPreviousBlockCount = 7 + pinMinimumLength = 7 + pinSpecialCharactersUsage = "blocked" + pinExpirationInDays = 7 + pinLowercaseCharactersUsage = "blocked" + pinMaximumLength = 7 + } + Description = "FakeStringValue" + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + } + } + } + + 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 + Should -Invoke -CommandName Update-MgDeviceManagementDeviceConfiguration -Exactly 1 + } + } + + Context -Name "ReverseDSC Tests" -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential + } + + Mock -CommandName Get-MgDeviceManagementDeviceConfiguration -MockWith { + return @{ + AdditionalProperties = @{ + pinRecoveryEnabled = $True + pinExpirationInDays = 25 + pinMinimumLength = 25 + securityDeviceRequired = $True + useCertificatesForOnPremisesAuthEnabled = $True + unlockWithBiometricsEnabled = $True + '@odata.type' = "#microsoft.graph.windowsIdentityProtectionConfiguration" + pinLowercaseCharactersUsage = "blocked" + pinPreviousBlockCount = 25 + windowsHelloForBusinessBlocked = $True + useSecurityKeyForSignin = $True + pinSpecialCharactersUsage = "blocked" + pinMaximumLength = 25 + enhancedAntiSpoofingForFacialFeaturesEnabled = $True + pinUppercaseCharactersUsage = "blocked" + } + Description = "FakeStringValue" + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + SupportsScopeTags = $True + + } + } + } + It "Should Reverse Engineer resource from the Export method" { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope