From 81c127043664b4fa4c64d18e61b7a0b3aa6a4889 Mon Sep 17 00:00:00 2001 From: Piyush Dubey Date: Fri, 18 Oct 2024 14:55:08 +0530 Subject: [PATCH 01/60] first commit --- .../MSFT_AADServicePrincipal.psm1 | 248 +++++++++++++++++- .../MSFT_AADServicePrincipal.schema.mof | 17 ++ 2 files changed, 264 insertions(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 index 5640892910..d9f3d1b1e7 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 @@ -32,6 +32,10 @@ function Get-TargetResource [System.Boolean] $AppRoleAssignmentRequired, + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $CustomSecurityAttributes, + [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $DelegatedPermissionClassifications, @@ -226,6 +230,8 @@ function Get-TargetResource $complexDelegatedPermissionClassifications += $hashtable } + $complexCustomSecurityAttributes = [Array](Get-CustomSecurityAttributes -AppId $AppId) + $result = @{ AppId = $AADServicePrincipal.AppId AppRoleAssignedTo = $AppRoleAssignedToValues @@ -234,6 +240,7 @@ function Get-TargetResource AlternativeNames = $AADServicePrincipal.AlternativeNames AccountEnabled = [boolean]$AADServicePrincipal.AccountEnabled AppRoleAssignmentRequired = $AADServicePrincipal.AppRoleAssignmentRequired + CustomSecurityAttributes = $complexCustomSecurityAttributes DelegatedPermissionClassifications = [Array]$complexDelegatedPermissionClassifications ErrorUrl = $AADServicePrincipal.ErrorUrl Homepage = $AADServicePrincipal.Homepage @@ -304,6 +311,10 @@ function Set-TargetResource [System.Boolean] $AppRoleAssignmentRequired, + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $CustomSecurityAttributes, + [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $DelegatedPermissionClassifications, @@ -467,6 +478,39 @@ function Set-TargetResource $currentParameters.Remove('AppRoleAssignedTo') | Out-Null $currentParameters.Remove('Owners') | Out-Null $currentParameters.Remove('DelegatedPermissionClassifications') | Out-Null + + # logic to update the custom security attributes to be cmdlet comsumable + $updatedCustomSecurityAttributes = @{} + foreach ($attributeSet in $currentParameters.CustomSecurityAttributes) { + $attributeSetKey = $attributeSet.AttributeName + + $valuesHashtable = @{} + foreach ($attribute in $attributeSet.AttributeValues) { + $attributeKey = $attribute.AttributeName + $attributeValue + if ($null -ne $attribute.StringArrayValue) { + if ($attribute.StringArrayValue.Count -gt 1) { + $valuesHashtable($attributeKey + '@odata.type', "#Collection(String)") + $attributeValue = $attribute.StringArrayValue + } else { + $attributeValue = $attribute.StringArrayValue[0] + } + } + elseif ($null -ne $attribute.IntArrayValue) { + if ($attribute.IntArrayValue.Count -gt 1) { + $attributeValue = $attribute.IntArrayValue + } else { + $attributeValue = $attribute.IntArrayValue[0] + } + } + elseif ($null -ne $attribute.BoolValue) { + $attributeValue = $attribute.BoolValue + } + $valuesHashtable.Add($attributeKey, $attributeValue) + } + $updatedCustomSecurityAttributes.Add($attributeSetKey, $valuesHashtable) + } + Update-MgServicePrincipal -ServicePrincipalId $currentAADServicePrincipal.ObjectID @currentParameters if ($AppRoleAssignedTo) @@ -642,6 +686,10 @@ function Test-TargetResource [System.Boolean] $AppRoleAssignmentRequired, + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $CustomSecurityAttributes, + [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] $DelegatedPermissionClassifications, @@ -743,12 +791,14 @@ function Test-TargetResource { $source = $PSBoundParameters.$key $target = $CurrentValues.$key + if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') { $testResult = Compare-M365DSCComplexObject ` -Source ($source) ` -Target ($target) + Write-Host "Key: $key, Cim comparison value: $testResult" if (-not $testResult) { $testTargetResource = $false @@ -843,6 +893,9 @@ function Export-TargetResource -ErrorAction Stop foreach ($AADServicePrincipal in $Script:exportedInstances) { + if($AADServicePrincipal.AppId -ne 'e4763382-ff8d-4c4c-97e1-65e2241986df') { + continue + } if ($null -ne $Global:M365DSCExportResourceInstancesCount) { $Global:M365DSCExportResourceInstancesCount++ @@ -873,6 +926,10 @@ function Export-TargetResource { $Results.DelegatedPermissionClassifications = Get-M365DSCAzureADServicePrincipalDelegatedPermissionClassifications -PermissionClassifications $Results.DelegatedPermissionClassifications } + if ($Results.CustomSecurityAttributes.Count -gt 0) + { + $Results.CustomSecurityAttributes = Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsString -CustomSecurityAttributes $Results.CustomSecurityAttributes + } $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` -ConnectionMode $ConnectionMode ` -ModulePath $PSScriptRoot ` @@ -888,6 +945,11 @@ function Export-TargetResource $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` -ParameterName 'DelegatedPermissionClassifications' } + if ($null -ne $Results.CustomSecurityAttributes) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'CustomSecurityAttributes' + } $dscContent += $currentDSCBlock Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName @@ -912,6 +974,129 @@ function Export-TargetResource } } +# Function to create MSFT_AttributeValue +function Create-AttributeValue { + param ( + [string]$AttributeName, + [object]$Value + ) + + $attributeValue = @{ + AttributeName = $AttributeName + StringArrayValue = $null + IntArrayValue = $null + BoolValue = $null + } + + # Handle different types of values + if ($Value -is [string]) { + $attributeValue.StringArrayValue = @($Value) + } + elseif ($Value -is [System.Int32] -or $Value -is [System.Int64]) { + $attributeValue.IntArrayValue = @($Value) + } + elseif ($Value -is [bool]) { + $attributeValue.BoolValue = @($Value) + } + elseif ($Value -is [array]) { + if ($Value[0] -is [string]) { + $attributeValue.StringArrayValue = $Value + } + elseif ($Value[0] -is [System.Int32] -or $Value[0] -is [System.Int64]) { + $attributeValue.IntArrayValue = $Value + } + } + + return $attributeValue +} + + +function Get-CustomSecurityAttributes { + [OutputType([System.Array])] + param ( + [String]$AppId + ) + + $customSecurityAttributes = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/servicePrincipals(appId='$AppId')`?`$select=customSecurityAttributes" -Method Get + $customSecurityAttributes = $customSecurityAttributes.customSecurityAttributes + $newCustomSecurityAttributes = @() + + foreach ($key in $customSecurityAttributes.Keys) { + $attributeSet = @{ + AttributeSetName = $key + AttributeValues = @() + } + + foreach ($attribute in $customSecurityAttributes[$key].Keys) { + # Skip properties that end with '@odata.type' + if ($attribute -like "*@odata.type") { + continue + } + + $value = $customSecurityAttributes[$key][$attribute] + $attributeName = $attribute # Keep the attribute name as it is + + # Create the attribute value and add it to the set + $attributeSet.AttributeValues += Create-AttributeValue -AttributeName $attributeName -Value $value + } + + #Add the attribute set to the final structure + $newCustomSecurityAttributes += $attributeSet + } + + # Display the new structure + return [Array]$newCustomSecurityAttributes +} + +function Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsString +{ + [CmdletBinding()] + [OutputType([System.String])] + param( + [Parameter(Mandatory = $true)] + [System.Collections.ArrayList] + $CustomSecurityAttributes + ) + + $StringContent = "@(`r`n" + foreach ($customSecurityAttribute in $CustomSecurityAttributes) + { + $StringContent += " MSFT_AADServicePrincipalAttributeSet {`r`n" + $StringContent += " AttributeSetName = '" + $customSecurityAttribute.AttributeSetName + "'`r`n" + if ($customSecurityAttribute.AttributeValues.Length -gt 0) + { + $StringContent += " AttributeValues = @(`r`n" + foreach ($attributeValue in $customSecurityAttribute.AttributeValues) + { + $StringContent += " MSFT_AADServicePrincipalAttributeValue {`r`n" + $StringContent += " AttributeName = '" + $attributeValue.AttributeName + "'`r`n" + if ($null -ne $attributeValue.BoolValue){ + $StringContent += " BoolValue = '$" + $attributeValue.BoolValue + "'`r`n" + } + elseif ($null -ne $attributeValue.StringArrayValue){ + $StringContent += " StringArrayValue = @(" + $StringContent += ($attributeValue.StringArrayValue | ForEach-Object { "'$_'" }) -join "," + $StringContent += ")`r`n" + } + elseif ($null -ne $attributeValue.IntArrayValue){ + $StringContent += " IntArrayValue = @(" + $StringContent += $attributeValue.IntArrayValue -join "," + $StringContent += ")`r`n" + } + $StringContent += " }`r`n" + } + $StringContent += " )`r`n" + } + else + { + $StringContent += " AttributeValues = @()`r`n" + } + $StringContent += " }`r`n" + } + $StringContent += ' )' + return $StringContent +} + function Get-M365DSCAzureADServicePrincipalAssignmentAsString { [CmdletBinding()] @@ -956,4 +1141,65 @@ function Get-M365DSCAzureADServicePrincipalDelegatedPermissionClassifications return $StringContent } -Export-ModuleMember -Function *-TargetResource + + +function Print-Hashtable { + param ( + [Parameter(Mandatory=$true)] + [hashtable]$Hashtable, + [int]$IndentLevel = 0 + ) + + # Helper function to handle arrays and nested objects + function Print-Value { + param ( + [Parameter(Mandatory=$true)] + $Value, + [int]$IndentLevel + ) + + $indent = " " * ($IndentLevel * 4) + + if ($Value -is [hashtable]) { + # Recursively call the Print-Hashtable function for nested hashtables + Print-Hashtable -Hashtable $Value -IndentLevel ($IndentLevel + 1) + } elseif ($Value -is [array]) { + # Handle arrays + Write-Host "${indent}Array:" + for ($i = 0; $i -lt $Value.Count; $i++) { + $item = $Value[$i] + Write-Host "${indent}[$i]:" + if ($item -is [hashtable]) { + Print-Hashtable -Hashtable $item -IndentLevel ($IndentLevel + 1) + } else { + Print-Value -Value $item -IndentLevel ($IndentLevel + 1) + } + } + } else { + # Print simple values (non-hashtable, non-array) + Write-Host "${indent}$Value" + } + } + + # Iterate through each key-value pair in the hashtable + foreach ($key in $Hashtable.Keys) { + $indent = " " * ($IndentLevel * 4) + $value = $Hashtable[$key] + + if ($value -is [hashtable]) { + # Print key and call recursively if value is a nested hashtable + Write-Host "${indent}$key :" + Print-Hashtable -Hashtable $value -IndentLevel ($IndentLevel + 1) + } elseif ($value -is [array]) { + # Print arrays with an index for each element + Write-Host "${indent}$key :" + Print-Value -Value $value -IndentLevel ($IndentLevel + 1) + } else { + # Print key-value pairs for non-complex data types + Write-Host "${indent}$key : $value" + } + } +} + + +Export-ModuleMember -Function * diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof index 717b2e569f..7493435d82 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof @@ -12,6 +12,22 @@ class MSFT_AADServicePrincipalDelegatedPermissionClassification [Write, Description("Name of the permission")] String PermissionName; }; +[ClassVersion("1.0.0")] +class MSFT_AADServicePrincipalAttributeValue +{ + [Write, Description("Name of the Attribute")] String AttributeName; + [Write, Description("If the attribute has a string array value")] String StringArrayValue[]; + [Write, Description("If the attribute has a int array value")] UInt32 IntArrayValue[]; + [Write, Description("If the attribute has a boolean value")] Boolean BoolValue; +}; + +[ClassVersion("1.0.0")] +class MSFT_AADServicePrincipalAttributeSet +{ + [Write, Description("Attribute Set Name.")] String AttributeSetName; + [Write, Description("List of attribute values."), EmbeddedInstance("MSFT_AADServicePrincipalAttributeValue")] String AttributeValues[]; +}; + [ClassVersion("1.0.0.0"), FriendlyName("AADServicePrincipal")] class MSFT_AADServicePrincipal : OMI_BaseResource { @@ -33,6 +49,7 @@ class MSFT_AADServicePrincipal : OMI_BaseResource [Write, Description("The type of the service principal.")] String ServicePrincipalType; [Write, Description("Tags linked to this service principal.Note that if you intend for this service principal to show up in the All Applications list in the admin portal, you need to set this value to {WindowsAzureActiveDirectoryIntegratedApp}")] String Tags[]; [Write, Description("The permission classifications for delegated permissions exposed by the app that this service principal represents."), EmbeddedInstance("MSFT_AADServicePrincipalDelegatedPermissionClassification")] String DelegatedPermissionClassifications[]; + [Write, Description("The list of custom security attributes attached to this SPN"), EmbeddedInstance("MSFT_AADServicePrincipalAttributeSet")] String CustomSecurityAttributes[]; [Write, Description("Specify if the Azure AD App should exist or not."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; [Write, Description("Id of the Azure Active Directory application to authenticate with.")] String ApplicationId; From 9291b3a814cf760c2e74333bfeff079e400736b3 Mon Sep 17 00:00:00 2001 From: Piyush Dubey Date: Sat, 19 Oct 2024 21:13:28 +0530 Subject: [PATCH 02/60] added custom Security attribute property --- CHANGELOG.md | 3 +- .../MSFT_AADServicePrincipal.psm1 | 189 ++++++++---------- .../MSFT_AADServicePrincipal.schema.mof | 2 + 3 files changed, 92 insertions(+), 102 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5eeb43cba..24b6089172 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,8 @@ * AADLifecycleWorkflowSettings * Initial release. * AADServicePrincipal - * Adding Delegated Permission Classification Property + * Added Delegated Permission Classification Property + * Added Custom Security Attributes Property * ADOPermissionGroupSettings * Initial release. * EXOATPBuiltInProtectionRule diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 index d9f3d1b1e7..c539a90625 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 @@ -231,6 +231,9 @@ function Get-TargetResource } $complexCustomSecurityAttributes = [Array](Get-CustomSecurityAttributes -AppId $AppId) + if ($null -eq $complexCustomSecurityAttributes) { + $complexCustomSecurityAttributes = @() + } $result = @{ AppId = $AADServicePrincipal.AppId @@ -421,6 +424,13 @@ function Set-TargetResource $currentParameters.Remove('ApplicationSecret') | Out-Null $currentParameters.Remove('AccessTokens') | Out-Null + # update the custom security attributes to be cmdlet comsumable + if ($null -ne $currentParameters.CustomSecurityAttributes -and $currentParameters.CustomSecurityAttributes -gt 0) { + $currentParameters.CustomSecurityAttributes = Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsCmdletHashtable -CustomSecurityAttributes $currentParameters.CustomSecurityAttributes + } else { + $currentParameters.CustomSecurityAttributes = @() + } + # ServicePrincipal should exist but it doesn't if ($Ensure -eq 'Present' -and $currentAADServicePrincipal.Ensure -eq 'Absent') { @@ -479,36 +489,13 @@ function Set-TargetResource $currentParameters.Remove('Owners') | Out-Null $currentParameters.Remove('DelegatedPermissionClassifications') | Out-Null - # logic to update the custom security attributes to be cmdlet comsumable - $updatedCustomSecurityAttributes = @{} - foreach ($attributeSet in $currentParameters.CustomSecurityAttributes) { - $attributeSetKey = $attributeSet.AttributeName - - $valuesHashtable = @{} - foreach ($attribute in $attributeSet.AttributeValues) { - $attributeKey = $attribute.AttributeName - $attributeValue - if ($null -ne $attribute.StringArrayValue) { - if ($attribute.StringArrayValue.Count -gt 1) { - $valuesHashtable($attributeKey + '@odata.type', "#Collection(String)") - $attributeValue = $attribute.StringArrayValue - } else { - $attributeValue = $attribute.StringArrayValue[0] - } - } - elseif ($null -ne $attribute.IntArrayValue) { - if ($attribute.IntArrayValue.Count -gt 1) { - $attributeValue = $attribute.IntArrayValue - } else { - $attributeValue = $attribute.IntArrayValue[0] - } - } - elseif ($null -ne $attribute.BoolValue) { - $attributeValue = $attribute.BoolValue - } - $valuesHashtable.Add($attributeKey, $attributeValue) + #removing the current custom security attributes + if ($currentAADServicePrincipal.CustomSecurityAttributes.Count -gt 0) { + $currentAADServicePrincipal.CustomSecurityAttributes = Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsCmdletHashtable -CustomSecurityAttributes $currentAADServicePrincipal.CustomSecurityAttributes -GetForDelete $true + $CSAParams = @{ + customSecurityAttributes = $currentAADServicePrincipal.CustomSecurityAttributes } - $updatedCustomSecurityAttributes.Add($attributeSetKey, $valuesHashtable) + Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/servicePrincipals(appId='$($currentParameters.AppId)')" -Method Patch -Body $CSAParams } Update-MgServicePrincipal -ServicePrincipalId $currentAADServicePrincipal.ObjectID @currentParameters @@ -798,7 +785,6 @@ function Test-TargetResource -Source ($source) ` -Target ($target) - Write-Host "Key: $key, Cim comparison value: $testResult" if (-not $testResult) { $testTargetResource = $false @@ -885,7 +871,6 @@ function Export-TargetResource try { $i = 1 - Write-Host "`r`n" -NoNewline $Script:ExportMode = $true [array] $Script:exportedInstances = Get-MgServicePrincipal -All:$true ` -Filter $Filter ` @@ -893,7 +878,7 @@ function Export-TargetResource -ErrorAction Stop foreach ($AADServicePrincipal in $Script:exportedInstances) { - if($AADServicePrincipal.AppId -ne 'e4763382-ff8d-4c4c-97e1-65e2241986df') { + if($AADServicePrincipal.AppId -ne '0b750897-174f-4aed-ad21-ab3799c7e404') { continue } if ($null -ne $Global:M365DSCExportResourceInstancesCount) @@ -901,7 +886,6 @@ function Export-TargetResource $Global:M365DSCExportResourceInstancesCount++ } - Write-Host " |---[$i/$($Script:exportedInstances.Count)] $($AADServicePrincipal.DisplayName)" -NoNewline $Params = @{ Credential = $Credential ApplicationId = $ApplicationId @@ -954,7 +938,6 @@ function Export-TargetResource Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName - Write-Host $Global:M365DSCEmojiGreenCheckMark $i++ } } @@ -962,7 +945,6 @@ function Export-TargetResource } catch { - Write-Host $Global:M365DSCEmojiRedX New-M365DSCLogEntry -Message 'Error during Export:' ` -Exception $_ ` @@ -974,6 +956,64 @@ function Export-TargetResource } } +function Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsCmdletHashtable +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param( + [Parameter(Mandatory = $true)] + [System.Collections.ArrayList] + $CustomSecurityAttributes, + + [Parameter()] + [System.Boolean] + $GetForDelete = $false + ) + + # logic to update the custom security attributes to be cmdlet comsumable + $updatedCustomSecurityAttributes = @{} + foreach ($attributeSet in $CustomSecurityAttributes) { + $attributeSetKey = $attributeSet.AttributeSetName + + $valuesHashtable = @{} + $valuesHashtable.Add('@odata.type', '#Microsoft.DirectoryServices.CustomSecurityAttributeValue') + foreach ($attribute in $attributeSet.AttributeValues) { + $attributeKey = $attribute.AttributeName + # supply attributeName = $null in the body, if you want to delete this attribute + if ($GetForDelete -eq $true) { + $valuesHashtable.Add($attributeKey, $null) + continue + } + + $odataKey = $attributeKey + '@odata.type' + + if ($null -ne $attribute.StringArrayValue) { + $valuesHashtable.Add($odataKey, "#Collection(String)") + $attributeValue = $attribute.StringArrayValue + } + elseif ($null -ne $attribute.IntArrayValue) { + $valuesHashtable.Add($odataKey, "#Collection(Int32)") + $attributeValue = $attribute.IntArrayValue + } + elseif ($null -ne $attribute.StringValue) { + $valuesHashtable.Add($odataKey, "#String") + $attributeValue = $attribute.StringValue + } + elseif ($null -ne $attribute.IntValue) { + $valuesHashtable.Add($odataKey, "#Int32") + $attributeValue = $attribute.IntValue + } + elseif ($null -ne $attribute.BoolValue) { + $attributeValue = $attribute.BoolValue + } + + $valuesHashtable.Add($attributeKey, $attributeValue) + } + $updatedCustomSecurityAttributes.Add($attributeSetKey, $valuesHashtable) + } + return $updatedCustomSecurityAttributes +} + # Function to create MSFT_AttributeValue function Create-AttributeValue { param ( @@ -985,18 +1025,20 @@ function Create-AttributeValue { AttributeName = $AttributeName StringArrayValue = $null IntArrayValue = $null + StringValue = $null + IntValue = $null BoolValue = $null } # Handle different types of values if ($Value -is [string]) { - $attributeValue.StringArrayValue = @($Value) + $attributeValue.StringValue = $Value } elseif ($Value -is [System.Int32] -or $Value -is [System.Int64]) { - $attributeValue.IntArrayValue = @($Value) + $attributeValue.IntValue = $Value } elseif ($Value -is [bool]) { - $attributeValue.BoolValue = @($Value) + $attributeValue.BoolValue = $Value } elseif ($Value -is [array]) { if ($Value[0] -is [string]) { @@ -1071,7 +1113,13 @@ function Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsString $StringContent += " MSFT_AADServicePrincipalAttributeValue {`r`n" $StringContent += " AttributeName = '" + $attributeValue.AttributeName + "'`r`n" if ($null -ne $attributeValue.BoolValue){ - $StringContent += " BoolValue = '$" + $attributeValue.BoolValue + "'`r`n" + $StringContent += " BoolValue = $" + $attributeValue.BoolValue + "`r`n" + } + elseif ($null -ne $attributeValue.StringValue){ + $StringContent += " StringValue = '" + $attributeValue.StringValue + "'`r`n" + } + elseif ($null -ne $attributeValue.IntValue){ + $StringContent += " IntValue = " + $attributeValue.IntValue + "`r`n" } elseif ($null -ne $attributeValue.StringArrayValue){ $StringContent += " StringArrayValue = @(" @@ -1141,65 +1189,4 @@ function Get-M365DSCAzureADServicePrincipalDelegatedPermissionClassifications return $StringContent } - - -function Print-Hashtable { - param ( - [Parameter(Mandatory=$true)] - [hashtable]$Hashtable, - [int]$IndentLevel = 0 - ) - - # Helper function to handle arrays and nested objects - function Print-Value { - param ( - [Parameter(Mandatory=$true)] - $Value, - [int]$IndentLevel - ) - - $indent = " " * ($IndentLevel * 4) - - if ($Value -is [hashtable]) { - # Recursively call the Print-Hashtable function for nested hashtables - Print-Hashtable -Hashtable $Value -IndentLevel ($IndentLevel + 1) - } elseif ($Value -is [array]) { - # Handle arrays - Write-Host "${indent}Array:" - for ($i = 0; $i -lt $Value.Count; $i++) { - $item = $Value[$i] - Write-Host "${indent}[$i]:" - if ($item -is [hashtable]) { - Print-Hashtable -Hashtable $item -IndentLevel ($IndentLevel + 1) - } else { - Print-Value -Value $item -IndentLevel ($IndentLevel + 1) - } - } - } else { - # Print simple values (non-hashtable, non-array) - Write-Host "${indent}$Value" - } - } - - # Iterate through each key-value pair in the hashtable - foreach ($key in $Hashtable.Keys) { - $indent = " " * ($IndentLevel * 4) - $value = $Hashtable[$key] - - if ($value -is [hashtable]) { - # Print key and call recursively if value is a nested hashtable - Write-Host "${indent}$key :" - Print-Hashtable -Hashtable $value -IndentLevel ($IndentLevel + 1) - } elseif ($value -is [array]) { - # Print arrays with an index for each element - Write-Host "${indent}$key :" - Print-Value -Value $value -IndentLevel ($IndentLevel + 1) - } else { - # Print key-value pairs for non-complex data types - Write-Host "${indent}$key : $value" - } - } -} - - -Export-ModuleMember -Function * +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof index 7493435d82..3b2c37bc14 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.schema.mof @@ -18,6 +18,8 @@ class MSFT_AADServicePrincipalAttributeValue [Write, Description("Name of the Attribute")] String AttributeName; [Write, Description("If the attribute has a string array value")] String StringArrayValue[]; [Write, Description("If the attribute has a int array value")] UInt32 IntArrayValue[]; + [Write, Description("If the attribute has a string value")] String StringValue; + [Write, Description("If the attribute has a int value")] UInt32 IntValue; [Write, Description("If the attribute has a boolean value")] Boolean BoolValue; }; From acdfd1a348ea3573595c59af0293a499e1849311 Mon Sep 17 00:00:00 2001 From: Piyush Dubey Date: Sat, 19 Oct 2024 21:17:07 +0530 Subject: [PATCH 03/60] minor --- .../MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 index c539a90625..85a0ee97c7 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 @@ -871,6 +871,7 @@ function Export-TargetResource try { $i = 1 + Write-Host "`r`n" -NoNewline $Script:ExportMode = $true [array] $Script:exportedInstances = Get-MgServicePrincipal -All:$true ` -Filter $Filter ` @@ -886,6 +887,7 @@ function Export-TargetResource $Global:M365DSCExportResourceInstancesCount++ } + Write-Host " |---[$i/$($Script:exportedInstances.Count)] $($AADServicePrincipal.DisplayName)" -NoNewline $Params = @{ Credential = $Credential ApplicationId = $ApplicationId @@ -938,6 +940,7 @@ function Export-TargetResource Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName + Write-Host $Global:M365DSCEmojiGreenCheckMark $i++ } } @@ -945,6 +948,7 @@ function Export-TargetResource } catch { + Write-Host $Global:M365DSCEmojiRedX New-M365DSCLogEntry -Message 'Error during Export:' ` -Exception $_ ` From 2de919a53b85f1f8d6d268363eb73bce6e08a410 Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Mon, 21 Oct 2024 17:36:34 +0530 Subject: [PATCH 04/60] initial commit --- .../MSFT_AADIdentityAPIConnector.psm1 | 566 ++++++++++++++++++ .../MSFT_AADIdentityAPIConnector.schema.mof | 18 + .../MSFT_AADIdentityAPIConnector/readme.md | 6 + .../settings.json | 25 + ...ft365DSC.AADIdentityAPIConnector.Tests.ps1 | 317 ++++++++++ Tests/Unit/Stubs/Microsoft365.psm1 | 287 +++++++++ 6 files changed, 1219 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/settings.json create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 new file mode 100644 index 0000000000..1c6c4bc75d --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 @@ -0,0 +1,566 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + #region resource generator code + [Parameter(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $TargetUrl, + + [Parameter(Mandatory = $true)] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Username, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Password, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Pkcs12Value, + + #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, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + try + { + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $nullResult = $PSBoundParameters + $nullResult.Ensure = 'Absent' + + $getValue = $null + #region resource generator code + $getValue = Get-MgBetaIdentityAPIConnector -IdentityApiConnectorId $Id -ErrorAction SilentlyContinue + + if ($null -eq $getValue) + { + Write-Verbose -Message "Could not find an Azure AD Identity A P I Connector with Id {$Id}" + + if (-not [System.String]::IsNullOrEmpty($DisplayName)) + { + $getValue = Get-MgBetaIdentityAPIConnector ` + -Filter "DisplayName eq '$DisplayName'" ` + -ErrorAction SilentlyContinue | Where-Object ` + -FilterScript { + $_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.IdentityApiConnector" + } + } + } + #endregion + if ($null -eq $getValue) + { + Write-Verbose -Message "Could not find an Azure AD Identity API Connector with DisplayName {$DisplayName}." + return $nullResult + } + $Id = $getValue.Id + Write-Verbose -Message "An Azure AD Identity API Connector with Id {$Id} and DisplayName {$DisplayName} was found" + + #region resource generator code + $complexAuthenticationConfiguration = @{} + + if($null -ne $getValue.AuthenticationConfiguration.AdditionalProperties.password) { + $securePassword = ConvertTo-SecureString $getValue.AuthenticationConfiguration.AdditionalProperties.password -AsPlainText -Force + + $Password = New-Object System.Management.Automation.PSCredential ('Password', $securePassword) + } + + if($null -ne $getValue.AuthenticationConfiguration.AdditionalProperties.pkcs12Value) { + $securePassword = ConvertTo-SecureString $getValue.AuthenticationConfiguration.AdditionalProperties.pkcs12Value -AsPlainText -Force + + $pkcs12Value = New-Object System.Management.Automation.PSCredential ('pkcs12Value', $securePassword) + } + #endregion + + $results = @{ + #region resource generator code + DisplayName = $getValue.DisplayName + TargetUrl = $getValue.TargetUrl + Id = $getValue.Id + Username = $getValue.AuthenticationConfiguration.AdditionalProperties.username + Password = $Password + Pkcs12Value = $pkcs12Value + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + ApplicationSecret = $ApplicationSecret + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + #endregion + } + + 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(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $TargetUrl, + + [Parameter(Mandatory = $true)] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Username, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Password, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Pkcs12Value, + + #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, + + [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 + + $BoundParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + + + if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + { + Write-Verbose -Message "Creating an Azure AD Identity API Connector with DisplayName {$DisplayName}" + + $createParameters = ([Hashtable]$BoundParameters).Clone() + $createParameters = Rename-M365DSCCimInstanceParameter -Properties $createParameters + $createParameters.Remove('Id') | Out-Null + + $createParameters.Remove('Password') | Out-Null + $createParameters.Remove('Pkcs12Value') | Out-Null + + $createParameters.Add("AuthenticationConfiguration", @{ + '@odata.type' = "microsoft.graph.basicAuthentication" + "password" = $Password.GetNetworkCredential().Password + "username" = $Username + }) + + $createParameters.Add("@odata.type", "#microsoft.graph.IdentityApiConnector") + $policy = New-MgBetaIdentityAPIConnector -BodyParameter $createParameters + } + elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + { + Write-Verbose -Message "Updating the Azure AD Identity API Connector with Id {$($currentInstance.Id)}" + + $updateParameters = ([Hashtable]$BoundParameters).Clone() + $updateParameters = Rename-M365DSCCimInstanceParameter -Properties $updateParameters + + $updateParameters.Remove('Id') | Out-Null + + $updateParameters.Remove('Password') | Out-Null + $updateParameters.Remove('Pkcs12Value') | Out-Null + + $updateParameters.Add("AuthenticationConfiguration", @{ + '@odata.type' = "microsoft.graph.basicAuthentication" + "password" = $Password.GetNetworkCredential().Password + "username" = $Username + }) + + $UpdateParameters.Add("@odata.type", "#microsoft.graph.IdentityApiConnector") + Update-MgBetaIdentityAPIConnector ` + -IdentityApiConnectorId $currentInstance.Id ` + -BodyParameter $UpdateParameters + } + elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + { + Write-Verbose -Message "Removing the Azure AD Identity API Connector with Id {$($currentInstance.Id)}" + Remove-MgBetaIdentityAPIConnector -IdentityApiConnectorId $currentInstance.Id + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + #region resource generator code + [Parameter(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $TargetUrl, + + [Parameter(Mandatory = $true)] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Username, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Password, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Pkcs12Value, + + #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, + + [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 + + Write-Verbose -Message "Testing configuration of the Azure AD Identity A P I Connector with Id {$Id} and DisplayName {$DisplayName}" + + $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() + + 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 ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') + { + $testResult = Compare-M365DSCComplexObject ` + -Source ($source) ` + -Target ($target) + + if (-not $testResult) + { + break + } + + $ValuesToCheck.Remove($key) | Out-Null + } + } + + $ValuesToCheck.Remove('Id') | Out-Null + $ValuesToCheck.Remove('Password') | Out-Null + $ValuesToCheck.Remove('Pkcs12Value') | Out-Null + $ValuesToCheck = Remove-M365DSCAuthenticationParameter -BoundParameters $ValuesToCheck + + 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.String] + $Filter, + + [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 + ) + + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + try + { + #region resource generator code + [array]$getValue = Get-MgBetaIdentityAPIConnector ` + -Filter $Filter ` + -All ` + -ErrorAction Stop + #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 + } + elseif (-not [string]::IsNullOrEmpty($config.name)) + { + $displayedKey = $config.name + } + 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 + AccessTokens = $AccessTokens + } + + $Results = Get-TargetResource @Params + $Results.Password = "New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString ((New-Guid).ToString()) -AsPlainText -Force));" + + $Results.Pkcs12Value = "New-Object System.Management.Automation.PSCredential('Pkcs12Value', (ConvertTo-SecureString ((New-Guid).ToString()) -AsPlainText -Force));" + + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + + + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Pkcs12Value' + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Password' + + $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_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof new file mode 100644 index 0000000000..7c359ad403 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof @@ -0,0 +1,18 @@ +[ClassVersion("1.0.0.0"), FriendlyName("AADIdentityAPIConnector")] +class MSFT_AADIdentityAPIConnector : OMI_BaseResource +{ + [Required, Description("The name of the API connector.")] String DisplayName; + [Write, Description("The URL of the API endpoint to call.")] String TargetUrl; + [Key, Description("The unique identifier for an entity. Read-only.")] String Id; + [Write, Description("The username of the password")] String Username; + [Write, Description("The password of certificate/basic auth"), EmbeddedInstance("MSFT_Credential")] String Password; + [Write, Description("The Pkcs12Value of the certificate"), EmbeddedInstance("MSFT_Credential")] String Pkcs12Value; + [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[]; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/readme.md new file mode 100644 index 0000000000..cfafce7110 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/readme.md @@ -0,0 +1,6 @@ + +# AADIdentityAPIConnector + +## Description + +Azure AD Identity API Connector diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/settings.json new file mode 100644 index 0000000000..327fd87153 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/settings.json @@ -0,0 +1,25 @@ +{ + "resourceName": "AADIdentityAPIConnector", + "description": "This resource configures an Azure AD Identity A P I Connector.", + "permissions": { + "graph": { + "delegated": { + "read": [ + + ], + "update": [ + + ] + }, + "application": { + "read": [ + + ], + "update": [ + + ] + } + } +} + +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 new file mode 100644 index 0000000000..18e4a5281a --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 @@ -0,0 +1,317 @@ +[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 "AADIdentityAPIConnector" -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 Get-PSSession -MockWith { + } + + Mock -CommandName Remove-PSSession -MockWith { + } + + Mock -CommandName Update-MgBetaIdentityAPIConnector -MockWith { + } + + Mock -CommandName New-MgBetaIdentityAPIConnector -MockWith { + } + + Mock -CommandName Remove-MgBetaIdentityAPIConnector -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return "Credentials" + } + + # 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 AADIdentityAPIConnector should exist but it DOES NOT" -Fixture { + BeforeAll { + $testParams = @{ + AuthenticationConfiguration = (New-CimInstance -ClassName MSFT_MicrosoftGraphapiAuthenticationConfigurationBase -Property @{ + Password = "FakeStringValue" + Pkcs12Value = "FakeStringValue" + CertificateList = [CimInstance[]]@( + (New-CimInstance -ClassName MSFT_MicrosoftGraphpkcs12CertificateInformation -Property @{ + IsActive = $True + NotAfter = $True + Thumbprint = "FakeStringValue" + NotBefore = $True + } -ClientOnly) + ) + Username = "FakeStringValue" + odataType = "#microsoft.graph.basicAuthentication" + } -ClientOnly) + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + TargetUrl = "FakeStringValue" + Ensure = "Present" + Credential = $Credential; + } + + Mock -CommandName Get-MgBetaIdentityAPIConnector -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-MgBetaIdentityAPIConnector -Exactly 1 + } + } + + Context -Name "The AADIdentityAPIConnector exists but it SHOULD NOT" -Fixture { + BeforeAll { + $testParams = @{ + AuthenticationConfiguration = (New-CimInstance -ClassName MSFT_MicrosoftGraphapiAuthenticationConfigurationBase -Property @{ + Password = "FakeStringValue" + Pkcs12Value = "FakeStringValue" + CertificateList = [CimInstance[]]@( + (New-CimInstance -ClassName MSFT_MicrosoftGraphpkcs12CertificateInformation -Property @{ + IsActive = $True + NotAfter = $True + Thumbprint = "FakeStringValue" + NotBefore = $True + } -ClientOnly) + ) + Username = "FakeStringValue" + odataType = "#microsoft.graph.basicAuthentication" + } -ClientOnly) + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + TargetUrl = "FakeStringValue" + Ensure = 'Absent' + Credential = $Credential; + } + + Mock -CommandName Get-MgBetaIdentityAPIConnector -MockWith { + return @{ + AdditionalProperties = @{ + '@odata.type' = "#microsoft.graph.IdentityApiConnector" + } + AuthenticationConfiguration = @{ + '@odata.type' = "#microsoft.graph.basicAuthentication" + Pkcs12Value = "FakeStringValue" + Password = "FakeStringValue" + Username = "FakeStringValue" + CertificateList = @( + @{ + IsActive = $True + NotAfter = $True + Thumbprint = "FakeStringValue" + NotBefore = $True + } + ) + } + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + TargetUrl = "FakeStringValue" + + } + } + } + + 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-MgBetaIdentityAPIConnector -Exactly 1 + } + } + Context -Name "The AADIdentityAPIConnector Exists and Values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + AuthenticationConfiguration = (New-CimInstance -ClassName MSFT_MicrosoftGraphapiAuthenticationConfigurationBase -Property @{ + Password = "FakeStringValue" + Pkcs12Value = "FakeStringValue" + CertificateList = [CimInstance[]]@( + (New-CimInstance -ClassName MSFT_MicrosoftGraphpkcs12CertificateInformation -Property @{ + IsActive = $True + NotAfter = $True + Thumbprint = "FakeStringValue" + NotBefore = $True + } -ClientOnly) + ) + Username = "FakeStringValue" + odataType = "#microsoft.graph.basicAuthentication" + } -ClientOnly) + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + TargetUrl = "FakeStringValue" + Ensure = 'Present' + Credential = $Credential; + } + + Mock -CommandName Get-MgBetaIdentityAPIConnector -MockWith { + return @{ + AdditionalProperties = @{ + '@odata.type' = "#microsoft.graph.IdentityApiConnector" + } + AuthenticationConfiguration = @{ + '@odata.type' = "#microsoft.graph.basicAuthentication" + Pkcs12Value = "FakeStringValue" + Password = "FakeStringValue" + Username = "FakeStringValue" + CertificateList = @( + @{ + IsActive = $True + NotAfter = $True + Thumbprint = "FakeStringValue" + NotBefore = $True + } + ) + } + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + TargetUrl = "FakeStringValue" + + } + } + } + + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name "The AADIdentityAPIConnector exists and values are NOT in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + AuthenticationConfiguration = (New-CimInstance -ClassName MSFT_MicrosoftGraphapiAuthenticationConfigurationBase -Property @{ + Password = "FakeStringValue" + Pkcs12Value = "FakeStringValue" + CertificateList = [CimInstance[]]@( + (New-CimInstance -ClassName MSFT_MicrosoftGraphpkcs12CertificateInformation -Property @{ + IsActive = $True + NotAfter = $True + Thumbprint = "FakeStringValue" + NotBefore = $True + } -ClientOnly) + ) + Username = "FakeStringValue" + odataType = "#microsoft.graph.basicAuthentication" + } -ClientOnly) + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + TargetUrl = "FakeStringValue" + Ensure = 'Present' + Credential = $Credential; + } + + Mock -CommandName Get-MgBetaIdentityAPIConnector -MockWith { + return @{ + AuthenticationConfiguration = @{ + '@odata.type' = "#microsoft.graph.basicAuthentication" + Pkcs12Value = "FakeStringValue" + Password = "FakeStringValue" + Username = "FakeStringValue" + CertificateList = @( + @{ + Thumbprint = "FakeStringValue" + } + ) + } + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + TargetUrl = "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-MgBetaIdentityAPIConnector -Exactly 1 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential + } + + Mock -CommandName Get-MgBetaIdentityAPIConnector -MockWith { + return @{ + AdditionalProperties = @{ + '@odata.type' = "#microsoft.graph.IdentityApiConnector" + } + AuthenticationConfiguration = @{ + '@odata.type' = "#microsoft.graph.basicAuthentication" + Pkcs12Value = "FakeStringValue" + Password = "FakeStringValue" + Username = "FakeStringValue" + CertificateList = @( + @{ + IsActive = $True + NotAfter = $True + Thumbprint = "FakeStringValue" + NotBefore = $True + } + ) + } + DisplayName = "FakeStringValue" + Id = "FakeStringValue" + TargetUrl = "FakeStringValue" + + } + } + } + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index cdcf592740..f44a1bebb7 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -97638,3 +97638,290 @@ function Invoke-PnPSPRestMethod $Content ) } +#region MgBetaIdentityAPIConnector +function Get-MgBetaIdentityApiConnector +{ + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $IdentityApiConnectorId, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.String[]] + $ExpandProperty, + + [Parameter()] + [System.String[]] + $Property, + + [Parameter()] + [System.String] + $Filter, + + [Parameter()] + [System.String] + $Search, + + [Parameter()] + [System.Int32] + $Skip, + + [Parameter()] + [System.String[]] + $Sort, + + [Parameter()] + [System.Int32] + $Top, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject[]] + $HttpPipelineAppend, + + [Parameter()] + [PSObject[]] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Int32] + $PageSize, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $All, + + [Parameter()] + [System.String] + $CountVariable + ) +} + +function New-MgBetaIdentityApiConnector +{ + [CmdletBinding()] + param + ( + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Collections.Hashtable] + $AuthenticationConfiguration, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $TargetUrl, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject[]] + $HttpPipelineAppend, + + [Parameter()] + [PSObject[]] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm + ) +} + +function Remove-MgBetaIdentityApiConnector +{ + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $IdentityApiConnectorId, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject[]] + $HttpPipelineAppend, + + [Parameter()] + [PSObject[]] + $HttpPipelinePrepend, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $PassThru, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm + ) +} + +function Update-MgBetaIdentityApiConnector +{ + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $IdentityApiConnectorId, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Collections.Hashtable] + $AuthenticationConfiguration, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $TargetUrl, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject[]] + $HttpPipelineAppend, + + [Parameter()] + [PSObject[]] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm + ) +} + +#endregion + From 7a1568241f712499b32256018d77ff4148a9d61a Mon Sep 17 00:00:00 2001 From: "Arpita Mohapatra (from Dev Box)" Date: Mon, 21 Oct 2024 18:46:42 +0530 Subject: [PATCH 05/60] intial changes --- ...MSFT_EXOMailboxAuditBypassAssociation.psm1 | 343 ++++++++++++++++++ ...XOMailboxAuditBypassAssociation.schema.mof | 13 + .../readme.md | 5 + .../settings.json | 35 ++ .../2-Update.ps1 | 34 ++ ...EXOMailboxAuditBypassAssociation.Tests.ps1 | 162 +++++++++ Tests/Unit/Stubs/Microsoft365.psm1 | 26 ++ 7 files changed, 618 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/EXOMailboxAuditBypassAssociation/2-Update.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOMailboxAuditBypassAssociation.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.psm1 new file mode 100644 index 0000000000..b38f64321c --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.psm1 @@ -0,0 +1,343 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter()] + [System.Boolean] + $AuditBypassEnabled, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure, + + [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 + ) + + New-M365DSCConnection -Workload 'ExchangeOnline' ` + -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) + { + $instance = $Script:exportedInstances | Where-Object -FilterScript {$_.Identity -eq $Identity} + } + else + { + $instance = Get-MailboxAuditBypassAssociation -Identity $Identity -ErrorAction Stop + } + if ($null -eq $instance) + { + return $nullResult + } + + $results = @{ + Identity = [System.String]$Identity + AuditBypassEnabled = [System.Boolean]$instance.AuditBypassEnabled + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + 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 + ( + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter()] + [System.Boolean] + $AuditBypassEnabled, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure, + + [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 + + Set-MailboxAuditBypassAssociation @SetParameters +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter()] + [System.Boolean] + $AuditBypassEnabled, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure, + + [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 + ) + + $ConnectionMode = New-M365DSCConnection -Workload 'ExchangeOnline' ` + -InboundParameters $PSBoundParameters + + 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 + [array] $Script:exportedInstances = Get-MailboxAuditBypassAssociation -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) + { + $displayedKey = $config.Identity + Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline + $params = @{ + Identity = $config.Identity + 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 '' + } +} diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.schema.mof new file mode 100644 index 0000000000..59381b5ba7 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.schema.mof @@ -0,0 +1,13 @@ +[ClassVersion("1.0.0.0"), FriendlyName("EXOMailboxAuditBypassAssociation")] +class MSFT_EXOMailboxAuditBypassAssociation : OMI_BaseResource +{ + [Key, Description("The Identity parameter specifies the user account or computer account where you want to view the value of the AuditBypassEnabled property.")] String Identity; + [Write, Description("The AuditBypassEnabled parameter specifies whether audit bypass is enabled for the user or computer.")] Boolean AuditBypassEnabled; + [Write, Description("Specifies if this instance should exist."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] 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_EXOMailboxAuditBypassAssociation/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/readme.md new file mode 100644 index 0000000000..092c6a9914 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/readme.md @@ -0,0 +1,5 @@ +# EXOMailboxAuditBypassAssociation + +## Description + +Use the Set-MailboxAuditBypassAssociation cmdlet to configure mailbox audit logging bypass for user or computer accounts such as service accounts for applications that access mailboxes frequently. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json new file mode 100644 index 0000000000..cbacd227bb --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json @@ -0,0 +1,35 @@ +{ + "resourceName": "EXOJournalRule", + "description": "", + "roles": { + "read": [ + "Global Reader" + ], + "update": [ + "Exchange Administrator" + ] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [], + "update": [] + } + }, + "exchange": { + "requiredroles": [ + "Compliance Admin", + "View-Only Configuration", + "Journaling" + ], + "requiredrolegroups": [ + "Organization Management", + "Compliance Management" + ] + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOMailboxAuditBypassAssociation/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOMailboxAuditBypassAssociation/2-Update.ps1 new file mode 100644 index 0000000000..0d7c78206c --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOMailboxAuditBypassAssociation/2-Update.ps1 @@ -0,0 +1,34 @@ +<# +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 + { + EXOMailboxAuditBypassAssociation "EXOMailboxAuditBypassAssociation-Test" + { + AuditBypassEnabled = $True; #Updated Property + Credential = $Credscredential; + Ensure = "Present"; + Identity = "TestMailbox109"; + } + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOMailboxAuditBypassAssociation.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOMailboxAuditBypassAssociation.Tests.ps1 new file mode 100644 index 0000000000..882ae49894 --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOMailboxAuditBypassAssociation.Tests.ps1 @@ -0,0 +1,162 @@ +[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" + } + + Mock -CommandName Set-MailboxAuditBypassAssociation -MockWith { + return $null + } + + # Mock Write-Host to hide output during the tests + Mock -CommandName Write-Host -MockWith { + } + $Script:exportedInstances =$null + $Script:ExportMode = $false + } + # Test contexts + + Context -Name 'Settings are not in the desired state' -Fixture { + BeforeAll { + $testParams = @{ + AuditBypassEnabled = $False; + Credential = $Credscredential; + Ensure = "Present"; + Identity = "TestMailbox109"; + } + + Mock -CommandName Get-MailboxAuditBypassAssociation -MockWith { + return @{ + AuditBypassEnabled = $True; #Drift + Credential = $Credscredential; + Ensure = "Present"; + Identity = "TestMailbox109"; + } + } + } + + 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 Set-MailboxAuditBypassAssociation -Exactly 1 + } + + It 'Should return Present from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + } + + Context -Name 'Settings are already in the desired state' -Fixture { + BeforeAll { + $testParams = @{ + AuditBypassEnabled = $False; + Credential = $Credscredential; + Ensure = "Present"; + Identity = "TestMailbox109"; + } + + Mock -CommandName Get-MailboxAuditBypassAssociation -MockWith { + return @{ + AuditBypassEnabled = $False; + Credential = $Credscredential; + Ensure = "Present"; + Identity = "TestMailbox109"; + } + } + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + + It 'Should return Present from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Present' + } + } + + Context -Name "User doesn't exist" -Fixture { + BeforeAll { + $testParams = @{ + AuditBypassEnabled = $False; + Credential = $Credscredential; + Ensure = "Present"; + Identity = "TestMailbox109"; + } + + Mock -CommandName Get-MailboxAuditBypassAssociation -MockWith { + return $null + } + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should return Absent from the Get method' { + (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' + } + } + + + + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential; + } + + Mock -CommandName Get-MailboxAuditBypassAssociation -MockWith { + return @{ + AuditBypassEnabled = $False; + Credential = $Credscredential; + Ensure = "Present"; + Identity = "TestMailbox109"; + } + } + } + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index cdcf592740..2578db280b 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -2768,6 +2768,19 @@ function Get-Mailbox $IncludeEmailAddressDisplayNames ) } +function Get-MailboxAuditBypassAssociation +{ + [CmdletBinding()] + param( + [Parameter()] + [System.Object] + $Identity, + + [Parameter()] + [System.Object] + $ResultSize + ) +} function Get-MailboxAutoReplyConfiguration { [CmdletBinding()] @@ -12123,6 +12136,19 @@ function Set-Mailbox $MessageTrackingReadStatusEnabled ) } +function Set-MailboxAuditBypassAssociation +{ + [CmdletBinding()] + param( + [Parameter()] + [System.Boolean] + $AuditBypassEnabled, + + [Parameter()] + [System.Object] + $Identity + ) +} function Set-MailboxAutoReplyConfiguration { [CmdletBinding()] From 3be4b1587c3d484ff37c40ed39e15f9a8ec0a5f9 Mon Sep 17 00:00:00 2001 From: "Arpita Mohapatra (from Dev Box)" Date: Mon, 21 Oct 2024 22:35:35 +0530 Subject: [PATCH 06/60] put proper resource name in settings.json --- .../MSFT_EXOMailboxAuditBypassAssociation/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json index cbacd227bb..35a9d6c51d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json @@ -1,5 +1,5 @@ { - "resourceName": "EXOJournalRule", + "resourceName": "EXOMailboxAuditBypassAssociation", "description": "", "roles": { "read": [ From 113ce98c372fae7c5c9adebe3c2812d129433e30 Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Tue, 22 Oct 2024 10:29:17 +0530 Subject: [PATCH 07/60] Making changes for certificates --- .../MSFT_AADIdentityAPIConnector.psm1 | 54 ++++++++++++++----- .../MSFT_AADIdentityAPIConnector.schema.mof | 11 +++- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 index 1c6c4bc75d..60f6d95ac3 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 @@ -26,8 +26,8 @@ function Get-TargetResource $Password, [Parameter()] - [System.Management.Automation.PSCredential] - $Pkcs12Value, + [Microsoft.Management.Infrastructure.CimInstance[]] + $Certificates, #endregion @@ -121,10 +121,19 @@ function Get-TargetResource $Password = New-Object System.Management.Automation.PSCredential ('Password', $securePassword) } - if($null -ne $getValue.AuthenticationConfiguration.AdditionalProperties.pkcs12Value) { - $securePassword = ConvertTo-SecureString $getValue.AuthenticationConfiguration.AdditionalProperties.pkcs12Value -AsPlainText -Force - $pkcs12Value = New-Object System.Management.Automation.PSCredential ('pkcs12Value', $securePassword) + $complexCertificates = @() + foreach ($currentCertificate in $getValue.AuthenticationConfiguration.AdditionalProperties.certificateList) + { + $myCertificate= @{} + $myCertificate.Add('Pkcs12Value', "Please insert a valid Pkcs12Value") + $myCertificate.Add('Thumbprint', $currentCertificate.thumbprint) + $myCertificate.Add('IsActive', $currentCertificate.isActive) + + if ($myCertificate.values.Where({$null -ne $_}).Count -gt 0) + { + $complexCertificates += $myCertificate + } } #endregion @@ -135,7 +144,7 @@ function Get-TargetResource Id = $getValue.Id Username = $getValue.AuthenticationConfiguration.AdditionalProperties.username Password = $Password - Pkcs12Value = $pkcs12Value + Certificates = $complexCertificates Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId @@ -187,8 +196,8 @@ function Set-TargetResource $Password, [Parameter()] - [System.Management.Automation.PSCredential] - $Pkcs12Value, + [Microsoft.Management.Infrastructure.CimInstance[]] + $Certificates, #endregion @@ -321,8 +330,8 @@ function Test-TargetResource $Password, [Parameter()] - [System.Management.Automation.PSCredential] - $Pkcs12Value, + [Microsoft.Management.Infrastructure.CimInstance[]] + $Certificates, #endregion @@ -526,11 +535,25 @@ function Export-TargetResource $Results = Get-TargetResource @Params $Results.Password = "New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString ((New-Guid).ToString()) -AsPlainText -Force));" - $Results.Pkcs12Value = "New-Object System.Management.Automation.PSCredential('Pkcs12Value', (ConvertTo-SecureString ((New-Guid).ToString()) -AsPlainText -Force));" - $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results + + if ($null -ne $Results.Certificates) + { + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.Certificates` + -CIMInstanceName 'AADIdentityAPIConnectionCertificate' + if (-not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.Certificates = $complexTypeStringResult + } + else + { + $Results.Remove('Certificates') | Out-Null + } + } + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` -ConnectionMode $ConnectionMode ` -ModulePath $PSScriptRoot ` @@ -538,9 +561,14 @@ function Export-TargetResource -Credential $Credential - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Pkcs12Value' $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Password' + + if ($Results.Certificates) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName "Certificates" -IsCIMArray:$True + } + $dscContent += $currentDSCBlock Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof index 7c359ad403..8407b6ae24 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof @@ -1,3 +1,12 @@ +[ClassVersion("1.0.0")] +class MSFT_AADIdentityAPIConnectionCertificate +{ + [Write, Description("")] String Pkcs12Value; + [Write, Description("")] String Thumbprint; + [Write, Description("")] Boolean IsActive; +}; + + [ClassVersion("1.0.0.0"), FriendlyName("AADIdentityAPIConnector")] class MSFT_AADIdentityAPIConnector : OMI_BaseResource { @@ -6,7 +15,7 @@ class MSFT_AADIdentityAPIConnector : OMI_BaseResource [Key, Description("The unique identifier for an entity. Read-only.")] String Id; [Write, Description("The username of the password")] String Username; [Write, Description("The password of certificate/basic auth"), EmbeddedInstance("MSFT_Credential")] String Password; - [Write, Description("The Pkcs12Value of the certificate"), EmbeddedInstance("MSFT_Credential")] String Pkcs12Value; + [Write, Description(""), EmbeddedInstance("MSFT_AADIdentityAPIConnectionCertificate")] String Certificates[]; [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; From c213db26a2c6663bb3d79c382e6651bbcb9f6f21 Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Tue, 22 Oct 2024 10:54:30 +0530 Subject: [PATCH 08/60] temp --- .../MSFT_AADIdentityAPIConnector.psm1 | 83 +++++++++++-------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 index 60f6d95ac3..bf311e9b62 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 @@ -251,54 +251,67 @@ function Set-TargetResource $BoundParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + # If the certificates array is not empty, then we need to create a new instance of New-MgBetaAADIdentityAPIConnector - if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') - { - Write-Verbose -Message "Creating an Azure AD Identity API Connector with DisplayName {$DisplayName}" + $needToUpdateCertificates = $false + if($null -ne $Certificates -and $Certificates.Count -gt 0) { + $needToUpdateCertificates = $true + } - $createParameters = ([Hashtable]$BoundParameters).Clone() - $createParameters = Rename-M365DSCCimInstanceParameter -Properties $createParameters - $createParameters.Remove('Id') | Out-Null + if($needToUpdateCertificates -eq $false) { + if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + { + Write-Verbose -Message "Creating an Azure AD Identity API Connector with DisplayName {$DisplayName}" - $createParameters.Remove('Password') | Out-Null - $createParameters.Remove('Pkcs12Value') | Out-Null + $createParameters = ([Hashtable]$BoundParameters).Clone() + $createParameters = Rename-M365DSCCimInstanceParameter -Properties $createParameters + $createParameters.Remove('Id') | Out-Null - $createParameters.Add("AuthenticationConfiguration", @{ - '@odata.type' = "microsoft.graph.basicAuthentication" - "password" = $Password.GetNetworkCredential().Password - "username" = $Username - }) + $createParameters.Remove('Password') | Out-Null + $createParameters.Remove('Pkcs12Value') | Out-Null - $createParameters.Add("@odata.type", "#microsoft.graph.IdentityApiConnector") - $policy = New-MgBetaIdentityAPIConnector -BodyParameter $createParameters - } - elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') - { - Write-Verbose -Message "Updating the Azure AD Identity API Connector with Id {$($currentInstance.Id)}" + if($username -ne $null) { + $createParameters.Add("AuthenticationConfiguration", @{ + '@odata.type' = "microsoft.graph.basicAuthentication" + "password" = $Password.GetNetworkCredential().Password + "username" = $Username + }) + } - $updateParameters = ([Hashtable]$BoundParameters).Clone() - $updateParameters = Rename-M365DSCCimInstanceParameter -Properties $updateParameters + $createParameters.Add("@odata.type", "#microsoft.graph.IdentityApiConnector") + $policy = New-MgBetaIdentityAPIConnector -BodyParameter $createParameters + } + elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + { + Write-Verbose -Message "Updating the Azure AD Identity API Connector with Id {$($currentInstance.Id)}" + + $updateParameters = ([Hashtable]$BoundParameters).Clone() + $updateParameters = Rename-M365DSCCimInstanceParameter -Properties $updateParameters - $updateParameters.Remove('Id') | Out-Null + $updateParameters.Remove('Id') | Out-Null - $updateParameters.Remove('Password') | Out-Null - $updateParameters.Remove('Pkcs12Value') | Out-Null + $updateParameters.Remove('Password') | Out-Null + $updateParameters.Remove('Pkcs12Value') | Out-Null - $updateParameters.Add("AuthenticationConfiguration", @{ - '@odata.type' = "microsoft.graph.basicAuthentication" - "password" = $Password.GetNetworkCredential().Password - "username" = $Username - }) + $updateParameters.Add("AuthenticationConfiguration", @{ + '@odata.type' = "microsoft.graph.basicAuthentication" + "password" = $Password.GetNetworkCredential().Password + "username" = $Username + }) - $UpdateParameters.Add("@odata.type", "#microsoft.graph.IdentityApiConnector") - Update-MgBetaIdentityAPIConnector ` + $UpdateParameters.Add("@odata.type", "#microsoft.graph.IdentityApiConnector") + Update-MgBetaIdentityAPIConnector ` -IdentityApiConnectorId $currentInstance.Id ` -BodyParameter $UpdateParameters + } + elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + { + Write-Verbose -Message "Removing the Azure AD Identity API Connector with Id {$($currentInstance.Id)}" + Remove-MgBetaIdentityAPIConnector -IdentityApiConnectorId $currentInstance.Id + } } - elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') - { - Write-Verbose -Message "Removing the Azure AD Identity API Connector with Id {$($currentInstance.Id)}" - Remove-MgBetaIdentityAPIConnector -IdentityApiConnectorId $currentInstance.Id + else { + Remove-MgBetaIdentityAPIConnector -IdentityApiConnectorId $currentInstance.Id } } From d4aa20d86537a434b2e4babf5dbb8de29331db7c Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Tue, 22 Oct 2024 15:19:07 +0530 Subject: [PATCH 09/60] added logic to upload certificates --- .../MSFT_AADIdentityAPIConnector.psm1 | 85 ++++++++++++++++++- .../MSFT_AADIdentityAPIConnector.schema.mof | 1 + 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 index bf311e9b62..8218e0f355 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 @@ -128,6 +128,7 @@ function Get-TargetResource $myCertificate= @{} $myCertificate.Add('Pkcs12Value', "Please insert a valid Pkcs12Value") $myCertificate.Add('Thumbprint', $currentCertificate.thumbprint) + $myCertificate.Add('Password', "Please insert a valid Password for the certificate") $myCertificate.Add('IsActive', $currentCertificate.isActive) if ($myCertificate.values.Where({$null -ne $_}).Count -gt 0) @@ -311,7 +312,90 @@ function Set-TargetResource } } else { + + # Remove the existing instance if already present + if($currentInstance.Ensure -ne 'Absent') { + Write-Verbose -Message "Removing the Azure AD Identity API Connector with Id {$($currentInstance.Id)}" Remove-MgBetaIdentityAPIConnector -IdentityApiConnectorId $currentInstance.Id + } + + # Create a new instance with the certificates + Write-Verbose -Message "Creating an Azure AD Identity API Connector with DisplayName {$DisplayName}" + + $createParameters = ([Hashtable]$BoundParameters).Clone() + $createParameters = Rename-M365DSCCimInstanceParameter -Properties $createParameters + $createParameters.Remove('Id') | Out-Null + + $createParameters.Remove('Password') | Out-Null + $createParameters.Remove('Pkcs12Value') | Out-Null + + # Get the active and inactive certificates + $activeCertificates = @() + $inactiveCertificates = @() + foreach ($currentCertificate in $Certificates) + { + $myCertificate = @{} + $myCertificate.Add('Pkcs12Value', $currentCertificate.Pkcs12Value) + $myCertificate.Add('Password', $currentCertificate.Password) + + if($currentCertificate.IsActive -eq $true) { + $activeCertificates += $myCertificate + } + else { + $inactiveCertificates += $myCertificate + } + } + + # Only one certificate can be active + if($activeCertificates.Count -ne 1) { + Write-Error "There should be one active certificate" + throw + } + + if($inactiveCertificates.Count -eq 0) { + $createParameters.Add("AuthenticationConfiguration", @{ + '@odata.type' = "microsoft.graph.pkcs12Certificate" + "password" = $activeCertificates[0].Password + "pkcs12Value" = $activeCertificates[0].Pkcs12Value + }) + $activeCertificates = $activeCertificates[1..$activeCertificates.Count] + } + else { + $createParameters.Add("AuthenticationConfiguration", @{ + '@odata.type' = "microsoft.graph.pkcs12Certificate" + "password" = $inactiveCertificates[0].Password + "pkcs12Value" = $inactiveCertificates[0].Pkcs12Value + }) + # remove the first element from the inactive certificates + $inactiveCertificates = $inactiveCertificates[1..$inactiveCertificates.Count] + } + + $createParameters.Add("@odata.type", "#microsoft.graph.IdentityApiConnector") + $policy = New-MgBetaIdentityAPIConnector -BodyParameter $createParameters + + + # Upload the inactive certificates + foreach ($currentCertificate in $inactiveCertificates) + { + $params = @{ + pkcs12Value = $currentCertificate.Pkcs12Value + password = $currentCertificate.Password + } + + Invoke-MgBetaUploadIdentityApiConnectorClientCertificate -IdentityApiConnectorId $policy.Id -BodyParameter $params + } + + # Upload active certificate + foreach ($currentCertificate in $activeCertificates) + { + $params = @{ + pkcs12Value = $currentCertificate.Pkcs12Value + password = $currentCertificate.Password + } + + Invoke-MgBetaUploadIdentityApiConnectorClientCertificate -IdentityApiConnectorId $policy.Id -BodyParameter $params + } + } } @@ -428,7 +512,6 @@ function Test-TargetResource $ValuesToCheck.Remove('Id') | Out-Null $ValuesToCheck.Remove('Password') | Out-Null - $ValuesToCheck.Remove('Pkcs12Value') | Out-Null $ValuesToCheck = Remove-M365DSCAuthenticationParameter -BoundParameters $ValuesToCheck Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof index 8407b6ae24..32f16ddc5e 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof @@ -3,6 +3,7 @@ class MSFT_AADIdentityAPIConnectionCertificate { [Write, Description("")] String Pkcs12Value; [Write, Description("")] String Thumbprint; + [Write, Description("")] String Password; [Write, Description("")] Boolean IsActive; }; From d239edb117fa83ef32ff0779888321e2b31bc7c0 Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Tue, 22 Oct 2024 17:22:16 +0530 Subject: [PATCH 10/60] Added MSFT_Credentials in password and pkcs12Value --- .../MSFT_AADIdentityAPIConnector.psm1 | 14 ++++---------- .../MSFT_AADIdentityAPIConnector.schema.mof | 4 ++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 index 8218e0f355..60116d209c 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 @@ -97,10 +97,7 @@ function Get-TargetResource { $getValue = Get-MgBetaIdentityAPIConnector ` -Filter "DisplayName eq '$DisplayName'" ` - -ErrorAction SilentlyContinue | Where-Object ` - -FilterScript { - $_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.IdentityApiConnector" - } + -ErrorAction SilentlyContinue } } #endregion @@ -335,8 +332,8 @@ function Set-TargetResource foreach ($currentCertificate in $Certificates) { $myCertificate = @{} - $myCertificate.Add('Pkcs12Value', $currentCertificate.Pkcs12Value) - $myCertificate.Add('Password', $currentCertificate.Password) + $myCertificate.Add('Pkcs12Value', ($currentCertificate.Pkcs12Value).Password) + $myCertificate.Add('Password', ($currentCertificate.Password).Password) if($currentCertificate.IsActive -eq $true) { $activeCertificates += $myCertificate @@ -629,7 +626,7 @@ function Export-TargetResource } $Results = Get-TargetResource @Params - $Results.Password = "New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString ((New-Guid).ToString()) -AsPlainText -Force));" + $Results.Password = "Please insert a valid Password" $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results @@ -657,9 +654,6 @@ function Export-TargetResource -Credential $Credential - $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'Password' - - if ($Results.Certificates) { $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName "Certificates" -IsCIMArray:$True diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof index 32f16ddc5e..9d7db64611 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof @@ -1,9 +1,9 @@ [ClassVersion("1.0.0")] class MSFT_AADIdentityAPIConnectionCertificate { - [Write, Description("")] String Pkcs12Value; + [Write, Description(""), EmbeddedInstance("MSFT_Credential")] String Pkcs12Value; [Write, Description("")] String Thumbprint; - [Write, Description("")] String Password; + [Write, Description(""), EmbeddedInstance("MSFT_Credential")] String Password; [Write, Description("")] Boolean IsActive; }; From 48efcff2752562b398cccffe1416670c90972b77 Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Tue, 22 Oct 2024 20:00:54 +0530 Subject: [PATCH 11/60] Fixed Test Target Resource --- .../MSFT_AADIdentityAPIConnector.psm1 | 60 +++++++++++++++++-- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 index 60116d209c..04f48ad77f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.psm1 @@ -494,16 +494,64 @@ function Test-TargetResource $target = $CurrentValues.$key if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') { - $testResult = Compare-M365DSCComplexObject ` - -Source ($source) ` - -Target ($target) + + # create a list of thumbprints from the source list + $sourceThumbprints = @() + foreach ($item in $source) + { + $myCertificate = @{} + $myCertificate.Add('Thumbprint', $item.Thumbprint) + $myCertificate.Add('IsActive', $item.IsActive) + $sourceThumbprints += $myCertificate + } + + # create a list of thumbprints from the target list + $targetThumbprints = @() + foreach ($item in $target) + { + $myCertificate = @{} + $myCertificate.Add('Thumbprint', $item.Thumbprint) + $myCertificate.Add('IsActive', $item.IsActive) + $targetThumbprints += $myCertificate + } + # sort the lists + $sourceThumbprints = $sourceThumbprints | Sort-Object -Property { $_.Thumbprint } + $targetThumbprints = $targetThumbprints | Sort-Object -Property { $_.Thumbprint } + + # print the list in verbose logs + foreach ($item in $sourceThumbprints) + { + Write-Verbose -Message "Source Thumbprints: $(Convert-M365DscHashtableToString -Hashtable $item)" + } - if (-not $testResult) + foreach ($item in $targetThumbprints) { - break + Write-Verbose -Message "Target Thumbprints: $(Convert-M365DscHashtableToString -Hashtable $item)" + } + + # check if the lists are identical + $compareResult = $true + if ($sourceThumbprints.Count -ne $targetThumbprints.Count) + { + $compareResult = $false + } + else + { + for ($i = 0; $i -lt $sourceThumbprints.Count; $i++) + { + if ($sourceThumbprints[$i].Thumbprint -ne $targetThumbprints[$i].Thumbprint) + { + $compareResult = $false + Write-Verbose -Message "Thumbprint mismatch: $($sourceThumbprints[$i].Thumbprint) - $($targetThumbprints[$i].Thumbprint)" + break + } + } } - $ValuesToCheck.Remove($key) | Out-Null + if($compareResult -eq $true) + { + $ValuesToCheck.Remove($key) | Out-Null + } } } From 940d185e3dbf6973ad5f62588e2dbabbbba75647 Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 22 Oct 2024 11:22:43 -0400 Subject: [PATCH 12/60] SentinelAlertRule - Initial Release --- .../MSFT_SentinelAlertRule.psm1 | 642 ++++++++++++++++++ .../MSFT_SentinelAlertRule.schema.mof | 109 +++ .../MSFT_SentinelAlertRule/readme.md | 6 + .../MSFT_SentinelAlertRule/settings.json | 32 + .../Resources/SentinelAlertRule/1-Create.ps1 | 26 + .../Resources/SentinelAlertRule/2-Update.ps1 | 26 + .../Resources/SentinelAlertRule/3-Remove.ps1 | 26 + ...icrosoft365DSC.SentinelAlertRule.Tests.ps1 | 178 +++++ 8 files changed, 1045 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 new file mode 100644 index 0000000000..947211cb19 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 @@ -0,0 +1,642 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $DisplayName, + + [Parameter(Mandatory = $true)] + [System.String] + $SubscriptionId, + + [Parameter(Mandatory = $true)] + [System.String] + $ResourceGroupName, + + [Parameter(Mandatory = $true)] + [System.String] + $WorkspaceName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [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 + ) + + New-M365DSCConnection -Workload 'Azure' ` + -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 ([System.String]::IsNullOrEmpty($TenantId) -and -not $null -eq $Credential) + { + $TenantId = $Credential.UserName.Split('@')[1] + } + + if (-not [System.String]::IsNullOrEmpty($Id)) + { + $instance = Get-M365DSCSentinelAlertRule -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -WorkspaceName $WorkspaceName ` + -TenantId $TenantId ` + -Id $Id + } + if ($null -eq $instance) + { + $instances = Get-M365DSCSentinelAlertRule -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -WorkspaceName $WorkspaceName ` + -TenantId $TenantId + $instance = $instances | Where-Object -FilterScript {$_.properties.displayName -eq $DisplayName} + } + if ($null -eq $instance) + { + return $nullResult + } + + $results = @{ + ProductFilter = $instance.properties.ProductFilter + Enabled = $instance.properties.Enabled + Severity = $instance.properties.Severity + Tactics = $instance.properties.Tactics + Techniques = $instance.properties.Techniques + Query = $instance.properties.Query + QueryFrequency = $instance.properties.QueryFrequency + QueryPeriod = $instance.properties.QueryPeriod + TriggerOperator = $instance.properties.TriggerOperator + TriggerThreshold = $instance.properties.TriggerThreshold + SuppressionDuration = $instance.properties.SuppressionDuration + SuppresionEnabled = $instance.properties.SuppresionEnabled + AlertRuleTemplateName = $instance.properties.AlertRuleTemplateName + DisplayNamesExcludeFilter = $instance.properties.DisplayNamesExcludeFilter + DisplayNamesFilter = $instance.properties.DisplayNamesFilter + SeveritiesFilter = $instance.properties.SeveritiesFilter + DisplayName = $instance.properties.displayName + SubscriptionId = $SubscriptionId + ResourceGroupName = $ResourceGroupName + WorkspaceName = $WorkspaceName + Id = $instance.name + Description = $instance.properties.description + 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 + ) + + $ConnectionMode = New-M365DSCConnection -Workload 'Azure' ` + -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 + $workspaces = Get-AzResource -ResourceType 'Microsoft.OperationalInsights/workspaces' + $Script:exportedInstances = @() + $i = 1 + $dscContent = '' + if ($Script:exportedInstances.Length -eq 0) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + + if ([System.String]::IsNullOrEmpty($TenantId) -and $null -ne $Credential) + { + $TenantId = $Credential.UserName.Split('@')[1] + } + foreach ($workspace in $workspaces) + { + if ($null -ne $Global:M365DSCExportResourceInstancesCount) + { + $Global:M365DSCExportResourceInstancesCount++ + } + + Write-Host " |---[$i/$($workspaces.Length)] $($workspace.Name)" -NoNewline + $subscriptionId = $workspace.ResourceId.Split('/')[2] + $resourceGroupName = $workspace.ResourceGroupName + $workspaceName = $workspace.Name + + $rules = Get-M365DSCSentinelAlertRule -SubscriptionId $subscriptionId ` + -ResourceGroupName $resourceGroupName ` + -WorkspaceName $workspaceName ` + -TenantId $TenantId + + $j = 1 + if ($currentWatchLists.Length -eq 0 ) + { + Write-Host $Global:M365DSCEmojiGreenCheckMark + } + else + { + Write-Host "`r`n" -NoNewline + } + + foreach ($rule in $rules) + { + $displayedKey = $rule.properties.DisplayName + Write-Host " |---[$j/$($rules.Count)] $displayedKey" -NoNewline + $params = @{ + DisplayName = $indruleicator.properties.displayName + Id = $rule.name + SubscriptionId = $subscriptionId + ResourceGroupName = $resourceGroupName + WorkspaceName = $workspaceName + 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 + $j++ + 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 Get-M365DSCSentinelAlertRule +{ + [CmdletBinding()] + [OutputType([Array])] + param( + [Parameter()] + [System.String] + $SubscriptionId, + + [Parameter()] + [System.String] + $ResourceGroupName, + + [Parameter()] + [System.String] + $WorkspaceName, + + [Parameter(Mandatory = $true)] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $Id + ) + + try + { + $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId + $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" + if (-not [System.String]::IsNullOrEmpty($Id)) + { + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules/$($Id)?api-version=2023-12-01-preview" + $response = Invoke-AzRest -Uri $uri -Method 'GET' + $result = ConvertFrom-Json $response.Content + return $result + } + else + { + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules?api-version=2023-12-01-preview" + $response = Invoke-AzRest -Uri $uri -Method 'GET' + $result = ConvertFrom-Json $response.Content + return $result.value + } + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId + throw $_ + } +} + +function New-M365DSCSentinelThreatIntelligenceIndicator +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $SubscriptionId, + + [Parameter()] + [System.String] + $ResourceGroupName, + + [Parameter()] + [System.String] + $WorkspaceName, + + [Parameter(Mandatory = $true)] + [System.String] + $TenantId, + + [Parameter()] + [System.Collections.Hashtable] + $Body + ) + + try + { + $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId + $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" + + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules/$((New-GUID).ToString())?api-version=2023-12-01-preview" + $payload = ConvertTo-Json $Body -Depth 10 -Compress + $response = Invoke-AzRest -Uri $uri -Method 'PUT' -Payload $payload + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId + throw $_ + } +} + +function Set-M365DSCSentinelThreatIntelligenceIndicator +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $SubscriptionId, + + [Parameter()] + [System.String] + $ResourceGroupName, + + [Parameter()] + [System.String] + $WorkspaceName, + + [Parameter(Mandatory = $true)] + [System.String] + $TenantId, + + [Parameter(Mandatory = $true)] + [System.String] + $Id, + + [Parameter()] + [System.Collections.Hashtable] + $Body + ) + + try + { + $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId + $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" + + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/threatIntelligence/main/indicators/$($Id)?api-version=2024-03-01" + $payload = ConvertTo-Json $Body -Depth 10 -Compress + $response = Invoke-AzRest -Uri $uri -Method 'PUT' -Payload $payload + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId + throw $_ + } +} + +function Remove-M365DSCSentinelThreatIntelligenceIndicator +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $SubscriptionId, + + [Parameter()] + [System.String] + $ResourceGroupName, + + [Parameter()] + [System.String] + $WorkspaceName, + + [Parameter(Mandatory = $true)] + [System.String] + $TenantId, + + [Parameter(Mandatory = $true)] + [System.String] + $Id + ) + + try + { + $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId + $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" + + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/threatIntelligence/main/indicators/$($Id)?api-version=2024-03-01" + $response = Invoke-AzRest -Uri $uri -Method 'DELETE' + } + catch + { + Write-Verbose -Message $_ + New-M365DSCLogEntry -Message 'Error retrieving data:' ` + -Exception $_ ` + -Source $($MyInvocation.MyCommand.Source) ` + -TenantId $TenantId + throw $_ + } +} + +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof new file mode 100644 index 0000000000..30c37ecf58 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof @@ -0,0 +1,109 @@ +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleEventGroupSettings +{ + [Write, Description("")] String aggregationKind; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleCustomDetails +{ + [Write, Description("")] String DetailKey; + [Write, Description("")] String DetailValue; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleEntityMapping +{ + [Write, Description("")] String entityType; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMappingFieldMapping")] String fieldMappings[]; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleEntityMappingFieldMapping +{ + [Write, Description("")] String columnName; + [Write, Description("")] String identifier; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleAlertDetailsOverride +{ + [Write, Description("")] String alertDescriptionFormat; + [Write, Description("")] String alertDisplayNameFormat; + [Write, Description("")] String alertColumnName; + [Write, Description("")] String alertTacticsColumnName; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty")] String alertDynamicProperties; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty +{ + [Write, Description("")] String alertProperty; + [Write, Description("")] String alertPropertyValue; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleIncidentConfiguration +{ + [Write, Description("")] Boolean createIncident; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration")] String groupingConfiguration; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration +{ + [Write, Description("")] Boolean enabled; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail")] groupByAlertDetails[]; + [Write, Description("")] String groupByCustomDetails[]; + [Write, Description("")] String groupByEntities[]; + [Write, Description("")] String lookbackDuration; + [Write, Description("")] String matchingMethod; + [Write, Description("")] Boolean reopenClosedIncident; +}; + +[ClassVersion("1.0.0")] +class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail +{ + [Write, Description("")] String DisplayName; + [Write, Description("")] String Severity; +}; + +[ClassVersion("1.0.0.0"), FriendlyName("ResourceName")] +class MSFT_ResourceName : OMI_BaseResource +{ + [Key, Description("The display name of the indicator")] String DisplayName; + [Write, Description("The name of the resource group. The name is case insensitive.")] String SubscriptionId; + [Write, Description("The name of the resource group. The name is case insensitive.")] String ResourceGroupName; + [Write, Description("The name of the workspace.")] String WorkspaceName; + [Write, Description("The unique id of the indicator.")] String Id; + [Write, Description("The name of the workspace.")] String Description; + [Write, Description("")] String ProductFilter; + [Write, Description("")] Boolean Enabled; + [Write, Description("")] String Severity; + [Write, Description("")] String Tactics[]; + [Write, Description("")] String Techniques[]; + [Write, Description("")] String Query; + [Write, Description("")] String QueryFrequency; + [Write, Description("")] String QueryPeriod; + [Write, Description("")] String TriggerOperator; + [Write, Description("")] UInt32 TriggerThreshold; + [Write, Description("")] String SuppressionDuration; + [Write, Description("")] String SuppresionEnabled; + [Write, Description("")] String AlertRuleTemplateName; + [Write, Description("")] String DisplayNamesExcludeFilter[]; + [Write, Description("")] String DisplayNamesFilter[]; + [Write, Description("")] String SeveritiesFilter[]; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEventGroupSettings")] String EventGroupSettings; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleCustomDetails")] String CustomDetails; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMapping")] String EntityMappings[]; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverride")] String AlertDetailsOverride; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfiguration")] String IncidentConfiguration; + + [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_SentinelAlertRule/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md new file mode 100644 index 0000000000..32e0e7fb27 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/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_SentinelAlertRule/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json new file mode 100644 index 0000000000..edf14b05e4 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/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/SentinelAlertRule/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/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/SentinelAlertRule/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/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/SentinelAlertRule/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 new file mode 100644 index 0000000000..b516274848 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/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.SentinelAlertRule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 new file mode 100644 index 0000000000..780e0f343d --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.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 a6fab102a5b3d9525c94ef255ebbf22b412436dd Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Tue, 22 Oct 2024 23:17:02 +0200 Subject: [PATCH 13/60] Add Intune Group.Read.All permissions to generated permissions --- ResourceGenerator/M365DSCResourceGenerator.psm1 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ResourceGenerator/M365DSCResourceGenerator.psm1 b/ResourceGenerator/M365DSCResourceGenerator.psm1 index 932f7012a2..9cd5544639 100644 --- a/ResourceGenerator/M365DSCResourceGenerator.psm1 +++ b/ResourceGenerator/M365DSCResourceGenerator.psm1 @@ -967,7 +967,15 @@ class MSFT_DeviceManagementConfigurationPolicyAssignments -Workload $Workload ` -CmdLetNoun $CmdLetNoun ` -ApiVersion $ApiVersion ` - -UpdateVerb $updateVerb).permissions | ConvertTo-Json -Depth 20 + -UpdateVerb $updateVerb).permissions + if ($ResourceName -like "Intune*") + { + $resourcePermissions.application.read += @{ name = 'Group.Read.All' } + $resourcePermissions.application.update += @{ name = 'Group.Read.All' } + $resourcePermissions.delegated.read += @{ name = 'Group.Read.All' } + $resourcePermissions.delegated.update += @{ name = 'Group.Read.All' } + } + $resourcePermissions = $resourcePermissions | ConvertTo-Json -Depth 20 $resourcePermissions = ' ' + $resourcePermissions Write-TokenReplacement -Token '' -Value $ResourceName -FilePath $settingsFilePath Write-TokenReplacement -Token '' -Value $resourceDescription -FilePath $settingsFilePath From 47b1372de3a6ed932ff0f0598cca27cc5dbeec8b Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 22 Oct 2024 23:01:40 -0400 Subject: [PATCH 14/60] Ready for Review --- CHANGELOG.md | 2 + .../MSFT_SentinelAlertRule.psm1 | 889 ++++++++++++++++-- .../MSFT_SentinelAlertRule.schema.mof | 18 +- .../MSFT_SentinelAlertRule/readme.md | 4 +- .../MSFT_SentinelAlertRule/settings.json | 24 +- .../Dependencies/Manifest.psd1 | 42 +- .../Resources/SentinelAlertRule/1-Create.ps1 | 51 +- .../Resources/SentinelAlertRule/2-Update.ps1 | 51 +- .../Resources/SentinelAlertRule/3-Remove.ps1 | 14 +- ...icrosoft365DSC.SentinelAlertRule.Tests.ps1 | 347 ++++++- 10 files changed, 1286 insertions(+), 156 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c94a634efc..da4bf910c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ * Fixed missing permissions in settings.json * Intune workload * Fixed missing permissions in settings.json +* SentinelAlertRule + * Initial release. * SentinelThreatIntelligenceIndicator * Initial release. * SPOTenantSettings diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 index 947211cb19..77911ab204 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.psm1 @@ -28,6 +28,98 @@ function Get-TargetResource [System.String] $Description, + [Parameter()] + [System.String] + $ProductFilter, + + [Parameter()] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.String] + $Severity, + + [Parameter()] + [System.String[]] + $Tactics, + + [Parameter()] + [System.String[]] + $Techniques, + + [Parameter()] + [System.String[]] + $SubTechniques, + + [Parameter()] + [System.String] + $Query, + + [Parameter()] + [System.String] + $QueryFrequency, + + [Parameter()] + [System.String] + $QueryPeriod, + + [Parameter()] + [System.String] + $TriggerOperator, + + [Parameter()] + [System.UInt32] + $TriggerThreshold, + + [Parameter()] + [System.String] + $SuppressionDuration, + + [Parameter()] + [System.String] + $SuppressionEnabled, + + [Parameter()] + [System.String] + $AlertRuleTemplateName, + + [Parameter()] + [System.String[]] + $DisplayNamesExcludeFilter, + + [Parameter()] + [System.String[]] + $DisplayNamesFilter, + + [Parameter()] + [System.String[]] + $SeveritiesFilter, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $EventGroupingSettings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $CustomDetails, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $EntityMappings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $AlertDetailsOverride, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $IncidentConfiguration, + + [Parameter()] + [System.String] + $Kind, + [Parameter()] [ValidateSet('Present', 'Absent')] [System.String] @@ -103,28 +195,119 @@ function Get-TargetResource return $nullResult } + # EventGroupingSettings + $EventGroupingValueSettingsValue = $null + if ($null -ne $instance.properties.eventGroupingSettings) + { + $EventGroupingValueSettingsValue = @{ + aggregationKind = $instance.properties.eventGroupingSettings.aggregationKind + } + } + + # CustomDetails + $CustomDetailsValue = @() + if ($null -ne $instance.properties.customDetails) + { + $detailAsHash = @{} + $instance.properties.customDetails.psobject.properties | foreach { $detailAsHash[$_.Name] = $_.Value } + foreach ($key in $detailAsHash.Keys) + { + $CustomDetailsValue += @{ + DetailKey = $key + DetailValue = $detailAsHash.$key + } + } + } + + #EntityMappings + $EntityMappingsValue = @() + if ($null -ne $instance.properties.entityMappings) + { + foreach ($mapping in $instance.properties.entityMappings) + { + $entity = @{ + entityType = $mapping.entityType + fieldMappings = @() + } + + foreach ($fieldMapping in $mapping.fieldMappings) + { + $entity.fieldMappings += @{ + identifier = $fieldMapping.identifier + columnName = $fieldMapping.columnName + } + } + + $EntityMappingsValue += $entity + } + } + + #AlertDetailsOverride + if ($null -ne $instance.properties.alertDetailsOverride) + { + $info = $instance.properties.alertDetailsOverride + $AlertDetailsOverrideValue = @{ + alertDisplayNameFormat = $info.alertDisplayNameFormat + alertDescriptionFormat = $info.alertDescriptionFormat + alertDynamicProperties = @() + } + + foreach ($propertyEntry in $info.alertDynamicProperties) + { + $AlertDetailsOverrideValue.alertDynamicProperties += @{ + alertProperty = $propertyEntry.alertProperty + alertPropertyValue = $propertyEntry.value + } + } + } + + #IncidentConfiguration + if ($null -ne $instance.properties.incidentConfiguration) + { + $info = $instance.properties.incidentConfiguration + $IncidentConfigurationValue = @{ + createIncident = [Boolean]::Parse($info.createIncident.ToString()) + groupingConfiguration = @{ + enabled = $info.groupingConfiguration.enabled + reopenClosedIncident = $info.groupingConfiguration.reopenClosedIncident + lookbackDuration = $info.groupingConfiguration.lookbackDuration + matchingMethod = $info.groupingConfiguration.matchingMethod + groupByEntities = $info.groupingConfiguration.groupByEntities + groupByAlertDetails = $info.groupingConfiguration.groupByAlertDetails + groupByCustomDetails = $info.groupingConfiguration.groupByCustomDetails + } + } + } + $results = @{ ProductFilter = $instance.properties.ProductFilter Enabled = $instance.properties.Enabled Severity = $instance.properties.Severity Tactics = $instance.properties.Tactics Techniques = $instance.properties.Techniques + SubTechniques = $instance.properties.SubTechniques Query = $instance.properties.Query QueryFrequency = $instance.properties.QueryFrequency QueryPeriod = $instance.properties.QueryPeriod TriggerOperator = $instance.properties.TriggerOperator TriggerThreshold = $instance.properties.TriggerThreshold SuppressionDuration = $instance.properties.SuppressionDuration - SuppresionEnabled = $instance.properties.SuppresionEnabled + SuppressionEnabled = $instance.properties.SuppressionEnabled AlertRuleTemplateName = $instance.properties.AlertRuleTemplateName DisplayNamesExcludeFilter = $instance.properties.DisplayNamesExcludeFilter DisplayNamesFilter = $instance.properties.DisplayNamesFilter SeveritiesFilter = $instance.properties.SeveritiesFilter DisplayName = $instance.properties.displayName + EventGroupingSettings = $EventGroupingValueSettingsValue + CustomDetails = $CustomDetailsValue + EntityMappings = $EntityMappingsValue + AlertDetailsOverride = $AlertDetailsOverrideValue + IncidentConfiguration = $IncidentConfigurationValue SubscriptionId = $SubscriptionId ResourceGroupName = $ResourceGroupName WorkspaceName = $WorkspaceName Id = $instance.name + Kind = $instance.kind Description = $instance.properties.description Ensure = 'Present' Credential = $Credential @@ -154,12 +337,121 @@ function Set-TargetResource [CmdletBinding()] param ( - ##TODO - Replace the PrimaryKey [Parameter(Mandatory = $true)] [System.String] - $PrimaryKey, + $DisplayName, + + [Parameter(Mandatory = $true)] + [System.String] + $SubscriptionId, + + [Parameter(Mandatory = $true)] + [System.String] + $ResourceGroupName, + + [Parameter(Mandatory = $true)] + [System.String] + $WorkspaceName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $ProductFilter, + + [Parameter()] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.String] + $Severity, + + [Parameter()] + [System.String[]] + $Tactics, + + [Parameter()] + [System.String[]] + $Techniques, + + [Parameter()] + [System.String[]] + $SubTechniques, + + [Parameter()] + [System.String] + $Query, - ##TODO - Add the list of Parameters + [Parameter()] + [System.String] + $QueryFrequency, + + [Parameter()] + [System.String] + $QueryPeriod, + + [Parameter()] + [System.String] + $TriggerOperator, + + [Parameter()] + [System.UInt32] + $TriggerThreshold, + + [Parameter()] + [System.String] + $SuppressionDuration, + + [Parameter()] + [System.String] + $SuppressionEnabled, + + [Parameter()] + [System.String] + $AlertRuleTemplateName, + + [Parameter()] + [System.String[]] + $DisplayNamesExcludeFilter, + + [Parameter()] + [System.String[]] + $DisplayNamesFilter, + + [Parameter()] + [System.String[]] + $SeveritiesFilter, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $EventGroupingSettings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $CustomDetails, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $EntityMappings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $AlertDetailsOverride, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $IncidentConfiguration, + + [Parameter()] + [System.String] + $Kind, [Parameter()] [ValidateSet('Present', 'Absent')] @@ -205,25 +497,222 @@ function Set-TargetResource $currentInstance = Get-TargetResource @PSBoundParameters - $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + if ([System.String]::IsNullOrEmpty($TenantId) -and -not $null -eq $Credential) + { + $TenantId = $Credential.UserName.Split('@')[1] + } + + $instance = @{} + if ($Kind -eq 'Fusion') + { + $instance = @{ + kind = $Kind + properties = @{ + alertRuleTemplateName = $AlertRuleTemplateName + enabled = $Enabled + } + } + } + elseif ($Kind -eq 'MicrosoftSecurityIncidentCreation') + { + $instance = @{ + kind = $Kind + properties = @{ + displayName = $DisplayName + description = $Description + productFilter = $ProductFilter + displayNamesExcludeFilter = $DisplayNamesExcludeFilter + displayNamesFilter = $DisplayNamesFilter + enabled = $Enabled + severitiesFilter = $AlertSeverity + } + } + } + elseif ($Kind -eq 'Scheduled') + { + $instance = @{ + kind = $Kind + properties = @{ + displayName = $DisplayName + enabled = $Enabled + description = $Description + query = $Query + queryFrequency = $QueryFrequency + queryPeriod = $QueryPeriod + severity = $Severity + suppressionDuration = $SuppressionDuration + suppressionEnabled = $SuppressionEnabled + triggerOperator = $TriggerOperator + triggerThreshold = $TriggerThreshold + eventGroupingSettings = @{ + aggregationKind = $EventGroupingSettings.aggregationKind + } + customDetails = @{} + alertDetailsOverride = @{ + alertDisplayNameFormat = $AlertDetailsOverride.alertDisplayNameFormat + alertDescriptionFormat = $AlertDetailsOverride.alertDescriptionFormat + alertDynamicProperties = @() + } + entityMappings = @() + incidentConfiguration = @{ + createIncident = $IncidentConfiguration.createIncident + groupingConfiguration = @{ + enabled = $IncidentConfiguration.groupingConfiguration.enabled + reopenClosedIncident = $IncidentConfiguration.groupingConfiguration.reopenClosedIncident + lookbackDuration = $IncidentConfiguration.groupingConfiguration.lookbackDuration + matchingMethod = $IncidentConfiguration.groupingConfiguration.matchingMethod + groupByEntities = $IncidentConfiguration.groupingConfiguration.groupByEntities + groupByAlertDetails = $IncidentConfiguration.groupingConfiguration.groupByAlertDetails + groupByCustomDetails = $IncidentConfiguration.groupingConfiguration.groupByCustomDetails + } + } + productFilter = $ProductFilter + displayNamesExcludeFilter = $DisplayNamesExcludeFilter + displayNamesFilter = $DisplayNamesFilter + severitiesFilter = $AlertSeverity + } + } + + foreach ($entity in $EntityMappings) + { + $entry = @{ + entityType = $entity.entityType + fieldMappings = @() + } + + foreach ($field in $entity.fieldMappings) + { + $entry.fieldMappings += @{ + identifier = $field.identifier + columnName = $field.columnName + } + } + + $instance.properties.entityMappings += $entry + } + + foreach ($detail in $CustomDetails) + { + $instance.properties.customDetails.Add($detail.DetailKey, $detail.DetailValue) + } + + foreach ($dynamicProp in $AlertDetailsOverride.alertDynamicProperties) + { + $instance.properties.alertDetailsOverride.alertDynamicProperties += @{ + alertProperty = $dynamicProp.alertProperty + value = $dynamicProp.alertPropertyValue + } + } + } + elseif ($Kind -eq 'NRT') + { + $instance = @{ + kind = $Kind + properties = @{ + displayName = $DisplayName + enabled = $Enabled + description = $Description + query = $Query + severity = $Severity + suppressionDuration = $SuppressionDuration + suppressionEnabled = $SuppressionEnabled + eventGroupingSettings = @{ + aggregationKind = $EventGroupingSettings.aggregationKind + } + alertDetailsOverride = @{ + alertDisplayNameFormat = $AlertDetailsOverride.alertDisplayNameFormat + alertDescriptionFormat = $AlertDetailsOverride.alertDescriptionFormat + alertDynamicProperties = @() + } + entityMappings = @() + customDetails = @{} + incidentConfiguration = @{ + createIncident = $IncidentConfiguration.createIncident + groupingConfiguration = @{ + enabled = $IncidentConfiguration.groupingConfiguration.enabled + reopenClosedIncident = $IncidentConfiguration.groupingConfiguration.reopenClosedIncident + lookbackDuration = $IncidentConfiguration.groupingConfiguration.lookbackDuration + matchingMethod = $IncidentConfiguration.groupingConfiguration.matchingMethod + groupByEntities = $IncidentConfiguration.groupingConfiguration.groupByEntities + groupByAlertDetails = $IncidentConfiguration.groupingConfiguration.groupByAlertDetails + groupByCustomDetails = $IncidentConfiguration.groupingConfiguration.groupByCustomDetails + } + } + techniques = $Techniques + subTechniques = $SubTechniques + tactics = $Tactics + } + } + + if ($null -eq $EntityMappings -or $EntityMappings.Length -eq 0) + { + $instance.properties.Remove('entityMappings') | Out-Null + } + else + { + foreach ($entity in $EntityMappings) + { + $entry = @{ + entityType = $entity.entityType + fieldMappings = @() + } + + foreach ($field in $entity.fieldMappings) + { + $entry.fieldMappings += @{ + identifier = $field.identifier + columnName = $field.columnName + } + } + + $instance.properties.entityMappings += $entry + } + } + + foreach ($detail in $CustomDetails) + { + $instance.properties.customDetails.Add($detail.DetailKey, $detail.DetailValue) + } + + foreach ($dynamicProp in $AlertDetailsOverride.alertDynamicProperties) + { + $instance.properties.alertDetailsOverride.alertDynamicProperties += @{ + alertProperty = $dynamicProp.alertProperty + value = $dynamicProp.alertPropertyValue + } + } + } # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { - ##TODO - Replace by the New cmdlet for the resource - New-Cmdlet @SetParameters + Write-Verbose -Message "Creating new Alert Rule {$DisplayName}" + New-M365DSCSentinelAlertRule -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -WorkspaceName $WorkspaceName ` + -TenantId $TenantId ` + -Body $instance } # UPDATE - elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + elseif($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') { - ##TODO - Replace by the Update/Set cmdlet for the resource - Set-cmdlet @SetParameters + Write-Verbose -Message "Updating Alert Rule {$DisplayName}" + New-M365DSCSentinelAlertRule -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -WorkspaceName $WorkspaceName ` + -TenantId $TenantId ` + -Body $instance ` + -Id $currentInstance.Id } # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') { - ##TODO - Replace by the Remove cmdlet for the resource - Remove-cmdlet @SetParameters + Write-Verbose -Message "Removing Alert Rule {$DisplayName}" + Remove-M365DSCSentinelAlertRule -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -WorkspaceName $WorkspaceName ` + -TenantId $TenantId ` + -Id $currentInstance.Id } } @@ -233,12 +722,121 @@ function Test-TargetResource [OutputType([System.Boolean])] param ( - ##TODO - Replace the PrimaryKey [Parameter(Mandatory = $true)] [System.String] - $PrimaryKey, + $DisplayName, + + [Parameter(Mandatory = $true)] + [System.String] + $SubscriptionId, + + [Parameter(Mandatory = $true)] + [System.String] + $ResourceGroupName, + + [Parameter(Mandatory = $true)] + [System.String] + $WorkspaceName, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $ProductFilter, + + [Parameter()] + [System.Boolean] + $Enabled, + + [Parameter()] + [System.String] + $Severity, + + [Parameter()] + [System.String[]] + $Tactics, + + [Parameter()] + [System.String[]] + $Techniques, + + [Parameter()] + [System.String[]] + $SubTechniques, + + [Parameter()] + [System.String] + $Query, + + [Parameter()] + [System.String] + $QueryFrequency, + + [Parameter()] + [System.String] + $QueryPeriod, + + [Parameter()] + [System.String] + $TriggerOperator, + + [Parameter()] + [System.UInt32] + $TriggerThreshold, + + [Parameter()] + [System.String] + $SuppressionDuration, + + [Parameter()] + [System.String] + $SuppressionEnabled, + + [Parameter()] + [System.String] + $AlertRuleTemplateName, - ##TODO - Add the list of Parameters + [Parameter()] + [System.String[]] + $DisplayNamesExcludeFilter, + + [Parameter()] + [System.String[]] + $DisplayNamesFilter, + + [Parameter()] + [System.String[]] + $SeveritiesFilter, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $EventGroupingSettings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $CustomDetails, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $EntityMappings, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $AlertDetailsOverride, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance] + $IncidentConfiguration, + + [Parameter()] + [System.String] + $Kind, [Parameter()] [ValidateSet('Present', 'Absent')] @@ -288,10 +886,33 @@ function Test-TargetResource 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 + #Compare Cim instances + foreach ($key in $PSBoundParameters.Keys) + { + $source = $PSBoundParameters.$key + $target = $CurrentValues.$key + if ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') + { + $testResult = Compare-M365DSCComplexObject ` + -Source ($source) ` + -Target ($target) + + if (-not $testResult) + { + break + } + + $ValuesToCheck.Remove($key) | Out-Null + } + } + + if ($testResult) + { + $testResult = Test-M365DSCParameterState -CurrentValues $CurrentValues ` + -Source $($MyInvocation.MyCommand.Source) ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck $ValuesToCheck.Keys + } Write-Verbose -Message "Test-TargetResource returned $testResult" @@ -400,7 +1021,7 @@ function Export-TargetResource $displayedKey = $rule.properties.DisplayName Write-Host " |---[$j/$($rules.Count)] $displayedKey" -NoNewline $params = @{ - DisplayName = $indruleicator.properties.displayName + DisplayName = $rule.properties.displayName Id = $rule.name SubscriptionId = $subscriptionId ResourceGroupName = $resourceGroupName @@ -417,11 +1038,173 @@ function Export-TargetResource $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` -Results $Results + if ( $null -ne $Results.EventGroupingSettings) + { + $complexMapping = @( + @{ + Name = 'EventGroupingSettings' + CimInstanceName = 'SentinelAlertRuleEventGroupingSettings' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.EventGroupingSettings ` + -CIMInstanceName 'SentinelAlertRuleEventGroupingSettings' ` + -ComplexTypeMapping $complexMapping + + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.EventGroupingSettings = $complexTypeStringResult + } + else + { + $Results.Remove('EventGroupingSettings') | Out-Null + } + } + + if ($null -ne $Results.CustomDetails) + { + $complexMapping = @( + @{ + Name = 'CustomDetails' + CimInstanceName = 'SentinelAlertRuleCustomDetails' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.CustomDetails ` + -CIMInstanceName 'SentinelAlertRuleCustomDetails' ` + -ComplexTypeMapping $complexMapping + + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.CustomDetails = $complexTypeStringResult + } + else + { + $Results.Remove('CustomDetails') | Out-Null + } + } + + if ( $null -ne $Results.EntityMappings) + { + $complexMapping = @( + @{ + Name = 'EntityMappings' + CimInstanceName = 'SentinelAlertRuleEntityMapping' + IsRequired = $False + }, + @{ + Name = 'fieldMappings' + CimInstanceName = 'SentinelAlertRuleEntityMappingFieldMapping' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.EntityMappings ` + -CIMInstanceName 'SentinelAlertRuleEntityMapping' ` + -ComplexTypeMapping $complexMapping + + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.EntityMappings = $complexTypeStringResult + } + else + { + $Results.Remove('EntityMappings') | Out-Null + } + } + + if ($null -ne $Results.AlertDetailsOverride) + { + $complexMapping = @( + @{ + Name = 'AlertDetailsOverride' + CimInstanceName = 'SentinelAlertRuleAlertDetailsOverride' + IsRequired = $False + }, + @{ + Name = 'alertDynamicProperties' + CimInstanceName = 'SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.AlertDetailsOverride ` + -CIMInstanceName 'SentinelAlertRuleAlertDetailsOverride' ` + -ComplexTypeMapping $complexMapping + + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.AlertDetailsOverride = $complexTypeStringResult + } + else + { + $Results.Remove('AlertDetailsOverride') | Out-Null + } + } + + if ($null -ne $Results.IncidentConfiguration) + { + $complexMapping = @( + @{ + Name = 'IncidentConfiguration' + CimInstanceName = 'SentinelAlertRuleIncidentConfiguration' + IsRequired = $False + }, + @{ + Name = 'groupingConfiguration' + CimInstanceName = 'SentinelAlertRuleIncidentConfigurationGroupingConfiguration' + IsRequired = $False + } + @{ + Name = 'groupByAlertDetails' + CimInstanceName = 'SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail' + IsRequired = $False + } + ) + $complexTypeStringResult = Get-M365DSCDRGComplexTypeToString ` + -ComplexObject $Results.IncidentConfiguration ` + -CIMInstanceName 'SentinelAlertRuleIncidentConfiguration' ` + -ComplexTypeMapping $complexMapping + + if (-Not [String]::IsNullOrWhiteSpace($complexTypeStringResult)) + { + $Results.IncidentConfiguration = $complexTypeStringResult + } + else + { + $Results.Remove('IncidentConfiguration') | Out-Null + } + } + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` -ConnectionMode $ConnectionMode ` -ModulePath $PSScriptRoot ` -Results $Results ` -Credential $Credential + + if ($Results.EventGroupingSettings) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'EventGroupingSettings' -IsCIMArray:$False + } + if ($Results.CustomDetails) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'CustomDetails' -IsCIMArray:$False + } + if ($Results.EntityMappings) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'EntityMappings' -IsCIMArray:$True + } + if ($Results.AlertDetailsOverride) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'AlertDetailsOverride' -IsCIMArray:$True + } + if ($Results.IncidentConfiguration) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock -ParameterName 'IncidentConfiguration' -IsCIMArray:$True + } + $dscContent += $currentDSCBlock Save-M365DSCPartialExport -Content $currentDSCBlock ` -FileName $Global:PartialExportFileName @@ -501,7 +1284,7 @@ function Get-M365DSCSentinelAlertRule } } -function New-M365DSCSentinelThreatIntelligenceIndicator +function New-M365DSCSentinelAlertRule { [CmdletBinding()] param( @@ -523,56 +1306,11 @@ function New-M365DSCSentinelThreatIntelligenceIndicator [Parameter()] [System.Collections.Hashtable] - $Body - ) + $Body, - try - { - $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId - $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" - - $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules/$((New-GUID).ToString())?api-version=2023-12-01-preview" - $payload = ConvertTo-Json $Body -Depth 10 -Compress - $response = Invoke-AzRest -Uri $uri -Method 'PUT' -Payload $payload - } - catch - { - Write-Verbose -Message $_ - New-M365DSCLogEntry -Message 'Error retrieving data:' ` - -Exception $_ ` - -Source $($MyInvocation.MyCommand.Source) ` - -TenantId $TenantId - throw $_ - } -} - -function Set-M365DSCSentinelThreatIntelligenceIndicator -{ - [CmdletBinding()] - param( [Parameter()] [System.String] - $SubscriptionId, - - [Parameter()] - [System.String] - $ResourceGroupName, - - [Parameter()] - [System.String] - $WorkspaceName, - - [Parameter(Mandatory = $true)] - [System.String] - $TenantId, - - [Parameter(Mandatory = $true)] - [System.String] - $Id, - - [Parameter()] - [System.Collections.Hashtable] - $Body + $Id ) try @@ -580,9 +1318,18 @@ function Set-M365DSCSentinelThreatIntelligenceIndicator $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" - $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/threatIntelligence/main/indicators/$($Id)?api-version=2024-03-01" + if ($null -eq $Id) + { + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules/$((New-GUID).ToString())?api-version=2024-04-01-preview" + } + else + { + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertrules/$($Id)?api-version=2024-04-01-preview" + } $payload = ConvertTo-Json $Body -Depth 10 -Compress + Write-Verbose -Message "Creating new rule against URL:`r`n$($uri)`r`nWith payload:`r`n$payload" $response = Invoke-AzRest -Uri $uri -Method 'PUT' -Payload $payload + Write-Verbose -Message $response.Content } catch { @@ -595,7 +1342,7 @@ function Set-M365DSCSentinelThreatIntelligenceIndicator } } -function Remove-M365DSCSentinelThreatIntelligenceIndicator +function Remove-M365DSCSentinelAlertRule { [CmdletBinding()] param( @@ -625,7 +1372,7 @@ function Remove-M365DSCSentinelThreatIntelligenceIndicator $hostUrl = Get-M365DSCAPIEndpoint -TenantId $TenantId $uri = $hostUrl.AzureManagement + "/subscriptions/$($SubscriptionId)/resourceGroups/$($ResourceGroupName)/" - $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/threatIntelligence/main/indicators/$($Id)?api-version=2024-03-01" + $uri += "providers/Microsoft.OperationalInsights/workspaces/$($WorkspaceName)/providers/Microsoft.SecurityInsights/alertRules/$($Id)?api-version=2024-04-01-preview" $response = Invoke-AzRest -Uri $uri -Method 'DELETE' } catch diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof index 30c37ecf58..3762c576f8 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof @@ -1,5 +1,5 @@ [ClassVersion("1.0.0")] -class MSFT_SentinelAlertRuleEventGroupSettings +class MSFT_SentinelAlertRuleEventGroupingSettings { [Write, Description("")] String aggregationKind; }; @@ -32,7 +32,7 @@ class MSFT_SentinelAlertRuleAlertDetailsOverride [Write, Description("")] String alertDisplayNameFormat; [Write, Description("")] String alertColumnName; [Write, Description("")] String alertTacticsColumnName; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty")] String alertDynamicProperties; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty")] String alertDynamicProperties[]; }; [ClassVersion("1.0.0")] @@ -53,7 +53,7 @@ class MSFT_SentinelAlertRuleIncidentConfiguration class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration { [Write, Description("")] Boolean enabled; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail")] groupByAlertDetails[]; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail")] String groupByAlertDetails[]; [Write, Description("")] String groupByCustomDetails[]; [Write, Description("")] String groupByEntities[]; [Write, Description("")] String lookbackDuration; @@ -68,8 +68,8 @@ class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetai [Write, Description("")] String Severity; }; -[ClassVersion("1.0.0.0"), FriendlyName("ResourceName")] -class MSFT_ResourceName : OMI_BaseResource +[ClassVersion("1.0.0.0"), FriendlyName("SentinelAlertRule")] +class MSFT_SentinelAlertRule : OMI_BaseResource { [Key, Description("The display name of the indicator")] String DisplayName; [Write, Description("The name of the resource group. The name is case insensitive.")] String SubscriptionId; @@ -82,22 +82,24 @@ class MSFT_ResourceName : OMI_BaseResource [Write, Description("")] String Severity; [Write, Description("")] String Tactics[]; [Write, Description("")] String Techniques[]; + [Write, Description("")] String SubTechniques[]; [Write, Description("")] String Query; [Write, Description("")] String QueryFrequency; [Write, Description("")] String QueryPeriod; [Write, Description("")] String TriggerOperator; [Write, Description("")] UInt32 TriggerThreshold; [Write, Description("")] String SuppressionDuration; - [Write, Description("")] String SuppresionEnabled; + [Write, Description("")] String SuppressionEnabled; [Write, Description("")] String AlertRuleTemplateName; [Write, Description("")] String DisplayNamesExcludeFilter[]; [Write, Description("")] String DisplayNamesFilter[]; [Write, Description("")] String SeveritiesFilter[]; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEventGroupSettings")] String EventGroupSettings; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleCustomDetails")] String CustomDetails; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEventGroupingSettings")] String EventGroupingSettings; + [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleCustomDetails")] String CustomDetails[]; [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMapping")] String EntityMappings[]; [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverride")] String AlertDetailsOverride; [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfiguration")] String IncidentConfiguration; + [Write, Description("")] String Kind; [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; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md index 32e0e7fb27..2bdf6ede04 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/readme.md @@ -1,6 +1,6 @@ -# ResourceName +# SentinelAlertRule ## Description -##TODO - Provide a short description of what the resource is set to configure. +Configures alert rules in Azure Sentinel. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json index edf14b05e4..d01973dfcf 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/settings.json @@ -1,13 +1,9 @@ { - "resourceName": "ResourceName", - "description": "Description of what the resource is about.", + "resourceName": "SentinelAlertRule", + "description": "Configures alert rules in Azure Sentinel.", "roles": { - "read": [ - "Role" - ], - "update": [ - "Role" - ] + "read": [], + "update": [] }, "permissions": { "graph": { @@ -16,16 +12,8 @@ "update": [] }, "application": { - "read": [ - { - "name": "Permission for Monitoring and Export" - } - ], - "update": [ - { - "name": "Permission for deploying" - } - ] + "read": [], + "update": [] } } } diff --git a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 index c9f78cc37b..bfb5342628 100644 --- a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 +++ b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 @@ -30,87 +30,87 @@ }, @{ ModuleName = 'Microsoft.Graph.Applications' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Applications' - Requiredversion = '2.23.0' + Requiredversion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Authentication' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Devices.CorporateManagement' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement.Administration' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement.Enrollment' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.DirectoryManagement' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.Governance' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.SignIns' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Reports' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Search' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Teams' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.DeviceManagement.Administration' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.DirectoryObjects' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Groups' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Groups' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Planner' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Sites' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Users' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Users.Actions' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.PowerApps.Administration.PowerShell' diff --git a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 index b516274848..48bcd14ca7 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/1-Create.ps1 @@ -21,6 +21,55 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + SentinelAlertRule "SentinelAlertRule-MyNRTRule" + { + AlertDetailsOverride = MSFT_SentinelAlertRuleAlertDetailsOverride{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + }; + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + CustomDetails = @( + MSFT_SentinelAlertRuleCustomDetails{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } + ); + Description = "Test"; + DisplayName = "MyNRTRule"; + Enabled = $True; + Ensure = "Present"; + EntityMappings = @( + MSFT_SentinelAlertRuleEntityMapping{ + fieldMappings = @( + MSFT_SentinelAlertRuleEntityMappingFieldMapping{ + identifier = 'AppId' + columnName = 'Id' + } + ) + entityType = 'CloudApplication' + } + ); + IncidentConfiguration = MSFT_SentinelAlertRuleIncidentConfiguration{ + groupingConfiguration = MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + }; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "ResourceGroupName"; + Severity = "Medium"; + SubscriptionId = "xxxx"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + TenantId = $TenantId; + WorkspaceName = "SentinelWorkspace"; + } } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 index b516274848..f2ce0ff25e 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/2-Update.ps1 @@ -21,6 +21,55 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + SentinelAlertRule "SentinelAlertRule-MyNRTRule" + { + AlertDetailsOverride = MSFT_SentinelAlertRuleAlertDetailsOverride{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + }; + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + CustomDetails = @( + MSFT_SentinelAlertRuleCustomDetails{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } + ); + Description = "Test"; + DisplayName = "MyNRTRule"; + Enabled = $True; + Ensure = "Present"; + EntityMappings = @( + MSFT_SentinelAlertRuleEntityMapping{ + fieldMappings = @( + MSFT_SentinelAlertRuleEntityMappingFieldMapping{ + identifier = 'AppId' + columnName = 'Id' + } + ) + entityType = 'CloudApplication' + } + ); + IncidentConfiguration = MSFT_SentinelAlertRuleIncidentConfiguration{ + groupingConfiguration = MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + }; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "ResourceGroupName"; + Severity = "High"; #Drift + SubscriptionId = "xxxx"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + TenantId = $TenantId; + WorkspaceName = "SentinelWorkspace"; + } } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 index b516274848..9d40a7633d 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/SentinelAlertRule/3-Remove.ps1 @@ -21,6 +21,18 @@ Configuration Example Import-DscResource -ModuleName Microsoft365DSC node localhost { - + SentinelAlertRule "SentinelAlertRule-MyNRTRule" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "Test"; + DisplayName = "MyNRTRule"; + Ensure = "Absent"; + ResourceGroupName = "ResourceGroupName"; + Severity = "Medium"; + SubscriptionId = "xxxx"; + TenantId = $TenantId; + WorkspaceName = "SentinelWorkspace"; + } } } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 index 780e0f343d..90786e49f9 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.SentinelAlertRule.Tests.ps1 @@ -35,7 +35,21 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { return "Credentials" } - ##TODO - Mock any Remove/Set/New cmdlets + Mock -CommandName Get-AzResource -MockWith { + return @{ + ResourceGroupName = "MyResourceGroup" + Name = 'MySentinelWorkspace' + ResourceId = "name/part/resourceId/" + } + } + + Mock -CommandName New-M365DSCSentinelAlertRule -MockWith { + + } + + Mock -CommandName Remove-M365DSCSentinelAlertRule -MockWith { + + } # Mock Write-Host to hide output during the tests Mock -CommandName Write-Host -MockWith { @@ -47,13 +61,47 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "The instance should exist but it DOES NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + AlertDetailsOverride = (New-CimInstance -ClassName MSFT_SentinelAlertRuleAlertDetailsOverride -Property @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } -ClientOnly) + CustomDetails = @( + (New-CimInstance -ClassName MSFT_SentinelAlertRuleCustomDetails -Property @{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } -ClientOnly) + ) + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + Ensure = "Present"; + EventGroupingSettings = (New-CimInstance -ClassName MSFT_SentinelAlertRuleEventGroupingSettings -Property @{ + aggregationKind = 'AlertPerResult' + } -ClientOnly) + IncidentConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfiguration -Property @{ + groupingConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration -Property @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } -ClientOnly) + createIncident = $True + } -ClientOnly) + Kind = "NRT"; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "TBDSentinel"; + Severity = "Medium"; + SubscriptionId = "42136a41-5030-4140-aba0-7e6211115d3a"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + WorkspaceName = "SentinelWorkspace"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return $null - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-M365DSCSentinelAlertRule -MockWith { return $null } } @@ -65,24 +113,91 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } 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 + Should -Invoke -CommandName New-M365DSCSentinelAlertRule -Exactly 1 } } Context -Name "The instance exists but it SHOULD NOT" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Absent' - Credential = $Credential; + AlertDetailsOverride = (New-CimInstance -ClassName MSFT_SentinelAlertRuleAlertDetailsOverride -Property @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } -ClientOnly) + CustomDetails = @( + (New-CimInstance -ClassName MSFT_SentinelAlertRuleCustomDetails -Property @{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } -ClientOnly) + ) + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + Ensure = "Absent"; + EventGroupingSettings = (New-CimInstance -ClassName MSFT_SentinelAlertRuleEventGroupingSettings -Property @{ + aggregationKind = 'AlertPerResult' + } -ClientOnly) + IncidentConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfiguration -Property @{ + groupingConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration -Property @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } -ClientOnly) + createIncident = $True + } -ClientOnly) + Kind = "NRT"; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "TBDSentinel"; + Severity = "Medium"; + SubscriptionId = "42136a41-5030-4140-aba0-7e6211115d3a"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + WorkspaceName = "SentinelWorkspace"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return an instance - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-M365DSCSentinelAlertRule -MockWith { return @{ - + Kind = 'NRT' + name = '12345-12345-12345-12345-12345' + properties = @{ + Query = "ThreatIntelIndicators"; + Severity = "Medium"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + AlertDetailsOverride = @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } + CustomDetails = @( + @{ + Color = 'TenantId' + } + ) + EventGroupingSettings = @{ + aggregationKind = 'AlertPerResult' + } + IncidentConfiguration = @{ + groupingConfiguration = @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + } + } } } } @@ -95,23 +210,89 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { 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 + Should -Invoke -CommandName Remove-M365DSCSentinelAlertRule -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; + AlertDetailsOverride = (New-CimInstance -ClassName MSFT_SentinelAlertRuleAlertDetailsOverride -Property @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } -ClientOnly) + CustomDetails = @( + (New-CimInstance -ClassName MSFT_SentinelAlertRuleCustomDetails -Property @{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } -ClientOnly) + ) + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + Ensure = "Present"; + EventGroupingSettings = (New-CimInstance -ClassName MSFT_SentinelAlertRuleEventGroupingSettings -Property @{ + aggregationKind = 'AlertPerResult' + } -ClientOnly) + IncidentConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfiguration -Property @{ + groupingConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration -Property @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } -ClientOnly) + createIncident = $True + } -ClientOnly) + Kind = "NRT"; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "TBDSentinel"; + Severity = "Medium"; + SubscriptionId = "42136a41-5030-4140-aba0-7e6211115d3a"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + WorkspaceName = "SentinelWorkspace"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return the desired values - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-M365DSCSentinelAlertRule -MockWith { return @{ - + Kind = 'NRT' + name = '12345-12345-12345-12345-12345' + properties = @{ + Query = "ThreatIntelIndicators"; + Severity = "Medium"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + AlertDetailsOverride = @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } + CustomDetails = + [PSCustomObject]@{ + Color = 'TenantId' + } + EventGroupingSettings = @{ + aggregationKind = 'AlertPerResult' + } + IncidentConfiguration = @{ + groupingConfiguration = @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + } + } } } } @@ -124,15 +305,83 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "The instance exists and values are NOT in the desired state" -Fixture { BeforeAll { $testParams = @{ - ##TODO - Add Parameters - Ensure = 'Present' - Credential = $Credential; + AlertDetailsOverride = (New-CimInstance -ClassName MSFT_SentinelAlertRuleAlertDetailsOverride -Property @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } -ClientOnly) + CustomDetails = @( + (New-CimInstance -ClassName MSFT_SentinelAlertRuleCustomDetails -Property @{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } -ClientOnly) + ) + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $False; #Drift + Ensure = "Present"; + EventGroupingSettings = (New-CimInstance -ClassName MSFT_SentinelAlertRuleEventGroupingSettings -Property @{ + aggregationKind = 'AlertPerResult' + } -ClientOnly) + IncidentConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfiguration -Property @{ + groupingConfiguration = (New-CimInstance -ClassName MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration -Property @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } -ClientOnly) + createIncident = $True + } -ClientOnly) + Kind = "NRT"; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "TBDSentinel"; + Severity = "Medium"; + SubscriptionId = "42136a41-5030-4140-aba0-7e6211115d3a"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + WorkspaceName = "SentinelWorkspace"; + Credential = $Credential; } - ##TODO - Mock the Get-Cmdlet to return a drift - Mock -CommandName Get-Cmdlet -MockWith { + Mock -CommandName Get-M365DSCSentinelAlertRule -MockWith { return @{ - + Kind = 'NRT' + name = '12345-12345-12345-12345-12345' + properties = @{ + Query = "ThreatIntelIndicators"; + Severity = "Medium"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + AlertDetailsOverride = @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } + CustomDetails = @( + @{ + Color = 'TenantId' + } + ) + EventGroupingSettings = @{ + aggregationKind = 'AlertPerResult' + } + IncidentConfiguration = @{ + groupingConfiguration = @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + } + } } } } @@ -147,8 +396,7 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { 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 + Should -Invoke -CommandName New-M365DSCSentinelAlertRule -Exactly 1 } } @@ -160,10 +408,43 @@ 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-M365DSCSentinelAlertRule -MockWith { return @{ - + Kind = 'NRT' + name = '12345-12345-12345-12345-12345' + properties = @{ + Query = "ThreatIntelIndicators"; + Severity = "Medium"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + Description = "Test"; + DisplayName = "TestDSC1"; + Enabled = $True; + AlertDetailsOverride = @{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + } + CustomDetails = @( + @{ + Color = 'TenantId' + } + ) + EventGroupingSettings = @{ + aggregationKind = 'AlertPerResult' + } + IncidentConfiguration = @{ + groupingConfiguration = @{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + } + } } } } From e6a170f947e06dd416fe81d1a0f2713cf1a50cac Mon Sep 17 00:00:00 2001 From: Nik Charlebois Date: Tue, 22 Oct 2024 23:26:12 -0400 Subject: [PATCH 15/60] Fixes --- .../MSFT_SentinelAlertRule.schema.mof | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof index 3762c576f8..e7fa79a05e 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_SentinelAlertRule/MSFT_SentinelAlertRule.schema.mof @@ -1,71 +1,71 @@ [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleEventGroupingSettings { - [Write, Description("")] String aggregationKind; + [Write, Description("The event grouping aggregation kinds")] String aggregationKind; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleCustomDetails { - [Write, Description("")] String DetailKey; - [Write, Description("")] String DetailValue; + [Write, Description("Key of the custom detail.")] String DetailKey; + [Write, Description("Associated value with the custom detail.")] String DetailValue; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleEntityMapping { - [Write, Description("")] String entityType; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMappingFieldMapping")] String fieldMappings[]; + [Write, Description("Type of entity.")] String entityType; + [Write, Description("List of field mappings."), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMappingFieldMapping")] String fieldMappings[]; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleEntityMappingFieldMapping { - [Write, Description("")] String columnName; - [Write, Description("")] String identifier; + [Write, Description("Name of the column")] String columnName; + [Write, Description("Identifier of the associated field.")] String identifier; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleAlertDetailsOverride { - [Write, Description("")] String alertDescriptionFormat; - [Write, Description("")] String alertDisplayNameFormat; - [Write, Description("")] String alertColumnName; - [Write, Description("")] String alertTacticsColumnName; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty")] String alertDynamicProperties[]; + [Write, Description("The format containing columns name(s) to override the alert description")] String alertDescriptionFormat; + [Write, Description("The format containing columns name(s) to override the alert name")] String alertDisplayNameFormat; + [Write, Description("The column name to take the alert severity from")] String alertSeverityColumnName; + [Write, Description("The column name to take the alert tactics from")] String alertTacticsColumnName; + [Write, Description("List of additional dynamic properties to override"), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty")] String alertDynamicProperties[]; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty { - [Write, Description("")] String alertProperty; - [Write, Description("")] String alertPropertyValue; + [Write, Description("Dynamic property key.")] String alertProperty; + [Write, Description("Dynamic property value.")] String alertPropertyValue; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleIncidentConfiguration { - [Write, Description("")] Boolean createIncident; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration")] String groupingConfiguration; + [Write, Description("Create incidents from alerts triggered by this analytics rule")] Boolean createIncident; + [Write, Description("Set how the alerts that are triggered by this analytics rule, are grouped into incidents"), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration")] String groupingConfiguration; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration { - [Write, Description("")] Boolean enabled; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail")] String groupByAlertDetails[]; - [Write, Description("")] String groupByCustomDetails[]; - [Write, Description("")] String groupByEntities[]; - [Write, Description("")] String lookbackDuration; - [Write, Description("")] String matchingMethod; - [Write, Description("")] Boolean reopenClosedIncident; + [Write, Description("Grouping enabled")] Boolean enabled; + [Write, Description("A list of alert details to group by (when matchingMethod is Selected)"), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail")] String groupByAlertDetails[]; + [Write, Description("A list of custom details keys to group by (when matchingMethod is Selected). Only keys defined in the current alert rule may be used.")] String groupByCustomDetails[]; + [Write, Description("A list of entity types to group by (when matchingMethod is Selected). Only entities defined in the current alert rule may be used.")] String groupByEntities[]; + [Write, Description("Limit the group to alerts created within the lookback duration (in ISO 8601 duration format)")] String lookbackDuration; + [Write, Description("Grouping matching method. When method is Selected at least one of groupByEntities, groupByAlertDetails, groupByCustomDetails must be provided and not empty.")] String matchingMethod; + [Write, Description("Re-open closed matching incidents")] Boolean reopenClosedIncident; }; [ClassVersion("1.0.0")] class MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail { - [Write, Description("")] String DisplayName; - [Write, Description("")] String Severity; + [Write, Description("Display name of the alert detail.")] String DisplayName; + [Write, Description("Severity level associated with the alert detail.")] String Severity; }; [ClassVersion("1.0.0.0"), FriendlyName("SentinelAlertRule")] @@ -77,29 +77,29 @@ class MSFT_SentinelAlertRule : OMI_BaseResource [Write, Description("The name of the workspace.")] String WorkspaceName; [Write, Description("The unique id of the indicator.")] String Id; [Write, Description("The name of the workspace.")] String Description; - [Write, Description("")] String ProductFilter; - [Write, Description("")] Boolean Enabled; - [Write, Description("")] String Severity; - [Write, Description("")] String Tactics[]; - [Write, Description("")] String Techniques[]; - [Write, Description("")] String SubTechniques[]; - [Write, Description("")] String Query; - [Write, Description("")] String QueryFrequency; - [Write, Description("")] String QueryPeriod; - [Write, Description("")] String TriggerOperator; - [Write, Description("")] UInt32 TriggerThreshold; - [Write, Description("")] String SuppressionDuration; - [Write, Description("")] String SuppressionEnabled; - [Write, Description("")] String AlertRuleTemplateName; - [Write, Description("")] String DisplayNamesExcludeFilter[]; - [Write, Description("")] String DisplayNamesFilter[]; - [Write, Description("")] String SeveritiesFilter[]; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEventGroupingSettings")] String EventGroupingSettings; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleCustomDetails")] String CustomDetails[]; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMapping")] String EntityMappings[]; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverride")] String AlertDetailsOverride; - [Write, Description(""), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfiguration")] String IncidentConfiguration; - [Write, Description("")] String Kind; + [Write, Description("The alerts' productName on which the cases will be generated")] String ProductFilter; + [Write, Description("Determines whether this alert rule is enabled or disabled.")] Boolean Enabled; + [Write, Description("The severity for alerts created by this alert rule.")] String Severity; + [Write, Description("The tactics of the alert rule")] String Tactics[]; + [Write, Description("The techniques of the alert rule")] String Techniques[]; + [Write, Description("The sub-techniques of the alert rule")] String SubTechniques[]; + [Write, Description("The query that creates alerts for this rule.")] String Query; + [Write, Description("The frequency (in ISO 8601 duration format) for this alert rule to run.")] String QueryFrequency; + [Write, Description("The period (in ISO 8601 duration format) that this alert rule looks at.")] String QueryPeriod; + [Write, Description("The operation against the threshold that triggers alert rule.")] String TriggerOperator; + [Write, Description("The threshold triggers this alert rule.")] UInt32 TriggerThreshold; + [Write, Description("The suppression (in ISO 8601 duration format) to wait since last time this alert rule been triggered.")] String SuppressionDuration; + [Write, Description("Determines whether the suppression for this alert rule is enabled or disabled.")] String SuppressionEnabled; + [Write, Description("The Name of the alert rule template used to create this rule.")] String AlertRuleTemplateName; + [Write, Description("The alerts' displayNames on which the cases will not be generated.")] String DisplayNamesExcludeFilter[]; + [Write, Description("The alerts' displayNames on which the cases will be generated.")] String DisplayNamesFilter[]; + [Write, Description("The alerts' severities on which the cases will be generated")] String SeveritiesFilter[]; + [Write, Description("The event grouping settings."), EmbeddedInstance("MSFT_SentinelAlertRuleEventGroupingSettings")] String EventGroupingSettings; + [Write, Description("Dictionary of string key-value pairs of columns to be attached to the alert"), EmbeddedInstance("MSFT_SentinelAlertRuleCustomDetails")] String CustomDetails[]; + [Write, Description("Array of the entity mappings of the alert rule"), EmbeddedInstance("MSFT_SentinelAlertRuleEntityMapping")] String EntityMappings[]; + [Write, Description("The alert details override settings"), EmbeddedInstance("MSFT_SentinelAlertRuleAlertDetailsOverride")] String AlertDetailsOverride; + [Write, Description("The settings of the incidents that created from alerts triggered by this analytics rule"), EmbeddedInstance("MSFT_SentinelAlertRuleIncidentConfiguration")] String IncidentConfiguration; + [Write, Description("The kind of the alert rule")] String Kind; [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; From 5b067bc4f1c7070fcb501304e14f08d5d1577e1b Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 03:43:38 +0000 Subject: [PATCH 16/60] Updated Resources and Cmdlet documentation pages --- .../security-compliance/SentinelAlertRule.md | 361 ++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 docs/docs/resources/security-compliance/SentinelAlertRule.md diff --git a/docs/docs/resources/security-compliance/SentinelAlertRule.md b/docs/docs/resources/security-compliance/SentinelAlertRule.md new file mode 100644 index 0000000000..89da026080 --- /dev/null +++ b/docs/docs/resources/security-compliance/SentinelAlertRule.md @@ -0,0 +1,361 @@ +# SentinelAlertRule + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **DisplayName** | Key | String | The display name of the indicator | | +| **SubscriptionId** | Write | String | The name of the resource group. The name is case insensitive. | | +| **ResourceGroupName** | Write | String | The name of the resource group. The name is case insensitive. | | +| **WorkspaceName** | Write | String | The name of the workspace. | | +| **Id** | Write | String | The unique id of the indicator. | | +| **Description** | Write | String | The name of the workspace. | | +| **ProductFilter** | Write | String | The alerts' productName on which the cases will be generated | | +| **Enabled** | Write | Boolean | Determines whether this alert rule is enabled or disabled. | | +| **Severity** | Write | String | The severity for alerts created by this alert rule. | | +| **Tactics** | Write | StringArray[] | The tactics of the alert rule | | +| **Techniques** | Write | StringArray[] | The techniques of the alert rule | | +| **SubTechniques** | Write | StringArray[] | The sub-techniques of the alert rule | | +| **Query** | Write | String | The query that creates alerts for this rule. | | +| **QueryFrequency** | Write | String | The frequency (in ISO 8601 duration format) for this alert rule to run. | | +| **QueryPeriod** | Write | String | The period (in ISO 8601 duration format) that this alert rule looks at. | | +| **TriggerOperator** | Write | String | The operation against the threshold that triggers alert rule. | | +| **TriggerThreshold** | Write | UInt32 | The threshold triggers this alert rule. | | +| **SuppressionDuration** | Write | String | The suppression (in ISO 8601 duration format) to wait since last time this alert rule been triggered. | | +| **SuppressionEnabled** | Write | String | Determines whether the suppression for this alert rule is enabled or disabled. | | +| **AlertRuleTemplateName** | Write | String | The Name of the alert rule template used to create this rule. | | +| **DisplayNamesExcludeFilter** | Write | StringArray[] | The alerts' displayNames on which the cases will not be generated. | | +| **DisplayNamesFilter** | Write | StringArray[] | The alerts' displayNames on which the cases will be generated. | | +| **SeveritiesFilter** | Write | StringArray[] | The alerts' severities on which the cases will be generated | | +| **EventGroupingSettings** | Write | MSFT_SentinelAlertRuleEventGroupingSettings | The event grouping settings. | | +| **CustomDetails** | Write | MSFT_SentinelAlertRuleCustomDetails[] | Dictionary of string key-value pairs of columns to be attached to the alert | | +| **EntityMappings** | Write | MSFT_SentinelAlertRuleEntityMapping[] | Array of the entity mappings of the alert rule | | +| **AlertDetailsOverride** | Write | MSFT_SentinelAlertRuleAlertDetailsOverride | The alert details override settings | | +| **IncidentConfiguration** | Write | MSFT_SentinelAlertRuleIncidentConfiguration | The settings of the incidents that created from alerts triggered by this analytics rule | | +| **Kind** | Write | String | The kind of the alert rule | | +| **Ensure** | Write | String | Present ensures the instance exists, absent ensures it is removed. | `Absent`, `Present` | +| **Credential** | Write | PSCredential | Credentials of the workload's Admin | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | +| **AccessTokens** | Write | StringArray[] | Access token used for authentication. | | + +### MSFT_SentinelAlertRuleEventGroupingSettings + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **aggregationKind** | Write | String | The event grouping aggregation kinds | | + +### MSFT_SentinelAlertRuleCustomDetails + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **DetailKey** | Write | String | Key of the custom detail. | | +| **DetailValue** | Write | String | Associated value with the custom detail. | | + +### MSFT_SentinelAlertRuleEntityMapping + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **entityType** | Write | String | Type of entity. | | +| **fieldMappings** | Write | MSFT_SentinelAlertRuleEntityMappingFieldMapping[] | List of field mappings. | | + +### MSFT_SentinelAlertRuleEntityMappingFieldMapping + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **columnName** | Write | String | Name of the column | | +| **identifier** | Write | String | Identifier of the associated field. | | + +### MSFT_SentinelAlertRuleAlertDetailsOverride + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **alertDescriptionFormat** | Write | String | The format containing columns name(s) to override the alert description | | +| **alertDisplayNameFormat** | Write | String | The format containing columns name(s) to override the alert name | | +| **alertSeverityColumnName** | Write | String | The column name to take the alert severity from | | +| **alertTacticsColumnName** | Write | String | The column name to take the alert tactics from | | +| **alertDynamicProperties** | Write | MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty[] | List of additional dynamic properties to override | | + +### MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **alertProperty** | Write | String | Dynamic property key. | | +| **alertPropertyValue** | Write | String | Dynamic property value. | | + +### MSFT_SentinelAlertRuleIncidentConfiguration + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **createIncident** | Write | Boolean | Create incidents from alerts triggered by this analytics rule | | +| **groupingConfiguration** | Write | MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration | Set how the alerts that are triggered by this analytics rule, are grouped into incidents | | + +### MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **enabled** | Write | Boolean | Grouping enabled | | +| **groupByAlertDetails** | Write | MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail[] | A list of alert details to group by (when matchingMethod is Selected) | | +| **groupByCustomDetails** | Write | StringArray[] | A list of custom details keys to group by (when matchingMethod is Selected). Only keys defined in the current alert rule may be used. | | +| **groupByEntities** | Write | StringArray[] | A list of entity types to group by (when matchingMethod is Selected). Only entities defined in the current alert rule may be used. | | +| **lookbackDuration** | Write | String | Limit the group to alerts created within the lookback duration (in ISO 8601 duration format) | | +| **matchingMethod** | Write | String | Grouping matching method. When method is Selected at least one of groupByEntities, groupByAlertDetails, groupByCustomDetails must be provided and not empty. | | +| **reopenClosedIncident** | Write | Boolean | Re-open closed matching incidents | | + +### MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **DisplayName** | Write | String | Display name of the alert detail. | | +| **Severity** | Write | String | Severity level associated with the alert detail. | | + + +## Description + +Configures alert rules in Azure Sentinel. + +## Permissions + +### Microsoft Graph + +To authenticate with the Microsoft Graph API, this resource required the following permissions: + +#### Delegated permissions + +- **Read** + + - None + +- **Update** + + - None + +#### Application permissions + +- **Read** + + - None + +- **Update** + + - None + +## Examples + +### Example 1 + +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + SentinelAlertRule "SentinelAlertRule-MyNRTRule" + { + AlertDetailsOverride = MSFT_SentinelAlertRuleAlertDetailsOverride{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + }; + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + CustomDetails = @( + MSFT_SentinelAlertRuleCustomDetails{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } + ); + Description = "Test"; + DisplayName = "MyNRTRule"; + Enabled = $True; + Ensure = "Present"; + EntityMappings = @( + MSFT_SentinelAlertRuleEntityMapping{ + fieldMappings = @( + MSFT_SentinelAlertRuleEntityMappingFieldMapping{ + identifier = 'AppId' + columnName = 'Id' + } + ) + entityType = 'CloudApplication' + } + ); + IncidentConfiguration = MSFT_SentinelAlertRuleIncidentConfiguration{ + groupingConfiguration = MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + }; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "ResourceGroupName"; + Severity = "Medium"; + SubscriptionId = "xxxx"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + TenantId = $TenantId; + WorkspaceName = "SentinelWorkspace"; + } + } +} +``` + +### Example 2 + +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + SentinelAlertRule "SentinelAlertRule-MyNRTRule" + { + AlertDetailsOverride = MSFT_SentinelAlertRuleAlertDetailsOverride{ + alertDescriptionFormat = 'This is an example of the alert content' + alertDisplayNameFormat = 'Alert from {{{TimeGenerated}} ' + }; + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + CustomDetails = @( + MSFT_SentinelAlertRuleCustomDetails{ + DetailKey = 'Color' + DetailValue = 'TenantId' + } + ); + Description = "Test"; + DisplayName = "MyNRTRule"; + Enabled = $True; + Ensure = "Present"; + EntityMappings = @( + MSFT_SentinelAlertRuleEntityMapping{ + fieldMappings = @( + MSFT_SentinelAlertRuleEntityMappingFieldMapping{ + identifier = 'AppId' + columnName = 'Id' + } + ) + entityType = 'CloudApplication' + } + ); + IncidentConfiguration = MSFT_SentinelAlertRuleIncidentConfiguration{ + groupingConfiguration = MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration{ + lookbackDuration = 'PT5H' + matchingMethod = 'Selected' + groupByCustomDetails = @('Color') + groupByEntities = @('CloudApplication') + reopenClosedIncident = $True + enabled = $True + } + createIncident = $True + }; + Query = "ThreatIntelIndicators"; + ResourceGroupName = "ResourceGroupName"; + Severity = "High"; #Drift + SubscriptionId = "xxxx"; + SuppressionDuration = "PT5H"; + Tactics = @(); + Techniques = @(); + TenantId = $TenantId; + WorkspaceName = "SentinelWorkspace"; + } + } +} +``` + +### Example 3 + +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + SentinelAlertRule "SentinelAlertRule-MyNRTRule" + { + ApplicationId = $ApplicationId; + CertificateThumbprint = $CertificateThumbprint; + Description = "Test"; + DisplayName = "MyNRTRule"; + Ensure = "Absent"; + ResourceGroupName = "ResourceGroupName"; + Severity = "Medium"; + SubscriptionId = "xxxx"; + TenantId = $TenantId; + WorkspaceName = "SentinelWorkspace"; + } + } +} +``` + From a50ebc607be92bc28407a5f79642b940e5bbe11f Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 03:48:55 +0000 Subject: [PATCH 17/60] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 355 ++++++++++++++++++ 1 file changed, 355 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 8e7cea3329..33d014f36c 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -49144,6 +49144,361 @@ } ] }, + { + "ClassName": "MSFT_SentinelAlertRuleEventGroupingSettings", + "Parameters": [ + { + "CIMType": "String", + "Name": "aggregationKind", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SentinelAlertRuleCustomDetails", + "Parameters": [ + { + "CIMType": "String", + "Name": "DetailKey", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "DetailValue", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SentinelAlertRuleEntityMapping", + "Parameters": [ + { + "CIMType": "String", + "Name": "entityType", + "Option": "Write" + }, + { + "CIMType": "MSFT_SentinelAlertRuleEntityMappingFieldMapping[]", + "Name": "fieldMappings", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SentinelAlertRuleEntityMappingFieldMapping", + "Parameters": [ + { + "CIMType": "String", + "Name": "columnName", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "identifier", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SentinelAlertRuleAlertDetailsOverride", + "Parameters": [ + { + "CIMType": "String", + "Name": "alertDescriptionFormat", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "alertDisplayNameFormat", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "alertSeverityColumnName", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "alertTacticsColumnName", + "Option": "Write" + }, + { + "CIMType": "MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty[]", + "Name": "alertDynamicProperties", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SentinelAlertRuleAlertDetailsOverrideAlertDynamicProperty", + "Parameters": [ + { + "CIMType": "String", + "Name": "alertProperty", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "alertPropertyValue", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SentinelAlertRuleIncidentConfiguration", + "Parameters": [ + { + "CIMType": "Boolean", + "Name": "createIncident", + "Option": "Write" + }, + { + "CIMType": "MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration", + "Name": "groupingConfiguration", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfiguration", + "Parameters": [ + { + "CIMType": "Boolean", + "Name": "enabled", + "Option": "Write" + }, + { + "CIMType": "MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail[]", + "Name": "groupByAlertDetails", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "groupByCustomDetails", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "groupByEntities", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "lookbackDuration", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "matchingMethod", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "reopenClosedIncident", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SentinelAlertRuleIncidentConfigurationGroupingConfigurationAlertDetail", + "Parameters": [ + { + "CIMType": "String", + "Name": "DisplayName", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Severity", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_SentinelAlertRule", + "Parameters": [ + { + "CIMType": "String", + "Name": "DisplayName", + "Option": "Key" + }, + { + "CIMType": "String", + "Name": "SubscriptionId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ResourceGroupName", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "WorkspaceName", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Id", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Description", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ProductFilter", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "Enabled", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Severity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "Tactics", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "Techniques", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "SubTechniques", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Query", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "QueryFrequency", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "QueryPeriod", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TriggerOperator", + "Option": "Write" + }, + { + "CIMType": "UInt32", + "Name": "TriggerThreshold", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "SuppressionDuration", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "SuppressionEnabled", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "AlertRuleTemplateName", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "DisplayNamesExcludeFilter", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "DisplayNamesFilter", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "SeveritiesFilter", + "Option": "Write" + }, + { + "CIMType": "MSFT_SentinelAlertRuleEventGroupingSettings", + "Name": "EventGroupingSettings", + "Option": "Write" + }, + { + "CIMType": "MSFT_SentinelAlertRuleCustomDetails[]", + "Name": "CustomDetails", + "Option": "Write" + }, + { + "CIMType": "MSFT_SentinelAlertRuleEntityMapping[]", + "Name": "EntityMappings", + "Option": "Write" + }, + { + "CIMType": "MSFT_SentinelAlertRuleAlertDetailsOverride", + "Name": "AlertDetailsOverride", + "Option": "Write" + }, + { + "CIMType": "MSFT_SentinelAlertRuleIncidentConfiguration", + "Name": "IncidentConfiguration", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Kind", + "Option": "Write" + }, + { + "CIMType": "string", + "Name": "Ensure", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Credential", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ApplicationId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TenantId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificateThumbprint", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "ManagedIdentity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "AccessTokens", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_SentinelSetting", "Parameters": [ From 833976c7c847fe05d4021594c977f6f5dbfacc64 Mon Sep 17 00:00:00 2001 From: Riyansh Goyal Date: Wed, 23 Oct 2024 11:52:36 +0530 Subject: [PATCH 18/60] added get,export and test target resource --- ...SFT_AADNetworkAccessForwardingProfile.psm1 | 546 ++++++++++++++++++ ...DNetworkAccessForwardingProfile.schema.mof | 25 + .../readme.md | 7 + .../settings.json | 33 ++ .../Dependencies/Manifest.psd1 | 4 + 5 files changed, 615 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/settings.json diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 new file mode 100644 index 0000000000..c0f158cd0f --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 @@ -0,0 +1,546 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + #region resource generator code + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $State, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Policies, + + #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, + + [Parameter()] + [System.String[]] + $AccessTokens + ) + + try + { + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + $nullResult = $PSBoundParameters + $nullResult.Ensure = 'Absent' + + $getValue = $null + #region resource generator code + if (-not [System.String]::IsNullOrEmpty($Id)) + { + $getValue = Get-MgBetaNetworkAccessForwardingProfile -ForwardingProfileId $Id -ErrorAction SilentlyContinue + } + if ($null -eq $getValue) + { + Write-Verbose -Message "Could not find an Azure AD Network Access Forwarding Profile with Id:{$Id}" + + if (-not [System.String]::IsNullOrEmpty($Name)) + { + $getValue = Get-MgBetaNetworkAccessForwardingProfile -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq $Name } + } + } + + + #endregion + if ($null -eq $getValue) + { + Write-Verbose -Message "Could not find an Azure AD Network Access Forwarding Profile with {$Name}." + return $nullResult + } + + Write-Verbose -Message "An Azure AD Network Access Forwarding Profile with {$Id} and {$Name} was found" + + $forwardingProfilePolicies = Get-MgBetaNetworkAccessForwardingProfilePolicy -ForwardingProfileId $getValue.Id -ErrorAction SilentlyContinue + if ($null -ne $forwardingProfilePolicies) + { + Write-Verbose -Message "An Azure AD Network Access Forwarding Profile Policy with $($forwardingProfilePolicies.Id) and $($forwardingProfilePolicies.Name) was found" + } + + $complexPolicies = @() + foreach ($currentPolicy in $forwardingProfilePolicies) + { + $myPolicies = @{} + $myPolicies.Add('Name', $currentPolicy.Policy.Name) + $myPolicies.Add('State', $currentPolicy.State) + $myPolicies.Add('Id', $currentPolicy.Id) + if ($myPolicies.values.Where({ $null -ne $_ }).Count -gt 0) + { + $complexPolicies += $myPolicies + } + } + + + $results = @{ + Name = $getValue.Name + Id = $getValue.Id + State = $getValue.State + Policies = $complexPolicies + # 'Policies@Odata.Context' = $getValue.AdditionalProperties['policies@odata.context'] + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + ApplicationSecret = $ApplicationSecret + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + } + + 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(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $State, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Policies, + + #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, + + [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 + + # $BoundParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + + + # if ( $Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') + # { + # Write-Warning -Message "Can not create the Azure AD Network Access Forwarding Profile with {$($currentInstance.Name)}." + # } + # elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') + # { + # Write-Verbose -Message "Updating the Azure AD Network Access Forwarding Profile with {$($currentInstance.Id)}" + + # $updateParameters = ([Hashtable]$BoundParameters).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 + # Update-MgBetaNetworkAccessForwardingProfile ` + # -ForwardingProfileId $Id ` + # -BodyParameter $UpdateParameters + # #endregion + # } + # elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') + # { + # Write-Warning -Message "Can not remove the Azure AD Network Access Forwarding Profile with {$($currentInstance.Name)}." + # } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + + #region resource generator code + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $State, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $Policies, + + #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, + + [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 + + Write-Verbose -Message "Testing configuration of the Azure AD Network Access Forwarding Profile with Id:{$Id} and Name:{$Name}" + + $CurrentValues = Get-TargetResource @PSBoundParameters + $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() + + 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 ($null -ne $source -and $source.GetType().Name -like '*CimInstance*') + { + $testResult = Compare-M365DSCComplexObject ` + -Source ($source) ` + -Target ($target) + + if (-not $testResult) + { + break + } + + $ValuesToCheck.Remove($key) | Out-Null + } + } + + $ValuesToCheck.Remove('Id') | Out-Null + $ValuesToCheck = Remove-M365DSCAuthenticationParameter -BoundParameters $ValuesToCheck + + 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.String] + $Filter, + + [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 + ) + + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + try + { + #region resource generator code + [array]$getValue = Get-MgBetaNetworkAccessForwardingProfile ` + -Filter $Filter ` + -All ` + -ErrorAction Stop + #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.name)) + { + $displayedKey = $config.name + } + Write-Host " |---[$i/$($getValue.Count)] $displayedKey" -NoNewline + $params = @{ + Id = $config.Id + Name = $config.Name + Ensure = 'Present' + Credential = $Credential + ApplicationId = $ApplicationId + TenantId = $TenantId + ApplicationSecret = $ApplicationSecret + CertificateThumbprint = $CertificateThumbprint + ManagedIdentity = $ManagedIdentity.IsPresent + AccessTokens = $AccessTokens + } + + $Results = Get-TargetResource @Params + $Results = Update-M365DSCExportAuthenticationResults -ConnectionMode $ConnectionMode ` + -Results $Results + + if ($Results.Policies.Count -gt 0) + { + $Results.Policies = Get-PoliciesAsString $Results.Policies + } + + $currentDSCBlock = Get-M365DSCExportContentForResource -ResourceName $ResourceName ` + -ConnectionMode $ConnectionMode ` + -ModulePath $PSScriptRoot ` + -Results $Results ` + -Credential $Credential + + if ($null -ne $Results.Policies) + { + $currentDSCBlock = Convert-DSCStringParamToVariable -DSCBlock $currentDSCBlock ` + -ParameterName 'Policies' + } + $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 Get-PoliciesAsString +{ + [CmdletBinding()] + [OutputType([System.String])] + param( + [Parameter(Mandatory = $true)] + [System.Collections.ArrayList] + $Policies + ) + + $StringContent = '@(' + foreach ($policy in $Policies) + { + $StringContent += "MSFT_MicrosoftGraphNetworkaccessPolicyLink {`r`n" + $StringContent += " State = '" + $policy.State + "'`r`n" + $StringContent += " Id = '" + $policy.Id + "'`r`n" + $StringContent += " Name = '" + $policy.Name + "'`r`n" + $StringContent += " }`r`n" + } + $StringContent += ' )' + return $StringContent +} + +Export-ModuleMember -Function *-TargetResource diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.schema.mof new file mode 100644 index 0000000000..939b48bdb3 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.schema.mof @@ -0,0 +1,25 @@ + +[ClassVersion("1.0.0")] +class MSFT_MicrosoftGraphNetworkaccessPolicyLink +{ + [Write, Description("Policy Name. Required")] String Name; + [Write, Description("A Unique Identifier")] String Id; + [Write, Description("status")] String state; +}; + +[ClassVersion("1.0.0.0"), FriendlyName("AADNetworkAccessForwardingProfile")] +class MSFT_AADNetworkAccessForwardingProfile : OMI_BaseResource +{ + [Key, Description("Profile Name. Required.")] String Name; + [Write, Description("Id of the profile. Unique Identifier")] String Id; + [Write, Description("status of the profile")] String State; + [Write, Description("Traffic forwarding policies associated with this profile."), EmbeddedInstance("MSFT_MicrosoftGraphNetworkaccessPolicyLink")] String Policies[]; + [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[]; +}; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/readme.md new file mode 100644 index 0000000000..385d36537f --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/readme.md @@ -0,0 +1,7 @@ + +# AADNetworkAccessForwardingProfile + +## Description + +This resource configure the Azure AD Network Access Forwarding Profile + diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/settings.json new file mode 100644 index 0000000000..4a473ad41b --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/settings.json @@ -0,0 +1,33 @@ + +{ + "resourceName": "AADNetworkAccessForwardingProfile", + "description": "This resource configures an Azure AD Network Access Forwarding Profile.", + "permissions": { + "graph": { + "delegated": { + "read": [ + { + "name": "NetworkAccess.Read.All" + } + ], + "update": [ + { + "name": "NetworkAccess.ReadWrite.All" + } + ] + }, + "application": { + "read": [ + { + "name": "NetworkAccess.Read.All" + } + ], + "update": [ + { + "name": "NetworkAccess.ReadWrite.All" + } + ] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 index 7fd6bbe7fe..288104b127 100644 --- a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 +++ b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 @@ -56,6 +56,10 @@ ModuleName = 'Microsoft.Graph.Beta.DeviceManagement.Enrollment' RequiredVersion = '2.23.0' }, + @{ + ModuleName = 'Microsoft.Graph.Beta.NetworkAccess' + RequiredVersion = '2.23.0' + }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.DirectoryManagement' RequiredVersion = '2.23.0' From ab76a26424d7b77598b701d06d167a80e00123b8 Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Wed, 23 Oct 2024 11:55:16 +0530 Subject: [PATCH 19/60] Added UTs --- .../MSFT_AADIdentityAPIConnector.schema.mof | 10 +- ...ft365DSC.AADIdentityAPIConnector.Tests.ps1 | 225 +++++++----------- 2 files changed, 88 insertions(+), 147 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof index 9d7db64611..447b39d00d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADIdentityAPIConnector/MSFT_AADIdentityAPIConnector.schema.mof @@ -1,10 +1,10 @@ [ClassVersion("1.0.0")] class MSFT_AADIdentityAPIConnectionCertificate { - [Write, Description(""), EmbeddedInstance("MSFT_Credential")] String Pkcs12Value; - [Write, Description("")] String Thumbprint; - [Write, Description(""), EmbeddedInstance("MSFT_Credential")] String Password; - [Write, Description("")] Boolean IsActive; + [Write, Description("Pkcs12Value of the certificate as a secure string in Base64 encoding"), EmbeddedInstance("MSFT_Credential")] String Pkcs12Value; + [Write, Description("Thumbprint of the certificate in Base64 encoding")] String Thumbprint; + [Write, Description("Password of the certificate as a secure string"), EmbeddedInstance("MSFT_Credential")] String Password; + [Write, Description("Tells if the certificate is in use or not")] Boolean IsActive; }; @@ -16,7 +16,7 @@ class MSFT_AADIdentityAPIConnector : OMI_BaseResource [Key, Description("The unique identifier for an entity. Read-only.")] String Id; [Write, Description("The username of the password")] String Username; [Write, Description("The password of certificate/basic auth"), EmbeddedInstance("MSFT_Credential")] String Password; - [Write, Description(""), EmbeddedInstance("MSFT_AADIdentityAPIConnectionCertificate")] String Certificates[]; + [Write, Description("List of certificates to be used in the API connector"), EmbeddedInstance("MSFT_AADIdentityAPIConnectionCertificate")] String Certificates[]; [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; diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 index 18e4a5281a..8fcde7f64b 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 @@ -55,26 +55,28 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { # Test contexts Context -Name "The AADIdentityAPIConnector should exist but it DOES NOT" -Fixture { BeforeAll { + $testParams = @{ - AuthenticationConfiguration = (New-CimInstance -ClassName MSFT_MicrosoftGraphapiAuthenticationConfigurationBase -Property @{ - Password = "FakeStringValue" - Pkcs12Value = "FakeStringValue" - CertificateList = [CimInstance[]]@( - (New-CimInstance -ClassName MSFT_MicrosoftGraphpkcs12CertificateInformation -Property @{ - IsActive = $True - NotAfter = $True - Thumbprint = "FakeStringValue" - NotBefore = $True - } -ClientOnly) - ) - Username = "FakeStringValue" - odataType = "#microsoft.graph.basicAuthentication" - } -ClientOnly) - DisplayName = "FakeStringValue" - Id = "FakeStringValue" - TargetUrl = "FakeStringValue" - Ensure = "Present" - Credential = $Credential; + DisplayName = 'FakeStringValue' + TargetUrl = 'FakeStringValue' + Id = 'FakeStringValue' + Username = 'FakeStringValue' + Password = $Credential + Certificates = @( + New-CimInstance -ClassName 'MSFT_AADIdentityAPIConnectionCertificate' -Property @{ + Thumbprint = 'FakeStringValue' + Pkcs12Value = (New-CimInstance -ClassName 'MSFT_Credential' -Property @{ + Username = 'FakeStringValue' + Password = 'FakeStringValue' + } -ClientOnly) + Password = (New-CimInstance -ClassName 'MSFT_Credential' -Property @{ + Username = 'FakeStringValue' + Password = 'FakeStringValue' + } -ClientOnly) + IsActive = $true + } -ClientOnly + ) + Credential = $Credential } Mock -CommandName Get-MgBetaIdentityAPIConnector -MockWith { @@ -96,50 +98,40 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Context -Name "The AADIdentityAPIConnector exists but it SHOULD NOT" -Fixture { BeforeAll { $testParams = @{ - AuthenticationConfiguration = (New-CimInstance -ClassName MSFT_MicrosoftGraphapiAuthenticationConfigurationBase -Property @{ - Password = "FakeStringValue" - Pkcs12Value = "FakeStringValue" - CertificateList = [CimInstance[]]@( - (New-CimInstance -ClassName MSFT_MicrosoftGraphpkcs12CertificateInformation -Property @{ - IsActive = $True - NotAfter = $True - Thumbprint = "FakeStringValue" - NotBefore = $True - } -ClientOnly) - ) - Username = "FakeStringValue" - odataType = "#microsoft.graph.basicAuthentication" - } -ClientOnly) - DisplayName = "FakeStringValue" - Id = "FakeStringValue" - TargetUrl = "FakeStringValue" + DisplayName = 'FakeStringValue' + TargetUrl = 'FakeStringValue' + Id = 'FakeStringValue' + Username = 'FakeStringValue' + Password = $Credential + Certificates = @( + New-CimInstance -ClassName 'MSFT_AADIdentityAPIConnectionCertificate' -Property @{ + Thumbprint = 'FakeStringValue' + Pkcs12Value = (New-CimInstance -ClassName 'MSFT_Credential' -Property @{ + Username = 'FakeStringValue' + Password = 'FakeStringValue' + } -ClientOnly) + Password = (New-CimInstance -ClassName 'MSFT_Credential' -Property @{ + Username = 'FakeStringValue' + Password = 'FakeStringValue' + } -ClientOnly) + IsActive = $true + } -ClientOnly + ) + Credential = $Credential Ensure = 'Absent' - Credential = $Credential; } Mock -CommandName Get-MgBetaIdentityAPIConnector -MockWith { return @{ - AdditionalProperties = @{ - '@odata.type' = "#microsoft.graph.IdentityApiConnector" - } + DisplayName = 'FakeStringValue' + TargetUrl = 'FakeStringValue' + Id = 'FakeStringValue' AuthenticationConfiguration = @{ - '@odata.type' = "#microsoft.graph.basicAuthentication" - Pkcs12Value = "FakeStringValue" - Password = "FakeStringValue" - Username = "FakeStringValue" - CertificateList = @( - @{ - IsActive = $True - NotAfter = $True - Thumbprint = "FakeStringValue" - NotBefore = $True - } - ) + AdditionalProperties = @{ + Username = 'FakeStringValue' + Password = $Cred + } } - DisplayName = "FakeStringValue" - Id = "FakeStringValue" - TargetUrl = "FakeStringValue" - } } } @@ -159,51 +151,28 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } Context -Name "The AADIdentityAPIConnector Exists and Values are already in the desired state" -Fixture { BeforeAll { + $testParams = @{ - AuthenticationConfiguration = (New-CimInstance -ClassName MSFT_MicrosoftGraphapiAuthenticationConfigurationBase -Property @{ - Password = "FakeStringValue" - Pkcs12Value = "FakeStringValue" - CertificateList = [CimInstance[]]@( - (New-CimInstance -ClassName MSFT_MicrosoftGraphpkcs12CertificateInformation -Property @{ - IsActive = $True - NotAfter = $True - Thumbprint = "FakeStringValue" - NotBefore = $True - } -ClientOnly) - ) - Username = "FakeStringValue" - odataType = "#microsoft.graph.basicAuthentication" - } -ClientOnly) - DisplayName = "FakeStringValue" - Id = "FakeStringValue" - TargetUrl = "FakeStringValue" + DisplayName = 'FakeStringValue' + TargetUrl = 'FakeStringValue' + Id = 'FakeStringValue' + Username = 'FakeStringValue' + Password = $Credential + Credential = $Credential Ensure = 'Present' - Credential = $Credential; } Mock -CommandName Get-MgBetaIdentityAPIConnector -MockWith { return @{ - AdditionalProperties = @{ - '@odata.type' = "#microsoft.graph.IdentityApiConnector" - } + DisplayName = 'FakeStringValue' + TargetUrl = 'FakeStringValue' + Id = 'FakeStringValue' AuthenticationConfiguration = @{ - '@odata.type' = "#microsoft.graph.basicAuthentication" - Pkcs12Value = "FakeStringValue" - Password = "FakeStringValue" - Username = "FakeStringValue" - CertificateList = @( - @{ - IsActive = $True - NotAfter = $True - Thumbprint = "FakeStringValue" - NotBefore = $True - } - ) + AdditionalProperties = @{ + Username = 'FakeStringValue' + Password = $Cred + } } - DisplayName = "FakeStringValue" - Id = "FakeStringValue" - TargetUrl = "FakeStringValue" - } } } @@ -215,45 +184,29 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } Context -Name "The AADIdentityAPIConnector exists and values are NOT in the desired state" -Fixture { + BeforeAll { $testParams = @{ - AuthenticationConfiguration = (New-CimInstance -ClassName MSFT_MicrosoftGraphapiAuthenticationConfigurationBase -Property @{ - Password = "FakeStringValue" - Pkcs12Value = "FakeStringValue" - CertificateList = [CimInstance[]]@( - (New-CimInstance -ClassName MSFT_MicrosoftGraphpkcs12CertificateInformation -Property @{ - IsActive = $True - NotAfter = $True - Thumbprint = "FakeStringValue" - NotBefore = $True - } -ClientOnly) - ) - Username = "FakeStringValue" - odataType = "#microsoft.graph.basicAuthentication" - } -ClientOnly) - DisplayName = "FakeStringValue" - Id = "FakeStringValue" - TargetUrl = "FakeStringValue" + DisplayName = 'FakeStringValue2' #drift + TargetUrl = 'FakeStringValue' + Id = 'FakeStringValue' + Username = 'FakeStringValue' + Password = $Credential + Credential = $Credential Ensure = 'Present' - Credential = $Credential; } Mock -CommandName Get-MgBetaIdentityAPIConnector -MockWith { return @{ + DisplayName = 'FakeStringValue' + TargetUrl = 'FakeStringValue' + Id = 'FakeStringValue' AuthenticationConfiguration = @{ - '@odata.type' = "#microsoft.graph.basicAuthentication" - Pkcs12Value = "FakeStringValue" - Password = "FakeStringValue" - Username = "FakeStringValue" - CertificateList = @( - @{ - Thumbprint = "FakeStringValue" - } - ) + AdditionalProperties = @{ + Username = 'FakeStringValue' + Password = 'FakeStringValue' + } } - DisplayName = "FakeStringValue" - Id = "FakeStringValue" - TargetUrl = "FakeStringValue" } } } @@ -282,27 +235,15 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Mock -CommandName Get-MgBetaIdentityAPIConnector -MockWith { return @{ - AdditionalProperties = @{ - '@odata.type' = "#microsoft.graph.IdentityApiConnector" - } + DisplayName = 'FakeStringValue' + TargetUrl = 'FakeStringValue' + Id = 'FakeStringValue' AuthenticationConfiguration = @{ - '@odata.type' = "#microsoft.graph.basicAuthentication" - Pkcs12Value = "FakeStringValue" - Password = "FakeStringValue" - Username = "FakeStringValue" - CertificateList = @( - @{ - IsActive = $True - NotAfter = $True - Thumbprint = "FakeStringValue" - NotBefore = $True - } - ) + AdditionalProperties = @{ + Username = 'FakeStringValue' + Password = 'FakeStringValue' + } } - DisplayName = "FakeStringValue" - Id = "FakeStringValue" - TargetUrl = "FakeStringValue" - } } } From e089ba5629c0e19350b99fee4f7db00b1ea8d2bd Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Wed, 23 Oct 2024 12:02:04 +0530 Subject: [PATCH 20/60] Adding more UTs --- ...ft365DSC.AADIdentityAPIConnector.Tests.ps1 | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 index 8fcde7f64b..d8484eef0b 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADIdentityAPIConnector.Tests.ps1 @@ -225,6 +225,60 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { } } + + Context -Name "The AADIdentityAPIConnector with certificates exists and values are in the desired state" -Fixture { + + BeforeAll { + $testParams = @{ + DisplayName = 'FakeStringValue' + TargetUrl = 'FakeStringValue' + Id = 'FakeStringValue' + Certificates = @( + New-CimInstance -ClassName 'MSFT_AADIdentityAPIConnectionCertificate' -Property @{ + Thumbprint = 'FakeStringValue' + Pkcs12Value = (New-CimInstance -ClassName 'MSFT_Credential' -Property @{ + Username = 'FakeStringValue' + Password = 'FakeStringValue' + } -ClientOnly) + Password = (New-CimInstance -ClassName 'MSFT_Credential' -Property @{ + Username = 'FakeStringValue' + Password = 'FakeStringValue' + } -ClientOnly) + IsActive = $true + } -ClientOnly + ) + Credential = $Credential + Ensure = 'Present' + } + + Mock -CommandName Get-MgBetaIdentityAPIConnector -MockWith { + return @{ + DisplayName = 'FakeStringValue' + TargetUrl = 'FakeStringValue' + Id = 'FakeStringValue' + AuthenticationConfiguration = @{ + AdditionalProperties = @{ + certificateList = @( + @{ + Thumbprint = 'FakeStringValue' + IsActive = $true + } + ) + } + } + } + } + } + + 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 $true + } + } + Context -Name 'ReverseDSC Tests' -Fixture { BeforeAll { $Global:CurrentModeIsExport = $true From d482791bbe1b8788fb8dd1dd7af226f70962c931 Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Wed, 23 Oct 2024 12:25:51 +0530 Subject: [PATCH 21/60] Adding examples --- .../AADIdentityAPIConnector/1-Create.ps1 | 37 +++++++++++++++++++ .../AADIdentityAPIConnector/2-Update.ps1 | 37 +++++++++++++++++++ .../AADIdentityAPIConnector/3-Remove.ps1 | 37 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/1-Create.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/2-Update.ps1 create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/3-Remove.ps1 diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/1-Create.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/1-Create.ps1 new file mode 100644 index 0000000000..7382692428 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/1-Create.ps1 @@ -0,0 +1,37 @@ +<# +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 + { + AADIdentityAPIConnector 'AADIdentityAPIConnector-TestConnector' + { + DisplayName = "NewTestConnector"; + Id = "RestApi_NewTestConnector"; + Username = "anexas"; + Password = New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString "anexas" -AsPlainText -Force)); + TargetUrl = "https://graph.microsoft.com"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/2-Update.ps1 new file mode 100644 index 0000000000..d123d0151f --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/2-Update.ps1 @@ -0,0 +1,37 @@ +<# +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 + { + AADIdentityAPIConnector 'AADIdentityAPIConnector-TestConnector' + { + DisplayName = "NewTestConnector"; + Id = "RestApi_NewTestConnector"; + Username = "anexas 1"; #drift + Password = New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString "anexas" -AsPlainText -Force)); + TargetUrl = "https://graph.microsoft.com"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/3-Remove.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/3-Remove.ps1 new file mode 100644 index 0000000000..926a36404f --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADIdentityAPIConnector/3-Remove.ps1 @@ -0,0 +1,37 @@ +<# +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 + { + AADIdentityAPIConnector 'AADIdentityAPIConnector-TestConnector' + { + DisplayName = "NewTestConnector"; + Id = "RestApi_NewTestConnector"; + Username = "anexas"; + Password = New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString "anexas" -AsPlainText -Force)); + TargetUrl = "https://graph.microsoft.com"; + Ensure = "Absent" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} From 69c06dde52ef3d7189a9d474a7db91529a3c7f3a Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Wed, 23 Oct 2024 09:40:22 +0200 Subject: [PATCH 22/60] Remove property from merge conflict --- .../MSFT_IntuneMobileAppsMacOSLobApp.schema.mof | 2 +- .../Resources/IntuneMobileAppsMacOSLobApp/2-Update.ps1 | 1 - .../Microsoft365DSC.IntuneMobileAppsMacOSLobApp.Tests.ps1 | 4 ---- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsMacOSLobApp/MSFT_IntuneMobileAppsMacOSLobApp.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsMacOSLobApp/MSFT_IntuneMobileAppsMacOSLobApp.schema.mof index ce7f2b865f..ec3e29882d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsMacOSLobApp/MSFT_IntuneMobileAppsMacOSLobApp.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneMobileAppsMacOSLobApp/MSFT_IntuneMobileAppsMacOSLobApp.schema.mof @@ -57,12 +57,12 @@ class MSFT_IntuneMobileAppsMacOSLobApp : OMI_BaseResource [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("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; [Write, Description("List of Scope Tag IDs for mobile app.")] String RoleScopeTagIds[]; [Write, Description("Whether to ignore the version of the app or not.")] Boolean IgnoreVersionDetection; + [Write, Description("Install the app as managed. Requires macOS 11.0.")] Boolean InstallAsManaged; [Write, Description("The icon for this app."), EmbeddedInstance("MSFT_DeviceManagementMimeContent")] String LargeIcon; [Write, Description("The minimum supported operating system to install the app."), EmbeddedInstance("MSFT_DeviceManagementMinimumOperatingSystem")] String MinimumSupportedOperatingSystem; [Write, Description("The list of categories for this app."), EmbeddedInstance("MSFT_DeviceManagementMobileAppCategory")] String Categories[]; diff --git a/Modules/Microsoft365DSC/Examples/Resources/IntuneMobileAppsMacOSLobApp/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/IntuneMobileAppsMacOSLobApp/2-Update.ps1 index f7746f29b1..4bb88786ac 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/IntuneMobileAppsMacOSLobApp/2-Update.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/IntuneMobileAppsMacOSLobApp/2-Update.ps1 @@ -38,7 +38,6 @@ Configuration Example Owner = ""; PrivacyInformationUrl = ""; Publisher = "Contoso"; - PublishingState = "published"; Assignments = @( MSFT_DeviceManagementMobileAppAssignment { groupDisplayName = 'All devices' diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneMobileAppsMacOSLobApp.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneMobileAppsMacOSLobApp.Tests.ps1 index abfda47b65..0e5552d5fd 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneMobileAppsMacOSLobApp.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.IntuneMobileAppsMacOSLobApp.Tests.ps1 @@ -138,7 +138,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Owner = "" PrivacyInformationUrl = "" Publisher = "Contoso" - PublishingState = "published" RoleScopeTagIds = @() IgnoreVersionDetection = $True AdditionalProperties = @{ @@ -204,7 +203,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Owner = "" PrivacyInformationUrl = "" Publisher = "Contoso" - PublishingState = "published" RoleScopeTagIds = @() AdditionalProperties = @{ '@odata.type' = '#microsoft.graph.macOSLobApp' @@ -260,7 +258,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Owner = "" PrivacyInformationUrl = "" Publisher = "Contoso" - PublishingState = "published" AdditionalProperties = @{ '@odata.type' = '#microsoft.graph.macOSLobApp' minimumSupportedOperatingSystem = @{ @@ -307,7 +304,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Owner = "" PrivacyInformationUrl = "" Publisher = "Contoso" - PublishingState = "published" RoleScopeTagIds = @() AdditionalProperties = @{ '@odata.type' = '#microsoft.graph.macOSLobApp' From b46b69e79d082349159ad729a77572e7ee720559 Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Wed, 23 Oct 2024 14:28:10 +0530 Subject: [PATCH 23/60] Updating change log --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 756b3e9f19..235fb7d6f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * Initial release. * AADIdentityGovernanceProgram * Initial release. +* AADIdentityAPIConnector + * Initial release. * SPOTenantSettings * Added support for AllowSelectSGsInODBListInTenant, DenySelectSGsInODBListInTenant, DenySelectSecurityGroupsInSPSitesList, From 72c5ac2c9380f204504c0ff5f763242a69f62465 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 10:06:09 +0000 Subject: [PATCH 24/60] Updated Resources and Cmdlet documentation pages --- docs/docs/resources/intune/IntuneMobileAppsMacOSLobApp.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/docs/resources/intune/IntuneMobileAppsMacOSLobApp.md b/docs/docs/resources/intune/IntuneMobileAppsMacOSLobApp.md index 2d7d5b3efe..da90863d02 100644 --- a/docs/docs/resources/intune/IntuneMobileAppsMacOSLobApp.md +++ b/docs/docs/resources/intune/IntuneMobileAppsMacOSLobApp.md @@ -14,12 +14,12 @@ | **Owner** | Write | String | The owner of the app. Inherited from mobileApp. | | | **PrivacyInformationUrl** | Write | String | The privacy statement Url. Inherited from mobileApp. | | | **Publisher** | Write | String | The publisher of the app. Inherited from mobileApp. | | -| **PublishingState** | Write | String | The publishing state for the app. The app cannot be assigned unless the app is published. Inherited from mobileApp. | `notPublished`, `processing`, `published` | | **BundleId** | Write | String | The bundleId of the app. | | | **BuildNumber** | Write | String | The build number of the app. | | | **VersionNumber** | Write | String | The version number of the app. | | | **RoleScopeTagIds** | Write | StringArray[] | List of Scope Tag IDs for mobile app. | | | **IgnoreVersionDetection** | Write | Boolean | Whether to ignore the version of the app or not. | | +| **InstallAsManaged** | Write | Boolean | Install the app as managed. Requires macOS 11.0. | | | **LargeIcon** | Write | MSFT_DeviceManagementMimeContent | The icon for this app. | | | **MinimumSupportedOperatingSystem** | Write | MSFT_DeviceManagementMinimumOperatingSystem | The minimum supported operating system to install the app. | | | **Categories** | Write | MSFT_DeviceManagementMobileAppCategory[] | The list of categories for this app. | | @@ -234,7 +234,6 @@ Configuration Example Owner = ""; PrivacyInformationUrl = ""; Publisher = "Contoso"; - PublishingState = "published"; Assignments = @( MSFT_DeviceManagementMobileAppAssignment { groupDisplayName = 'All devices' From 3b89b8da348d2065dac25d2276aaaa7ab736abd7 Mon Sep 17 00:00:00 2001 From: "Arpita Mohapatra (from Dev Box)" Date: Wed, 23 Oct 2024 15:46:14 +0530 Subject: [PATCH 25/60] Resolved comments --- ...MSFT_EXOMailboxAuditBypassAssociation.psm1 | 17 -------- ...XOMailboxAuditBypassAssociation.schema.mof | 1 - .../settings.json | 3 +- .../2-Update.ps1 | 5 ++- ...EXOMailboxAuditBypassAssociation.Tests.ps1 | 39 ------------------- 5 files changed, 5 insertions(+), 60 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.psm1 index b38f64321c..0d2fc291bf 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.psm1 @@ -12,11 +12,6 @@ function Get-TargetResource [System.Boolean] $AuditBypassEnabled, - [Parameter()] - [ValidateSet('Present', 'Absent')] - [System.String] - $Ensure, - [Parameter()] [System.Management.Automation.PSCredential] $Credential, @@ -58,7 +53,6 @@ function Get-TargetResource #endregion $nullResult = $PSBoundParameters - $nullResult.Ensure = 'Absent' try { if ($null -ne $Script:exportedInstances -and $Script:ExportMode) @@ -77,7 +71,6 @@ function Get-TargetResource $results = @{ Identity = [System.String]$Identity AuditBypassEnabled = [System.Boolean]$instance.AuditBypassEnabled - Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId @@ -112,11 +105,6 @@ function Set-TargetResource [System.Boolean] $AuditBypassEnabled, - [Parameter()] - [ValidateSet('Present', 'Absent')] - [System.String] - $Ensure, - [Parameter()] [System.Management.Automation.PSCredential] $Credential, @@ -175,11 +163,6 @@ function Test-TargetResource [System.Boolean] $AuditBypassEnabled, - [Parameter()] - [ValidateSet('Present', 'Absent')] - [System.String] - $Ensure, - [Parameter()] [System.Management.Automation.PSCredential] $Credential, diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.schema.mof index 59381b5ba7..318977aa0b 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/MSFT_EXOMailboxAuditBypassAssociation.schema.mof @@ -3,7 +3,6 @@ class MSFT_EXOMailboxAuditBypassAssociation : OMI_BaseResource { [Key, Description("The Identity parameter specifies the user account or computer account where you want to view the value of the AuditBypassEnabled property.")] String Identity; [Write, Description("The AuditBypassEnabled parameter specifies whether audit bypass is enabled for the user or computer.")] Boolean AuditBypassEnabled; - [Write, Description("Specifies if this instance should exist."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] 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; diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json index 35a9d6c51d..476fa8a84d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOMailboxAuditBypassAssociation/settings.json @@ -28,7 +28,8 @@ ], "requiredrolegroups": [ "Organization Management", - "Compliance Management" + "Compliance Management", + "Records Management" ] } } diff --git a/Modules/Microsoft365DSC/Examples/Resources/EXOMailboxAuditBypassAssociation/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/EXOMailboxAuditBypassAssociation/2-Update.ps1 index 0d7c78206c..ede62c46c9 100644 --- a/Modules/Microsoft365DSC/Examples/Resources/EXOMailboxAuditBypassAssociation/2-Update.ps1 +++ b/Modules/Microsoft365DSC/Examples/Resources/EXOMailboxAuditBypassAssociation/2-Update.ps1 @@ -26,9 +26,10 @@ Configuration Example EXOMailboxAuditBypassAssociation "EXOMailboxAuditBypassAssociation-Test" { AuditBypassEnabled = $True; #Updated Property - Credential = $Credscredential; - Ensure = "Present"; Identity = "TestMailbox109"; + ApplicationId = $ApplicationId; + TenantId = $TenantId; + CertificateThumbprint = $CertificateThumbprint; } } } diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOMailboxAuditBypassAssociation.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOMailboxAuditBypassAssociation.Tests.ps1 index 882ae49894..8e71aa877c 100644 --- a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOMailboxAuditBypassAssociation.Tests.ps1 +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.EXOMailboxAuditBypassAssociation.Tests.ps1 @@ -52,7 +52,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { $testParams = @{ AuditBypassEnabled = $False; Credential = $Credscredential; - Ensure = "Present"; Identity = "TestMailbox109"; } @@ -60,7 +59,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { return @{ AuditBypassEnabled = $True; #Drift Credential = $Credscredential; - Ensure = "Present"; Identity = "TestMailbox109"; } } @@ -74,10 +72,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { Set-TargetResource @testParams Should -Invoke -CommandName Set-MailboxAuditBypassAssociation -Exactly 1 } - - It 'Should return Present from the Get method' { - (Get-TargetResource @testParams).Ensure | Should -Be 'Present' - } } Context -Name 'Settings are already in the desired state' -Fixture { @@ -85,7 +79,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { $testParams = @{ AuditBypassEnabled = $False; Credential = $Credscredential; - Ensure = "Present"; Identity = "TestMailbox109"; } @@ -93,7 +86,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { return @{ AuditBypassEnabled = $False; Credential = $Credscredential; - Ensure = "Present"; Identity = "TestMailbox109"; } } @@ -102,38 +94,8 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { It 'Should return false from the Test method' { Test-TargetResource @testParams | Should -Be $true } - - It 'Should return Present from the Get method' { - (Get-TargetResource @testParams).Ensure | Should -Be 'Present' - } } - Context -Name "User doesn't exist" -Fixture { - BeforeAll { - $testParams = @{ - AuditBypassEnabled = $False; - Credential = $Credscredential; - Ensure = "Present"; - Identity = "TestMailbox109"; - } - - Mock -CommandName Get-MailboxAuditBypassAssociation -MockWith { - return $null - } - } - - It 'Should return false from the Test method' { - Test-TargetResource @testParams | Should -Be $false - } - - It 'Should return Absent from the Get method' { - (Get-TargetResource @testParams).Ensure | Should -Be 'Absent' - } - } - - - - Context -Name 'ReverseDSC Tests' -Fixture { BeforeAll { $Global:CurrentModeIsExport = $true @@ -146,7 +108,6 @@ Describe -Name $Global:DscHelper.DescribeHeader -Fixture { return @{ AuditBypassEnabled = $False; Credential = $Credscredential; - Ensure = "Present"; Identity = "TestMailbox109"; } } From 62d1fdc7bf9ff98eaa8ea3b1fede87a861c70e06 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 10:18:05 +0000 Subject: [PATCH 26/60] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 33d014f36c..89ecaf7dc0 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -36862,11 +36862,6 @@ "Name": "Publisher", "Option": "Write" }, - { - "CIMType": "String", - "Name": "PublishingState", - "Option": "Write" - }, { "CIMType": "String", "Name": "BundleId", @@ -36892,6 +36887,11 @@ "Name": "IgnoreVersionDetection", "Option": "Write" }, + { + "CIMType": "Boolean", + "Name": "InstallAsManaged", + "Option": "Write" + }, { "CIMType": "MSFT_DeviceManagementMimeContent", "Name": "LargeIcon", From b9a5da6236c5abbf4c3c8d6024d740fbd065771e Mon Sep 17 00:00:00 2001 From: "Arpita Mohapatra (from Dev Box)" Date: Wed, 23 Oct 2024 16:15:27 +0530 Subject: [PATCH 27/60] Added changelog.md file --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da4bf910c4..33bc3f85e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ * Initial release. * AADSocialIdentityProvider * Fixed missing permissions in settings.json +* EXOMailboxAuditBypassAssociation + * Initial release. * Intune workload * Fixed missing permissions in settings.json * SentinelAlertRule From fb6810fa0243adc176211f9b74418c30665b56aa Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Wed, 23 Oct 2024 12:45:48 +0200 Subject: [PATCH 28/60] Fix autofromconnector type for Intune EDR Windows10 policy --- CHANGELOG.md | 3 +++ ...ointDetectionAndResponsePolicyWindows10.psm1 | 17 +++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da4bf910c4..885caaa405 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ * Initial release. * AADSocialIdentityProvider * Fixed missing permissions in settings.json +* IntuneEndpointDetectionAndResponsePolicyWindows10 + * Fixes an issue with `AutoFromConnector` as the Configuration package type. + FIXES [#5246](https://github.com/microsoft/Microsoft365DSC/issues/5246) * Intune workload * Fixed missing permissions in settings.json * SentinelAlertRule diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 index a7a6ad3a6e..a861287001 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 @@ -126,18 +126,11 @@ function Get-TargetResource $policySettings = @{} $policySettings = Export-IntuneSettingCatalogPolicySettings -Settings $settings -ReturnHashtable $policySettings - if ($policySettings.ClientConfigurationPackageType -eq 'onboarding_fromconnector') - { - $policySettings.Add('ConfigurationType', 'AutoFromConnector') - } - else - { - $policySettings.Add('ConfigurationType', $policySettings.ClientConfigurationPackageType) - } + $policySettings.Add('ConfigurationType', $policySettings.ClientConfigurationPackageType) $policySettings.Remove('ClientConfigurationPackageType') $policySettings.Remove('onboarding') $policySettings.Remove('offboarding') - $policySettings.Remove('onboarding_fromconnector') + $policySettings.Remove('autofromconnector') # Removing TelemetryReportingFrequency because it's deprecated and doesn't need to be evaluated and enforced $policySettings.Remove('telemetryreportingfrequency') @@ -273,8 +266,8 @@ function Set-TargetResource { 'AutoFromConnector' { - $BoundParameters.Add('ClientConfigurationPackageType', 'onboarding_fromconnector') - $BoundParameters.Add('onboarding_fromconnector', $ConfigurationBlob) + $BoundParameters.Add('ClientConfigurationPackageType', 'autofromconnector') + $BoundParameters.Add('autofromconnector', 'autoConnectPlaceholder') $BoundParameters.Remove('ConfigurationBlob') | Out-Null } 'Onboard' @@ -291,7 +284,7 @@ function Set-TargetResource } } - if ([System.String]::IsNullOrEmpty($ConfigurationBlob)) + if ($ConfigurationType -ne 'AutoFromConnector' -and [System.String]::IsNullOrEmpty($ConfigurationBlob)) { throw "ConfigurationBlob is required for configurationType '$($DSCParams.ConfigurationType)'" } From 8bea1c3758db359815ff00b8624a464e3d7c903a Mon Sep 17 00:00:00 2001 From: Piyush Dubey Date: Wed, 23 Oct 2024 16:19:42 +0530 Subject: [PATCH 29/60] minor --- .../MSFT_AADServicePrincipal.psm1 | 5 +---- Tests/Unit/Stubs/Microsoft365.psm1 | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 index 85a0ee97c7..cee000347d 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADServicePrincipal/MSFT_AADServicePrincipal.psm1 @@ -428,7 +428,7 @@ function Set-TargetResource if ($null -ne $currentParameters.CustomSecurityAttributes -and $currentParameters.CustomSecurityAttributes -gt 0) { $currentParameters.CustomSecurityAttributes = Get-M365DSCAADServicePrincipalCustomSecurityAttributesAsCmdletHashtable -CustomSecurityAttributes $currentParameters.CustomSecurityAttributes } else { - $currentParameters.CustomSecurityAttributes = @() + $currentParameters.Remove('CustomSecurityAttributes') } # ServicePrincipal should exist but it doesn't @@ -879,9 +879,6 @@ function Export-TargetResource -ErrorAction Stop foreach ($AADServicePrincipal in $Script:exportedInstances) { - if($AADServicePrincipal.AppId -ne '0b750897-174f-4aed-ad21-ab3799c7e404') { - continue - } if ($null -ne $Global:M365DSCExportResourceInstancesCount) { $Global:M365DSCExportResourceInstancesCount++ diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index 816dc46565..f0d7eb3b2e 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -17378,6 +17378,10 @@ function New-MgServicePrincipal [PSObject] $DelegatedPermissionClassifications, + [Parameter()] + [System.Collections.Hashtable] + $CustomSecurityAttributes, + [Parameter()] [PSObject] $PasswordCredentials, @@ -18023,6 +18027,10 @@ function Update-MgServicePrincipal [PSObject] $DelegatedPermissionClassifications, + [Parameter()] + [System.Collections.Hashtable] + $CustomSecurityAttributes, + [Parameter()] [PSObject] $PasswordCredentials, @@ -50034,6 +50042,10 @@ function New-MgServicePrincipal [PSObject] $DelegatedPermissionClassifications, + [Parameter()] + [System.Collections.Hashtable] + $CustomSecurityAttributes, + [Parameter()] [PSObject] $PasswordCredentials, @@ -50679,6 +50691,10 @@ function Update-MgServicePrincipal [PSObject] $DelegatedPermissionClassifications, + [Parameter()] + [System.Collections.Hashtable] + $CustomSecurityAttributes, + [Parameter()] [PSObject] $PasswordCredentials, From 0365d28af199e7e6405cc578242a22ce338d85c9 Mon Sep 17 00:00:00 2001 From: "Kartikeya Saxena (from Dev Box)" Date: Wed, 23 Oct 2024 16:27:15 +0530 Subject: [PATCH 30/60] Added Stub files --- Tests/Unit/Stubs/Microsoft365.psm1 | 264 +++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index 7fc7c2ef08..0c9e839660 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -98721,4 +98721,268 @@ function Remove-MgBetaOrganizationCertificateBasedAuthConfiguration ) } +function Update-MgBetaIdentityApiConnector +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.Collections.Hashtable] + $AuthenticationConfiguration, + + [Parameter()] + [System.String] + $IdentityApiConnectorId, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.String] + $TargetUrl, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject] + $HttpPipelineAppend + ) +} + +function New-MgBetaIdentityApiConnector +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.Collections.Hashtable] + $AuthenticationConfiguration, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.String] + $TargetUrl, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject] + $HttpPipelineAppend + ) +} + +function Remove-MgBetaIdentityApiConnector +{ + [CmdletBinding()] + param( + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $PassThru, + + [Parameter()] + [System.String] + $IfMatch, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [PSObject] + $HttpPipelineAppend, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [System.String] + $IdentityApiConnectorId, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break + ) +} + +function Invoke-MgBetaUploadIdentityApiConnectorClientCertificate +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Password, + + [Parameter()] + [System.String] + $IdentityApiConnectorId, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.String] + $Pkcs12Value, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject] + $HttpPipelineAppend + ) +} + #endregion From 86b790e96084cbf7119b20c0d05b777cc80ed345 Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Wed, 23 Oct 2024 13:03:33 +0200 Subject: [PATCH 31/60] Fix property name on creation and update --- .../MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 index a861287001..505e591d58 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 @@ -267,7 +267,7 @@ function Set-TargetResource 'AutoFromConnector' { $BoundParameters.Add('ClientConfigurationPackageType', 'autofromconnector') - $BoundParameters.Add('autofromconnector', 'autoConnectPlaceholder') + $BoundParameters.Add('onboarding_fromconnector', 'autoConnectPlaceholder') $BoundParameters.Remove('ConfigurationBlob') | Out-Null } 'Onboard' From 1badbec356bc0d6c8727df3fbfe31f2dbee31079 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 11:40:13 +0000 Subject: [PATCH 32/60] Updated Resources and Cmdlet documentation pages --- .../resources/azure-ad/AADServicePrincipal.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/docs/resources/azure-ad/AADServicePrincipal.md b/docs/docs/resources/azure-ad/AADServicePrincipal.md index fc48241314..6e564a95c8 100644 --- a/docs/docs/resources/azure-ad/AADServicePrincipal.md +++ b/docs/docs/resources/azure-ad/AADServicePrincipal.md @@ -22,6 +22,7 @@ | **ServicePrincipalType** | Write | String | The type of the service principal. | | | **Tags** | Write | StringArray[] | Tags linked to this service principal.Note that if you intend for this service principal to show up in the All Applications list in the admin portal, you need to set this value to {WindowsAzureActiveDirectoryIntegratedApp} | | | **DelegatedPermissionClassifications** | Write | MSFT_AADServicePrincipalDelegatedPermissionClassification[] | The permission classifications for delegated permissions exposed by the app that this service principal represents. | | +| **CustomSecurityAttributes** | Write | MSFT_AADServicePrincipalAttributeSet[] | The list of custom security attributes attached to this SPN | | | **Ensure** | Write | String | Specify if the Azure AD App should exist or not. | `Present`, `Absent` | | **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | | **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | @@ -49,6 +50,28 @@ | **Classification** | Write | String | Classification of the delegated permission | `low`, `medium`, `high` | | **PermissionName** | Write | String | Name of the permission | | +### MSFT_AADServicePrincipalAttributeValue + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **AttributeName** | Write | String | Name of the Attribute | | +| **StringArrayValue** | Write | StringArray[] | If the attribute has a string array value | | +| **IntArrayValue** | Write | UInt32Array[] | If the attribute has a int array value | | +| **StringValue** | Write | String | If the attribute has a string value | | +| **IntValue** | Write | UInt32 | If the attribute has a int value | | +| **BoolValue** | Write | Boolean | If the attribute has a boolean value | | + +### MSFT_AADServicePrincipalAttributeSet + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **AttributeSetName** | Write | String | Attribute Set Name. | | +| **AttributeValues** | Write | MSFT_AADServicePrincipalAttributeValue[] | List of attribute values. | | + ## Description This resource configures an Azure Active Directory ServicePrincipal. From 67bd706cd7166b813036d485d0e91b332f24a79d Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 11:42:39 +0000 Subject: [PATCH 33/60] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 89ecaf7dc0..34aef93300 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -6924,6 +6924,56 @@ } ] }, + { + "ClassName": "MSFT_AADServicePrincipalAttributeValue", + "Parameters": [ + { + "CIMType": "String", + "Name": "AttributeName", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "StringArrayValue", + "Option": "Write" + }, + { + "CIMType": "UInt32[]", + "Name": "IntArrayValue", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "StringValue", + "Option": "Write" + }, + { + "CIMType": "UInt32", + "Name": "IntValue", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "BoolValue", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_AADServicePrincipalAttributeSet", + "Parameters": [ + { + "CIMType": "String", + "Name": "AttributeSetName", + "Option": "Write" + }, + { + "CIMType": "MSFT_AADServicePrincipalAttributeValue[]", + "Name": "AttributeValues", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_AADServicePrincipal", "Parameters": [ @@ -7017,6 +7067,11 @@ "Name": "DelegatedPermissionClassifications", "Option": "Write" }, + { + "CIMType": "MSFT_AADServicePrincipalAttributeSet[]", + "Name": "CustomSecurityAttributes", + "Option": "Write" + }, { "CIMType": "String", "Name": "Ensure", From d6ca82013a2849462e72580df064041242be055c Mon Sep 17 00:00:00 2001 From: Fabien Tschanz Date: Wed, 23 Oct 2024 14:01:48 +0200 Subject: [PATCH 34/60] Fix throw message for non-existing bound parameters --- .../MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 index 505e591d58..987f41812b 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10/MSFT_IntuneEndpointDetectionAndResponsePolicyWindows10.psm1 @@ -286,7 +286,7 @@ function Set-TargetResource if ($ConfigurationType -ne 'AutoFromConnector' -and [System.String]::IsNullOrEmpty($ConfigurationBlob)) { - throw "ConfigurationBlob is required for configurationType '$($DSCParams.ConfigurationType)'" + throw "ConfigurationBlob is required for configurationType '$($ConfigurationType)'" } $BoundParameters.Remove('ConfigurationType') | Out-Null From 093ef2bcdf7095e22021299c9d7dcf2ea2e0e9fd Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 12:03:52 +0000 Subject: [PATCH 35/60] Updated Resources and Cmdlet documentation pages --- .../azure-ad/AADIdentityAPIConnector.md | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 docs/docs/resources/azure-ad/AADIdentityAPIConnector.md diff --git a/docs/docs/resources/azure-ad/AADIdentityAPIConnector.md b/docs/docs/resources/azure-ad/AADIdentityAPIConnector.md new file mode 100644 index 0000000000..914b4b7df6 --- /dev/null +++ b/docs/docs/resources/azure-ad/AADIdentityAPIConnector.md @@ -0,0 +1,185 @@ +# AADIdentityAPIConnector + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **DisplayName** | Required | String | The name of the API connector. | | +| **TargetUrl** | Write | String | The URL of the API endpoint to call. | | +| **Id** | Key | String | The unique identifier for an entity. Read-only. | | +| **Username** | Write | String | The username of the password | | +| **Password** | Write | PSCredential | The password of certificate/basic auth | | +| **Certificates** | Write | MSFT_AADIdentityAPIConnectionCertificate[] | List of certificates to be used in the API connector | | +| **Ensure** | Write | String | Present ensures the policy exists, absent ensures it is removed. | `Present`, `Absent` | +| **Credential** | Write | PSCredential | Credentials of the Admin | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **ApplicationSecret** | Write | PSCredential | Secret of the Azure Active Directory tenant used for authentication. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | +| **AccessTokens** | Write | StringArray[] | Access token used for authentication. | | + +### MSFT_AADIdentityAPIConnectionCertificate + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Pkcs12Value** | Write | PSCredential | Pkcs12Value of the certificate as a secure string in Base64 encoding | | +| **Thumbprint** | Write | String | Thumbprint of the certificate in Base64 encoding | | +| **Password** | Write | PSCredential | Password of the certificate as a secure string | | +| **IsActive** | Write | Boolean | Tells if the certificate is in use or not | | + + +## Description + +Azure AD Identity API Connector + +## Permissions + +### Microsoft Graph + +To authenticate with the Microsoft Graph API, this resource required the following permissions: + +#### Delegated permissions + +- **Read** + + - None + +- **Update** + + - None + +#### Application permissions + +- **Read** + + - None + +- **Update** + + - None + +## Examples + +### Example 1 + +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + AADIdentityAPIConnector 'AADIdentityAPIConnector-TestConnector' + { + DisplayName = "NewTestConnector"; + Id = "RestApi_NewTestConnector"; + Username = "anexas"; + Password = New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString "anexas" -AsPlainText -Force)); + TargetUrl = "https://graph.microsoft.com"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} +``` + +### Example 2 + +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + AADIdentityAPIConnector 'AADIdentityAPIConnector-TestConnector' + { + DisplayName = "NewTestConnector"; + Id = "RestApi_NewTestConnector"; + Username = "anexas 1"; #drift + Password = New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString "anexas" -AsPlainText -Force)); + TargetUrl = "https://graph.microsoft.com"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} +``` + +### Example 3 + +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + AADIdentityAPIConnector 'AADIdentityAPIConnector-TestConnector' + { + DisplayName = "NewTestConnector"; + Id = "RestApi_NewTestConnector"; + Username = "anexas"; + Password = New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString "anexas" -AsPlainText -Force)); + TargetUrl = "https://graph.microsoft.com"; + Ensure = "Absent" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } + } +} +``` + From 2c5029f421e93cce05f413c396e9d74b09a67ca2 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 12:10:32 +0000 Subject: [PATCH 36/60] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 34aef93300..1d6b236341 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -5559,6 +5559,106 @@ } ] }, + { + "ClassName": "MSFT_AADIdentityAPIConnectionCertificate", + "Parameters": [ + { + "CIMType": "MSFT_Credential", + "Name": "Pkcs12Value", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Thumbprint", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Password", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "IsActive", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_AADIdentityAPIConnector", + "Parameters": [ + { + "CIMType": "String", + "Name": "DisplayName", + "Option": "Required" + }, + { + "CIMType": "String", + "Name": "TargetUrl", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "Id", + "Option": "Key" + }, + { + "CIMType": "String", + "Name": "Username", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Password", + "Option": "Write" + }, + { + "CIMType": "MSFT_AADIdentityAPIConnectionCertificate[]", + "Name": "Certificates", + "Option": "Write" + }, + { + "CIMType": "string", + "Name": "Ensure", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Credential", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ApplicationId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TenantId", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "ApplicationSecret", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificateThumbprint", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "ManagedIdentity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "AccessTokens", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_MicrosoftGraphUserFlowApiConnectorConfiguration", "Parameters": [ From c5793def0a77612c6eafcbab16cbd2a72114642a Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 12:25:05 +0000 Subject: [PATCH 37/60] Updated Resources and Cmdlet documentation pages --- .../EXOMailboxAuditBypassAssociation.md | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 docs/docs/resources/exchange/EXOMailboxAuditBypassAssociation.md diff --git a/docs/docs/resources/exchange/EXOMailboxAuditBypassAssociation.md b/docs/docs/resources/exchange/EXOMailboxAuditBypassAssociation.md new file mode 100644 index 0000000000..cb604b3bed --- /dev/null +++ b/docs/docs/resources/exchange/EXOMailboxAuditBypassAssociation.md @@ -0,0 +1,73 @@ +# EXOMailboxAuditBypassAssociation + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Identity** | Key | String | The Identity parameter specifies the user account or computer account where you want to view the value of the AuditBypassEnabled property. | | +| **AuditBypassEnabled** | Write | Boolean | The AuditBypassEnabled parameter specifies whether audit bypass is enabled for the user or computer. | | +| **Credential** | Write | PSCredential | Credentials of the workload's Admin | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | +| **AccessTokens** | Write | StringArray[] | Access token used for authentication. | | + +## Description + +Use the Set-MailboxAuditBypassAssociation cmdlet to configure mailbox audit logging bypass for user or computer accounts such as service accounts for applications that access mailboxes frequently. + +## Permissions + +### Exchange + +To authenticate with Microsoft Exchange, this resource required the following permissions: + +#### Roles + +- Compliance Admin, View-Only Configuration, Journaling + +#### Role Groups + +- Organization Management, Compliance Management, Records Management + +## Examples + +### Example 1 + +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + + Import-DscResource -ModuleName Microsoft365DSC + + node localhost + { + EXOMailboxAuditBypassAssociation "EXOMailboxAuditBypassAssociation-Test" + { + AuditBypassEnabled = $True; #Updated Property + Identity = "TestMailbox109"; + ApplicationId = $ApplicationId; + TenantId = $TenantId; + CertificateThumbprint = $CertificateThumbprint; + } + } +} +``` + From 877a14bd39e20ac4aefefa081d9e9203a37c36ce Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 12:25:33 +0000 Subject: [PATCH 38/60] Updated {Create} AAD Integration Tests --- .../M365DSCIntegration.AAD.Create.Tests.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Create.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Create.Tests.ps1 index a1bb9f2373..5d87e99d81 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Create.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Create.Tests.ps1 @@ -504,6 +504,18 @@ TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint } + AADIdentityAPIConnector 'AADIdentityAPIConnector-TestConnector' + { + DisplayName = "NewTestConnector"; + Id = "RestApi_NewTestConnector"; + Username = "anexas"; + Password = New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString "anexas" -AsPlainText -Force)); + TargetUrl = "https://graph.microsoft.com"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } AADIdentityB2XUserFlow 'AADIdentityB2XUserFlow-B2X_1_TestFlow' { ApplicationId = $ApplicationId From ba6279a1194362daaab6effd83eb60ea63997e6c Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 12:25:57 +0000 Subject: [PATCH 39/60] Updated {Update} AAD Integration Tests --- .../M365DSCIntegration.AAD.Update.Tests.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Update.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Update.Tests.ps1 index c02ddde277..a7082349e2 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Update.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Update.Tests.ps1 @@ -1005,6 +1005,18 @@ TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint } + AADIdentityAPIConnector 'AADIdentityAPIConnector-TestConnector' + { + DisplayName = "NewTestConnector"; + Id = "RestApi_NewTestConnector"; + Username = "anexas 1"; #drift + Password = New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString "anexas" -AsPlainText -Force)); + TargetUrl = "https://graph.microsoft.com"; + Ensure = "Present" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } AADIdentityB2XUserFlow 'AADIdentityB2XUserFlow-B2X_1_TestFlow' { ApplicationId = $ApplicationId From cc9f7f4bd94fedc077ba3e7b76ccd0cc48989802 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 12:26:16 +0000 Subject: [PATCH 40/60] Updated {Update} AAD Integration Tests --- .../M365DSCIntegration.AAD.Remove.Tests.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Remove.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Remove.Tests.ps1 index dc4a4a0fb6..77b24db586 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Remove.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Remove.Tests.ps1 @@ -312,6 +312,18 @@ TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint } + AADIdentityAPIConnector 'AADIdentityAPIConnector-TestConnector' + { + DisplayName = "NewTestConnector"; + Id = "RestApi_NewTestConnector"; + Username = "anexas"; + Password = New-Object System.Management.Automation.PSCredential('Password', (ConvertTo-SecureString "anexas" -AsPlainText -Force)); + TargetUrl = "https://graph.microsoft.com"; + Ensure = "Absent" + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + } AADIdentityB2XUserFlow 'AADIdentityB2XUserFlow-B2X_1_TestFlow' { ApplicationId = $ApplicationId From ad4f8d84ee6eb58dc1a26ce4f35f17d056494f8b Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 12:28:04 +0000 Subject: [PATCH 41/60] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index 1d6b236341..b1d3c54e71 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -12549,6 +12549,51 @@ } ] }, + { + "ClassName": "MSFT_EXOMailboxAuditBypassAssociation", + "Parameters": [ + { + "CIMType": "String", + "Name": "Identity", + "Option": "Key" + }, + { + "CIMType": "Boolean", + "Name": "AuditBypassEnabled", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Credential", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ApplicationId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TenantId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificateThumbprint", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "ManagedIdentity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "AccessTokens", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_EXOMailboxAutoReplyConfiguration", "Parameters": [ From cbe5a16c113582a9a35ca3034c5baa4602ee393e Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 12:29:18 +0000 Subject: [PATCH 42/60] Updated {Update} EXO Integration Tests --- .../M365DSCIntegration.EXO.Update.Tests.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 index 30dc923214..d5ef0c950b 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.EXO.Update.Tests.ps1 @@ -607,6 +607,14 @@ TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint } + EXOMailboxAuditBypassAssociation 'EXOMailboxAuditBypassAssociation-Test' + { + AuditBypassEnabled = $True; #Updated Property + Identity = "TestMailbox109"; + ApplicationId = $ApplicationId; + TenantId = $TenantId; + CertificateThumbprint = $CertificateThumbprint; + } EXOMailboxAutoReplyConfiguration 'EXOMailboxAutoReplyConfiguration' { AutoDeclineFutureRequestsWhenOOF = $False; From 1cdc364e865f39545967049b368ec3016bcc4763 Mon Sep 17 00:00:00 2001 From: Riyansh Goyal Date: Wed, 23 Oct 2024 20:05:10 +0530 Subject: [PATCH 43/60] removed ensure --- ...SFT_AADNetworkAccessForwardingProfile.psm1 | 127 ++++++++---------- ...DNetworkAccessForwardingProfile.schema.mof | 3 +- 2 files changed, 59 insertions(+), 71 deletions(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 index c0f158cd0f..c06ba0ed46 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 @@ -22,12 +22,6 @@ function Get-TargetResource $Policies, #endregion - - [Parameter()] - [System.String] - [ValidateSet('Absent', 'Present')] - $Ensure = 'Present', - [Parameter()] [System.Management.Automation.PSCredential] $Credential, @@ -75,7 +69,6 @@ function Get-TargetResource #endregion $nullResult = $PSBoundParameters - $nullResult.Ensure = 'Absent' $getValue = $null #region resource generator code @@ -83,6 +76,7 @@ function Get-TargetResource { $getValue = Get-MgBetaNetworkAccessForwardingProfile -ForwardingProfileId $Id -ErrorAction SilentlyContinue } + if ($null -eq $getValue) { Write-Verbose -Message "Could not find an Azure AD Network Access Forwarding Profile with Id:{$Id}" @@ -97,7 +91,7 @@ function Get-TargetResource #endregion if ($null -eq $getValue) { - Write-Verbose -Message "Could not find an Azure AD Network Access Forwarding Profile with {$Name}." + Write-Verbose -Message "Could not find an Azure AD Network Access Forwarding Profile with name {$Name}." return $nullResult } @@ -115,7 +109,7 @@ function Get-TargetResource $myPolicies = @{} $myPolicies.Add('Name', $currentPolicy.Policy.Name) $myPolicies.Add('State', $currentPolicy.State) - $myPolicies.Add('Id', $currentPolicy.Id) + $myPolicies.Add('PolicyLinkId', $currentPolicy.Id) if ($myPolicies.values.Where({ $null -ne $_ }).Count -gt 0) { $complexPolicies += $myPolicies @@ -129,7 +123,6 @@ function Get-TargetResource State = $getValue.State Policies = $complexPolicies # 'Policies@Odata.Context' = $getValue.AdditionalProperties['policies@odata.context'] - Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId @@ -175,11 +168,6 @@ function Set-TargetResource $Policies, #endregion - [Parameter()] - [System.String] - [ValidateSet('Absent', 'Present')] - $Ensure = 'Present', - [Parameter()] [System.Management.Automation.PSCredential] $Credential, @@ -212,52 +200,59 @@ function Set-TargetResource # 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 - - # $BoundParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters - - - # if ( $Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') - # { - # Write-Warning -Message "Can not create the Azure AD Network Access Forwarding Profile with {$($currentInstance.Name)}." - # } - # elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') - # { - # Write-Verbose -Message "Updating the Azure AD Network Access Forwarding Profile with {$($currentInstance.Id)}" - - # $updateParameters = ([Hashtable]$BoundParameters).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 - # Update-MgBetaNetworkAccessForwardingProfile ` - # -ForwardingProfileId $Id ` - # -BodyParameter $UpdateParameters - # #endregion - # } - # elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') - # { - # Write-Warning -Message "Can not remove the Azure AD Network Access Forwarding Profile with {$($currentInstance.Name)}." - # } + #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 + + $BoundParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters + + if ($null -ne $currentInstance) + { + Write-Verbose -Message "Updating the Azure AD Network Access Forwarding Profile with {$($currentInstance.Id)}" + + $updateParameters = ([Hashtable]$BoundParameters).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 + } + } + Write-Verbose -Message "Updating the Azure AD Network Access Forwarding Profile with {$($currentInstance.Id)} {$($currentInstance.Name)} State" + Update-MgBetaNetworkAccessForwardingProfile ` + -ForwardingProfileId $currentInstance.Id ` + -State $updateParameters.State + + $currentPolicies = $currentInstance.Policies + $updatedPolicies = $updateParameters.Policies + + # update the current policy's state with the updated policy's state. + foreach ($currentPolicy in $currentPolicies) + { + $updatedPolicy = $updatedPolicies | Where-Object { $_.Name -eq $currentPolicy.Name } + if ($null -ne $updatedPolicy) + { + Write-Verbose -Message "Updating the Azure AD Network Access Forwarding Profile Policy with Id {$($currentPolicy.PolicyLinkId)} {$($currentPolicy.Name)}" + Update-MgBetaNetworkAccessForwardingProfilePolicy ` + -ForwardingProfileId $currentInstance.Id ` + -PolicyLinkId $currentPolicy.PolicyLinkId ` + -State $updatedPolicy.State + } + } + #endregion + } } function Test-TargetResource @@ -286,11 +281,6 @@ function Test-TargetResource #endregion - [Parameter()] - [System.String] - [ValidateSet('Absent', 'Present')] - $Ensure = 'Present', - [Parameter()] [System.Management.Automation.PSCredential] $Credential, @@ -337,7 +327,7 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).clone() - if ($CurrentValues.Ensure -ne $Ensure) + if ($null -eq $CurrentValues) { Write-Verbose -Message "Test-TargetResource returned $false" return $false @@ -467,7 +457,6 @@ function Export-TargetResource $params = @{ Id = $config.Id Name = $config.Name - Ensure = 'Present' Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId @@ -535,7 +524,7 @@ function Get-PoliciesAsString { $StringContent += "MSFT_MicrosoftGraphNetworkaccessPolicyLink {`r`n" $StringContent += " State = '" + $policy.State + "'`r`n" - $StringContent += " Id = '" + $policy.Id + "'`r`n" + $StringContent += " PolicyLinkId = '" + $policy.PolicyLinkId + "'`r`n" $StringContent += " Name = '" + $policy.Name + "'`r`n" $StringContent += " }`r`n" } diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.schema.mof index 939b48bdb3..4bd1149ec8 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.schema.mof @@ -3,7 +3,7 @@ class MSFT_MicrosoftGraphNetworkaccessPolicyLink { [Write, Description("Policy Name. Required")] String Name; - [Write, Description("A Unique Identifier")] String Id; + [Write, Description("Policy Link Id")] String PolicyLinkId; [Write, Description("status")] String state; }; @@ -14,7 +14,6 @@ class MSFT_AADNetworkAccessForwardingProfile : OMI_BaseResource [Write, Description("Id of the profile. Unique Identifier")] String Id; [Write, Description("status of the profile")] String State; [Write, Description("Traffic forwarding policies associated with this profile."), EmbeddedInstance("MSFT_MicrosoftGraphNetworkaccessPolicyLink")] String Policies[]; - [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; From cd690c5dfd75c555e926b56c662e327f3143797b Mon Sep 17 00:00:00 2001 From: Ricardo Mestre Date: Wed, 23 Oct 2024 16:22:21 +0100 Subject: [PATCH 44/60] Fix retrieval of resource --- CHANGELOG.md | 4 ++ .../MSFT_IntuneAppCategory.psm1 | 68 ++++++++++++------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 855ee044d2..5a77b54a44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ * Fixed missing permissions in settings.json * EXOMailboxAuditBypassAssociation * Initial release. +* IntuneAppCategory + * Fixed retrieval of resource which could then result in multiple categories + being created with same name + * Added a few verbose messages * Intune workload * Fixed missing permissions in settings.json * SentinelAlertRule diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneAppCategory/MSFT_IntuneAppCategory.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneAppCategory/MSFT_IntuneAppCategory.psm1 index bb74801759..479083759f 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneAppCategory/MSFT_IntuneAppCategory.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneAppCategory/MSFT_IntuneAppCategory.psm1 @@ -78,25 +78,25 @@ function Get-TargetResource if ($null -eq $instance) { - $instance = Get-MgBetaDeviceAppManagementMobileAppCategory -MobileAppCategoryId $Id -ErrorAction Stop - - if ($null -eq $instance) - { - Write-Verbose -Message "Could not find MobileAppCategory by Id {$Id}." - - if (-Not [string]::IsNullOrEmpty($DisplayName)) - { - $instance = Get-MgBetaDeviceAppManagementMobileAppConfiguration ` - -Filter "DisplayName eq '$DisplayName'" ` - -ErrorAction SilentlyContinue - } - } - - if ($null -eq $instance) - { - Write-Verbose -Message "Could not find MobileAppCategory by DisplayName {$DisplayName}." - return $nullResult - } + $instance = Get-MgBetaDeviceAppManagementMobileAppCategory -MobileAppCategoryId $Id -ErrorAction SilentlyContinue + + if ($null -eq $instance) + { + Write-Verbose -Message "Could not find MobileAppCategory by Id {$Id}." + + if (-Not [string]::IsNullOrEmpty($DisplayName)) + { + $instance = Get-MgBetaDeviceAppManagementMobileAppCategory ` + -Filter "DisplayName eq '$DisplayName'" ` + -ErrorAction SilentlyContinue + } + } + + if ($null -eq $instance) + { + Write-Verbose -Message "Could not find MobileAppCategory by DisplayName {$DisplayName}." + return $nullResult + } } $results = @{ @@ -192,22 +192,27 @@ function Set-TargetResource $currentInstance = Get-TargetResource @PSBoundParameters $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters - $setParameters.remove('Id') | Out-Null - $setParameters.remove('Ensure') | Out-Null + $setParameters.Remove('Id') | Out-Null # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { + Write-Verbose -Message "Creating an Intune App Category with DisplayName {$DisplayName}" + New-MgBetaDeviceAppManagementMobileAppCategory @SetParameters } # UPDATE elseif ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Present') { + Write-Verbose -Message "Updating the Intune App Category with DisplayName {$DisplayName}" + Update-MgBetaDeviceAppManagementMobileAppCategory -MobileAppCategoryId $currentInstance.Id @SetParameters } # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') { + Write-Verbose -Message "Removing the Intune App Category with DisplayName {$DisplayName}" + Remove-MgBetaDeviceAppManagementMobileAppCategory -MobileAppCategoryId $currentInstance.Id -Confirm:$false } } @@ -279,13 +284,26 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + if ($CurrentValues.Ensure -ne $Ensure) + { + Write-Verbose -Message "Test-TargetResource returned $false" + return $false + } + $testResult = $true + + $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)" - $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" From c05da57fd7997ded049156e57abb99b868858076 Mon Sep 17 00:00:00 2001 From: Ricardo Mestre Date: Wed, 23 Oct 2024 17:21:50 +0100 Subject: [PATCH 45/60] Fix export and deployment --- CHANGELOG.md | 5 ++ .../MSFT_IntuneDerivedCredential.psm1 | 55 ++++++++++++------- .../MSFT_IntuneDerivedCredential.schema.mof | 4 +- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 855ee044d2..233b60a18f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ * Fixed missing permissions in settings.json * EXOMailboxAuditBypassAssociation * Initial release. +* IntuneDerivedCredential + * Fixed export and deployment when `NotificateType` had more than one option + selected + * Fixed retrieval of resource when it cannot be found by `Id` + * Added a few verbose messages * Intune workload * Fixed missing permissions in settings.json * SentinelAlertRule diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDerivedCredential/MSFT_IntuneDerivedCredential.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDerivedCredential/MSFT_IntuneDerivedCredential.psm1 index af4ecd222e..a3208e9ae9 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDerivedCredential/MSFT_IntuneDerivedCredential.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDerivedCredential/MSFT_IntuneDerivedCredential.psm1 @@ -23,7 +23,7 @@ function Get-TargetResource { $Issuer, [Parameter()] - [ValidateSet('none', 'email', 'companyPortal')] + [ValidateSet('none', 'email', 'companyPortal', 'companyPortal,email')] [System.String] $NotificationType = 'none', @@ -96,17 +96,18 @@ function Get-TargetResource { if ($null -eq $instance) { - $instance = Get-MgBetaDeviceManagementDerivedCredential -DeviceManagementDerivedCredentialSettingsId $Id -ErrorAction Stop + $instance = Get-MgBetaDeviceManagementDerivedCredential -DeviceManagementDerivedCredentialSettingsId $Id -ErrorAction SilentlyContinue - if ($null -eq $instance) - { - Write-Verbose -Message "Could not find Derived Credential by Id {$Id}." + if ($null -eq $instance) + { + Write-Verbose -Message "Could not find Derived Credential by Id {$Id}." + + if (-Not [string]::IsNullOrEmpty($DisplayName)) + { + $instance = Get-MgBetaDeviceManagementDerivedCredential ` + -Filter "DisplayName eq '$DisplayName'" ` + -ErrorAction SilentlyContinue - if (-Not [string]::IsNullOrEmpty($DisplayName)) - { - $instance = Get-MgBetaDeviceManagementDerivedCredential ` - -Filter "DisplayName eq '$DisplayName'" ` - -ErrorAction SilentlyContinue if ($null -eq $instance) { Write-Verbose -Message "Could not find Derived Credential by DisplayName {$DisplayName}." @@ -178,7 +179,7 @@ function Set-TargetResource { #endregion resource params [Parameter()] - [ValidateSet('none', 'email', 'companyPortal')] + [ValidateSet('none', 'email', 'companyPortal', 'companyPortal,email')] [System.String] $NotificationType = 'none', @@ -231,18 +232,21 @@ function Set-TargetResource { $currentInstance = Get-TargetResource @PSBoundParameters $setParameters = Remove-M365DSCAuthenticationParameter -BoundParameters $PSBoundParameters - $setParameters.remove('Id') | Out-Null - $setParameters.remove('Ensure') | Out-Null + $setParameters.Remove('Id') | Out-Null # CREATE if ($Ensure -eq 'Present' -and $currentInstance.Ensure -eq 'Absent') { + Write-Verbose -Message "Creating an Intune Derived Credential with DisplayName {$DisplayName}" + New-MgBetaDeviceManagementDerivedCredential @SetParameters } # UPDATE is not supported API, it always creates a new Derived Credential instance # REMOVE elseif ($Ensure -eq 'Absent' -and $currentInstance.Ensure -eq 'Present') { + Write-Verbose -Message "Removing the Intune Derived Credential with DisplayName {$DisplayName}" + Remove-MgBetaDeviceManagementDerivedCredential -DeviceManagementDerivedCredentialSettingsId $currentInstance.Id -Confirm:$false } } @@ -272,7 +276,7 @@ function Test-TargetResource { $Issuer, [Parameter()] - [ValidateSet('none', 'email', 'companyPortal')] + [ValidateSet('none', 'email', 'companyPortal', 'companyPortal,email')] [System.String] $NotificationType = 'none', @@ -330,13 +334,26 @@ function Test-TargetResource { $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() + if ($CurrentValues.Ensure -ne $Ensure) + { + Write-Verbose -Message "Test-TargetResource returned $false" + return $false + } + $testResult = $true + + $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)" - $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" @@ -368,7 +385,7 @@ function Export-TargetResource { $Issuer, [Parameter()] - [ValidateSet('none', 'email', 'companyPortal')] + [ValidateSet('none', 'email', 'companyPortal', 'companyPortal,email')] [System.String] $NotificationType = 'none', diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDerivedCredential/MSFT_IntuneDerivedCredential.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDerivedCredential/MSFT_IntuneDerivedCredential.schema.mof index e893173409..b04d7ad2b8 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDerivedCredential/MSFT_IntuneDerivedCredential.schema.mof +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_IntuneDerivedCredential/MSFT_IntuneDerivedCredential.schema.mof @@ -12,8 +12,8 @@ class MSFT_IntuneDerivedCredential : OMI_BaseResource String Issuer; [Write, Description("Supported values for the notification type to use."), - ValueMap{"none", "email", "companyPortal"}, - Values{"none", "email", "companyPortal"}] + ValueMap{"none", "email", "companyPortal", "companyPortal,email"}, + Values{"none", "email", "companyPortal", "companyPortal,email"}] String NotificationType; [Write, Description("Supported values for the notification type to use."), From 228714e7a902feb4feb8e76128df0442f71703b2 Mon Sep 17 00:00:00 2001 From: Ricardo Mestre Date: Wed, 23 Oct 2024 17:22:12 +0100 Subject: [PATCH 46/60] Fix typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 233b60a18f..394b4af421 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ * EXOMailboxAuditBypassAssociation * Initial release. * IntuneDerivedCredential - * Fixed export and deployment when `NotificateType` had more than one option + * Fixed export and deployment when `NotificationType` had more than one option selected * Fixed retrieval of resource when it cannot be found by `Id` * Added a few verbose messages From 04f0c1cd243c96ee11dd83ed2f3fa2c15f61984a Mon Sep 17 00:00:00 2001 From: Riyansh Goyal Date: Wed, 23 Oct 2024 22:29:42 +0530 Subject: [PATCH 47/60] Added UTs --- ...SFT_AADNetworkAccessForwardingProfile.psm1 | 2 +- ...ADNetworkAccessForwardingProfile.Tests.ps1 | 222 +++++++++++ Tests/Unit/Stubs/Microsoft365.psm1 | 357 ++++++++++++++++++ 3 files changed, 580 insertions(+), 1 deletion(-) create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADNetworkAccessForwardingProfile.Tests.ps1 diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 index c06ba0ed46..346290bfd6 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 @@ -87,7 +87,6 @@ function Get-TargetResource } } - #endregion if ($null -eq $getValue) { @@ -98,6 +97,7 @@ function Get-TargetResource Write-Verbose -Message "An Azure AD Network Access Forwarding Profile with {$Id} and {$Name} was found" $forwardingProfilePolicies = Get-MgBetaNetworkAccessForwardingProfilePolicy -ForwardingProfileId $getValue.Id -ErrorAction SilentlyContinue + if ($null -ne $forwardingProfilePolicies) { Write-Verbose -Message "An Azure AD Network Access Forwarding Profile Policy with $($forwardingProfilePolicies.Id) and $($forwardingProfilePolicies.Name) was found" diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADNetworkAccessForwardingProfile.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADNetworkAccessForwardingProfile.Tests.ps1 new file mode 100644 index 0000000000..45026a3cb0 --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADNetworkAccessForwardingProfile.Tests.ps1 @@ -0,0 +1,222 @@ +[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 'AADNetworkAccessForwardingProfile' -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 Get-PSSession -MockWith { + } + + Mock -CommandName Remove-PSSession -MockWith { + } + + Mock -CommandName Update-MgBetaNetworkAccessForwardingProfile -MockWith { + } + + Mock -CommandName Get-MgBetaNetworkAccessForwardingProfile -MockWith { + } + + Mock -CommandName Get-MgBetaNetworkAccessForwardingProfilePolicy -MockWith { + } + + Mock -CommandName Update-MgBetaNetworkAccessForwardingProfilePolicy -MockWith { + } + + Mock -CommandName New-M365DSCConnection -MockWith { + return 'Credentials' + } + + # 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 AADNetworkAccessForwardingProfile Exists and Values are already in the desired state' -Fixture { + BeforeAll { + $testParams = @{ + Name = 'Microsoft 365 traffic forwarding profile' + Id = '58847306-0ae2-4f65-91ee-d6587e9bebda' + State = 'enabled' + Policies = @( + New-CimInstance -ClassName MSFT_MicrosoftGraphNetworkaccessPolicyLink -Property @{ + Name = 'Custom Bypass' + PolicyLinkId = '58847306-0ae2-4f65-91ee-d6587e9bebda' + State = 'enabled' + } -ClientOnly + New-CimInstance -ClassName MSFT_MicrosoftGraphNetworkaccessPolicyLink -Property @{ + Name = 'Default Bypass' + PolicyLinkId = '12345678-1234-1234-1234-123456789012' + State = 'enabled' + } -ClientOnly + ) + Credential = $Credential + + } + + Mock -CommandName Get-MgBetaNetworkAccessForwardingProfile -MockWith { + return @{ + Name = 'Microsoft 365 traffic forwarding profile' + Id = '58847306-0ae2-4f65-91ee-d6587e9bebda' + State = 'enabled' + } + } + + Mock -CommandName Get-MgBetaNetworkAccessForwardingProfilePolicy -MockWith { + return @( + @{ + Policy = @{ + Name = 'Custom Bypass' + } + Id = '58847306-0ae2-4f65-91ee-d6587e9bebda' + State = 'enabled' + }, + @{ + Policy = @{ + Name = 'Default Bypass' + } + Id = '12345678-1234-1234-1234-123456789012' + State = 'enabled' + } + ) + } + } + + + It 'Should return true from the Test method' { + Test-TargetResource @testParams | Should -Be $true + } + } + + Context -Name 'The AADNetworkAccessForwardingProfile exists and values are NOT in the desired state' -Fixture { + BeforeAll { + $testParams = @{ + Name = 'Microsoft 365 traffic forwarding profile' + Id = '58847306-0ae2-4f65-91ee-d6587e9bebda' + State = 'disabled' + Policies = @( + New-CimInstance -ClassName MSFT_MicrosoftGraphNetworkaccessPolicyLink -Property @{ + Name = 'Custom Bypass' + PolicyLinkId = '58847306-0ae2-4f65-91ee-d6587e9bebda' + State = 'enabled' + } -ClientOnly + New-CimInstance -ClassName MSFT_MicrosoftGraphNetworkaccessPolicyLink -Property @{ + Name = 'Default Bypass' + PolicyLinkId = '12345678-1234-1234-1234-123456789012' + State = 'disabled' + } -ClientOnly + ) + Credential = $Credential + } + + Mock -CommandName Get-MgBetaNetworkAccessForwardingProfile -MockWith { + return @{ + Name = 'Microsoft 365 traffic forwarding profile' + Id = '58847306-0ae2-4f65-91ee-d6587e9bebda' + State = 'disabled' + } + } + + Mock -CommandName Get-MgBetaNetworkAccessForwardingProfilePolicy -MockWith { + return @( + @{ + Policy = @{ + Name = 'Custom Bypass' + } + Id = '58847306-0ae2-4f65-91ee-d6587e9bebda' + State = 'disabled' + }, + @{ + Policy = @{ + Name = 'Default Bypass' + } + Id = '12345678-1234-1234-1234-123456789012' + State = 'enabled' + } + ) + } + } + + It 'Should return false from the Test method' { + Test-TargetResource @testParams | Should -Be $false + } + + It 'Should call the Set Update-MgBetaNetworkAccessForwardingProfile method' { + Set-TargetResource @testParams + Should -Invoke -CommandName Update-MgBetaNetworkAccessForwardingProfile -Exactly 1 + } + + It 'Should call the Set Update-MgBetaNetworkAccessForwardingProfilePolicy method' { + Set-TargetResource @testParams + Should -Invoke -CommandName Update-MgBetaNetworkAccessForwardingProfilePolicy -Exactly 2 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential + } + + Mock -CommandName Get-MgBetaNetworkAccessForwardingProfile -MockWith { + return @{ + Name = 'Microsoft 365 traffic forwarding profile' + Id = '58847306-0ae2-4f65-91ee-d6587e9bebda' + State = 'disabled' + } + } + + Mock -CommandName Get-MgBetaNetworkAccessForwardingProfilePolicy -MockWith { + return @( + @{ + Policy = @{ + Name = 'Custom Bypass' + } + Id = '58847306-0ae2-4f65-91ee-d6587e9bebda' + State = 'disabled' + }, + @{ + Policy = @{ + Name = 'Default Bypass' + } + Id = '12345678-1234-1234-1234-123456789012' + State = 'enabled' + } + ) + } + } + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index cdcf592740..b89f560fb5 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -97638,3 +97638,360 @@ function Invoke-PnPSPRestMethod $Content ) } +#region MgBetaNetworkAccessForwardingProfile +function Get-MgBetaNetworkAccessForwardingProfile +{ + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $ForwardingProfileId, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.String[]] + $ExpandProperty, + + [Parameter()] + [System.String[]] + $Property, + + [Parameter()] + [System.String] + $Filter, + + [Parameter()] + [System.String] + $Search, + + [Parameter()] + [System.Int32] + $Skip, + + [Parameter()] + [System.String[]] + $Sort, + + [Parameter()] + [System.Int32] + $Top, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject[]] + $HttpPipelineAppend, + + [Parameter()] + [PSObject[]] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Int32] + $PageSize, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $All, + + [Parameter()] + [System.String] + $CountVariable + ) +} + +function Update-MgBetaNetworkAccessForwardingProfile +{ + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $ForwardingProfileId, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [PSObject[]] + $Associations, + + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.DateTime] + $LastModifiedDateTime, + + [Parameter()] + [System.String] + $Name, + + [Parameter()] + [PSObject[]] + $Policies, + + [Parameter()] + [System.Int32] + $Priority, + + [Parameter()] + [PSObject] + $ServicePrincipal, + + [Parameter()] + [System.String] + $State, + + [Parameter()] + [System.String] + $TrafficForwardingType, + + [Parameter()] + [System.String] + $Version, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject[]] + $HttpPipelineAppend, + + [Parameter()] + [PSObject[]] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm + ) +} + +#endregion + +#region MgBetaNetworkAccessForwardingProfilePolicy +function Get-MgBetaNetworkAccessForwardingProfilePolicy +{ + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $ForwardingProfileId, + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [System.String[]] + $ExpandProperty, + + [Parameter()] + [System.String[]] + $Property, + + [Parameter()] + [System.String] + $Filter, + + [Parameter()] + [System.String] + $Search, + + [Parameter()] + [System.Int32] + $Skip, + + [Parameter()] + [System.String[]] + $Sort, + + [Parameter()] + [System.Int32] + $Top, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject[]] + $HttpPipelineAppend, + + [Parameter()] + [PSObject[]] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Int32] + $PageSize, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $All, + + [Parameter()] + [System.String] + $CountVariable + ) +} + +function Update-MgBetaNetworkAccessForwardingProfilePolicy +{ + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $ForwardingProfileId, + + [Parameter()] + [System.String] + $PolicyLinkId , + + [Parameter()] + [PSObject] + $InputObject, + + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.String] + $State, + + [Parameter()] + [System.String] + $Version, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject[]] + $HttpPipelineAppend, + + [Parameter()] + [PSObject[]] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm + ) +} + +#endregion From 10574d5e7ae4fbd1787d6c5724ee1b45dd117cb1 Mon Sep 17 00:00:00 2001 From: Riyansh Goyal Date: Wed, 23 Oct 2024 22:34:31 +0530 Subject: [PATCH 48/60] added changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 855ee044d2..b6f223b1bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * AADAccessReviewDefinition * Initial release. +* AADNetworkAccessForwardingProfile + * Initial release. * AADAuthenticationMethodPolicyExternal * Initial release. * AADCustomSecurityAttributeDefinition From 39a48c0c66c795a0ec3dd7396c1fb324c475dfcb Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Wed, 23 Oct 2024 23:08:32 +0000 Subject: [PATCH 49/60] Updated Resources and Cmdlet documentation pages --- docs/docs/resources/intune/IntuneDerivedCredential.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/resources/intune/IntuneDerivedCredential.md b/docs/docs/resources/intune/IntuneDerivedCredential.md index c43b79bec9..b42c76a61e 100644 --- a/docs/docs/resources/intune/IntuneDerivedCredential.md +++ b/docs/docs/resources/intune/IntuneDerivedCredential.md @@ -9,7 +9,7 @@ | **HelpUrl** | Write | String | The URL that will be accessible to end users as they retrieve a derived credential using the Company Portal. | | | **RenewalThresholdPercentage** | Write | UInt32 | The nominal percentage of time before certificate renewal is initiated by the client. | | | **Issuer** | Write | String | Supported values for the derived credential issuer. | `intercede`, `entrustDatacard`, `purebred` | -| **NotificationType** | Write | String | Supported values for the notification type to use. | `none`, `email`, `companyPortal` | +| **NotificationType** | Write | String | Supported values for the notification type to use. | `none`, `email`, `companyPortal`, `companyPortal,email` | | **Ensure** | Write | String | Supported values for the notification type to use. | `Present`, `Absent` | | **Credential** | Write | PSCredential | Credentials of the Intune Admin | | | **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | From dd6a3be921c908e4f83f01798bfc359b865dbbec Mon Sep 17 00:00:00 2001 From: Riyansh Goyal Date: Thu, 24 Oct 2024 10:14:45 +0530 Subject: [PATCH 50/60] added example --- .../Update.ps1 | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADNetworkAccessForwardingProfile/Update.ps1 diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADNetworkAccessForwardingProfile/Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADNetworkAccessForwardingProfile/Update.ps1 new file mode 100644 index 0000000000..2a7431f519 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADNetworkAccessForwardingProfile/Update.ps1 @@ -0,0 +1,50 @@ +# Generated with Microsoft365DSC version 1.24.1016.1 +# For additional information on how to use Microsoft365DSC, please visit https://aka.ms/M365DSC + +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + + Import-DscResource -ModuleName 'Microsoft365DSC' + + Node localhost + { + AADNetworkAccessForwardingProfile "AADNetworkAccessForwardingProfile-Internet traffic forwarding profile" + { + + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + Name = "Internet traffic forwarding profile"; + Policies = @(MSFT_MicrosoftGraphNetworkaccessPolicyLink { + State = 'disabled' + PolicyLinkId = 'f8a43f3f-3f44-4738-8025-088bb095a711' + Name = 'Custom Bypass' + } +MSFT_MicrosoftGraphNetworkaccessPolicyLink { + State = 'enabled' + PolicyLinkId = 'b45d1db0-9965-487b-afb1-f4d25174e9db' + Name = 'Default Bypass' + } +MSFT_MicrosoftGraphNetworkaccessPolicyLink { + State = 'enabled' + PolicyLinkId = 'dfd9cd59-90ca-44fc-b997-7cc71f08e438' + Name = 'Default Acquire' + } + ); + State = "disabled"; + } + } +} From 22f5da9fde693049ce470c45e3993b514040e015 Mon Sep 17 00:00:00 2001 From: Piyush Dubey Date: Thu, 24 Oct 2024 10:41:17 +0530 Subject: [PATCH 51/60] first commit --- CHANGELOG.md | 2 + .../MSFT_AADAccessReviewPolicy.psm1 | 330 ++++++++++++++++++ .../MSFT_AADAccessReviewPolicy.schema.mof | 13 + .../MSFT_AADAccessReviewPolicy/readme.md | 6 + .../MSFT_AADAccessReviewPolicy/settings.json | 28 ++ .../AADAccessReviewPolicy/2-Update.ps1 | 34 ++ ...soft365DSC.AADAccessReviewPolicy.Tests.ps1 | 118 +++++++ Tests/Unit/Stubs/Microsoft365.psm1 | 110 ++++++ 8 files changed, 641 insertions(+) create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/MSFT_AADAccessReviewPolicy.psm1 create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/MSFT_AADAccessReviewPolicy.schema.mof create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/readme.md create mode 100644 Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/settings.json create mode 100644 Modules/Microsoft365DSC/Examples/Resources/AADAccessReviewPolicy/2-Update.ps1 create mode 100644 Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAccessReviewPolicy.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 046e476c01..741bb95188 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * AADAccessReviewDefinition * Initial release. +* AADAccessReviewPolicy + * Initial release. * AADCustomSecurityAttributeDefinition * Fixed missing permissions in settings.json * AADIdentityB2XUserFlow diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/MSFT_AADAccessReviewPolicy.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/MSFT_AADAccessReviewPolicy.psm1 new file mode 100644 index 0000000000..226e9f3c22 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/MSFT_AADAccessReviewPolicy.psm1 @@ -0,0 +1,330 @@ +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $IsGroupOwnerManagementEnabled, + + [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 + ) + + New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -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 + try + { + $instance = Get-MgBetaPolicyAccessReviewPolicy -ErrorAction Stop + if ($null -eq $instance) + { + throw 'Could not retrieve the Access Review Policy' + } + + $results = @{ + IsSingleInstance = 'Yes' + IsGroupOwnerManagementEnabled = $instance.IsGroupOwnerManagementEnabled + 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 + ( + [Parameter(Mandatory = $true)] + [System.String] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $IsGroupOwnerManagementEnabled, + + [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 + + $updateParameters = @{ + IsGroupOwnerManagementEnabled = $IsGroupOwnerManagementEnabled + } + + $updateJSON = ConvertTo-Json $updateParameters + Write-Verbose -Message "Updating the Entra Id Access Review Policy with values: $updateJSON" + Update-MgBetaPolicyAccessReviewPolicy -BodyParameter $updateParameters +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $IsSingleInstance, + + [Parameter()] + [System.Boolean] + $IsGroupOwnerManagementEnabled, + + [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 + ) + + $ConnectionMode = New-M365DSCConnection -Workload 'MicrosoftGraph' ` + -InboundParameters $PSBoundParameters + + #Ensure the proper dependencies are installed in the current environment. + Confirm-M365DSCDependencies + + #region Telemetry + $ResourceName = $MyInvocation.MyCommand.ModuleName.Replace('MSFT_', '') + $CommandName = $MyInvocation.MyCommand + $data = Format-M365DSCTelemetryParameters -ResourceName $ResourceName ` + -CommandName $CommandName ` + -Parameters $PSBoundParameters + Add-M365DSCTelemetryEvent -Data $data + #endregion + + try + { + $Script:ExportMode = $true + [array] $Script:exportedInstances = Get-MgBetaPolicyAccessReviewPolicy -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 = 'Access Review Policy' + Write-Host " |---[$i/$($Script:exportedInstances.Count)] $displayedKey" -NoNewline + $params = @{ + IsSingleInstance = 'Yes' + 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_AADAccessReviewPolicy/MSFT_AADAccessReviewPolicy.schema.mof b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/MSFT_AADAccessReviewPolicy.schema.mof new file mode 100644 index 0000000000..a5ddb2612b --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/MSFT_AADAccessReviewPolicy.schema.mof @@ -0,0 +1,13 @@ +[ClassVersion("1.0.0.0"), FriendlyName("AADAccessReviewPolicy")] +class MSFT_AADAccessReviewPolicy : OMI_BaseResource +{ + [Key, Description("Only valid value is 'Yes'."), ValueMap{"Yes"}, Values{"Yes"}] String IsSingleInstance; + [Write, Description("If true, group owners can create and manage access reviews on groups they own.")] Boolean IsGroupOwnerManagementEnabled; + + [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_AADAccessReviewPolicy/readme.md b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/readme.md new file mode 100644 index 0000000000..70b59b7f25 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/readme.md @@ -0,0 +1,6 @@ + +# AADAccessReviewPolicy + +## Description + +Use this resource to monitor the access review policy object. diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/settings.json new file mode 100644 index 0000000000..9dd2ddf441 --- /dev/null +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/settings.json @@ -0,0 +1,28 @@ +{ + "resourceName": "AADAccessReviewPolicy", + "description": "Use this resource to monitor the access review policy object.", + "roles": { + "read": [], + "update": [] + }, + "permissions": { + "graph": { + "delegated": { + "read": [], + "update": [] + }, + "application": { + "read": [ + { + "name": "Policy.ReadWrite.AccessReview" + } + ], + "update": [ + { + "name": "Policy.ReadWrite.AccessReview" + } + ] + } + } + } +} diff --git a/Modules/Microsoft365DSC/Examples/Resources/AADAccessReviewPolicy/2-Update.ps1 b/Modules/Microsoft365DSC/Examples/Resources/AADAccessReviewPolicy/2-Update.ps1 new file mode 100644 index 0000000000..f69b86add3 --- /dev/null +++ b/Modules/Microsoft365DSC/Examples/Resources/AADAccessReviewPolicy/2-Update.ps1 @@ -0,0 +1,34 @@ +<# +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 + { + AADAccessReviewPolicy "AADAccessReviewPolicy" + { + ApplicationId = $ConfigurationData.NonNodeData.ApplicationId; + CertificateThumbprint = $ConfigurationData.NonNodeData.CertificateThumbprint; + IsGroupOwnerManagementEnabled = $False; + IsSingleInstance = "Yes"; + TenantId = $OrganizationName; + } + + } +} diff --git a/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAccessReviewPolicy.Tests.ps1 b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAccessReviewPolicy.Tests.ps1 new file mode 100644 index 0000000000..76d5306bcf --- /dev/null +++ b/Tests/Unit/Microsoft365DSC/Microsoft365DSC.AADAccessReviewPolicy.Tests.ps1 @@ -0,0 +1,118 @@ +[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" + } + + Mock -Command Get-MgBetaPolicyAccessReviewPolicy -MockWith { + } + + Mock -Command Update-MgBetaPolicyAccessReviewPolicy -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 exists and values are already in the desired state" -Fixture { + BeforeAll { + $testParams = @{ + IsSingleInstance = 'Yes' + IsGroupOwnerManagementEnabled = $True; + Credential = $Credential; + } + + Mock -CommandName Get-MgBetaPolicyAccessReviewPolicy -MockWith { + return @{ + IsGroupOwnerManagementEnabled = $True; + } + } + } + + 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 = @{ + IsSingleInstance = 'Yes' + IsGroupOwnerManagementEnabled = $True; + Credential = $Credential; + } + + Mock -CommandName Get-MgBetaPolicyAccessReviewPolicy -MockWith { + return @{ + IsGroupOwnerManagementEnabled = $False; + } + } + } + + 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-MgBetaPolicyAccessReviewPolicy -Exactly 1 + } + } + + Context -Name 'ReverseDSC Tests' -Fixture { + BeforeAll { + $Global:CurrentModeIsExport = $true + $Global:PartialExportFileName = "$(New-Guid).partial.ps1" + $testParams = @{ + Credential = $Credential; + } + + Mock -CommandName Get-MgBetaPolicyAccessReviewPolicy -MockWith { + return @{ + IsGroupOwnerManagementEnabled = $True; + } + } + } + It 'Should Reverse Engineer resource from the Export method' { + $result = Export-TargetResource @testParams + $result | Should -Not -BeNullOrEmpty + } + } + } +} + +Invoke-Command -ScriptBlock $Global:DscHelper.CleanupScript -NoNewScope diff --git a/Tests/Unit/Stubs/Microsoft365.psm1 b/Tests/Unit/Stubs/Microsoft365.psm1 index 7d0f828598..4faabd95d5 100644 --- a/Tests/Unit/Stubs/Microsoft365.psm1 +++ b/Tests/Unit/Stubs/Microsoft365.psm1 @@ -34965,6 +34965,51 @@ function Get-MgBetaIdentityConditionalAccessPolicy $HttpPipelineAppend ) } +function Get-MgBetaPolicyAccessReviewPolicy +{ + [CmdletBinding()] + param( + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.String[]] + $ExpandProperty, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [PSObject] + $HttpPipelineAppend, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [System.String[]] + $Property, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break + ) +} function Get-MgBetaIdentityProvider { [CmdletBinding()] @@ -37466,6 +37511,71 @@ function Update-MgBetaPolicyAuthenticationStrengthPolicyAllowedCombination $Break ) } +function Update-MgBetaPolicyAccessReviewPolicy +{ + [CmdletBinding()] + param( + [Parameter()] + [System.String] + $Description, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $IsGroupOwnerManagementEnabled, + + [Parameter()] + [System.String] + $DisplayName, + + [Parameter()] + [System.Collections.Hashtable] + $AdditionalProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ProxyUseDefaultCredentials, + + [Parameter()] + [PSObject] + $HttpPipelinePrepend, + + [Parameter()] + [System.Uri] + $Proxy, + + [Parameter()] + [PSObject] + $BodyParameter, + + [Parameter()] + [System.String] + $Id, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Confirm, + + [Parameter()] + [System.Management.Automation.PSCredential] + $ProxyCredential, + + [Parameter()] + [System.String] + $ResponseHeadersVariable, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Break, + + [Parameter()] + [System.Collections.IDictionary] + $Headers, + + [Parameter()] + [PSObject] + $HttpPipelineAppend + ) +} function Update-MgBetaPolicyAuthorizationPolicy { [CmdletBinding()] From 4c1de0410ef855c3c8329f10b05d86767576a8cc Mon Sep 17 00:00:00 2001 From: Riyansh Goyal Date: Thu, 24 Oct 2024 10:46:29 +0530 Subject: [PATCH 52/60] minor change --- .../MSFT_AADNetworkAccessForwardingProfile.psm1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 index 346290bfd6..a1e6c27217 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADNetworkAccessForwardingProfile/MSFT_AADNetworkAccessForwardingProfile.psm1 @@ -122,7 +122,6 @@ function Get-TargetResource Id = $getValue.Id State = $getValue.State Policies = $complexPolicies - # 'Policies@Odata.Context' = $getValue.AdditionalProperties['policies@odata.context'] Credential = $Credential ApplicationId = $ApplicationId TenantId = $TenantId From 290e4dd334ad25f77f7b870c60c5e9e17bb932ef Mon Sep 17 00:00:00 2001 From: Piyush Dubey Date: Thu, 24 Oct 2024 10:59:15 +0530 Subject: [PATCH 53/60] minor --- .../DSCResources/MSFT_AADAccessReviewPolicy/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/settings.json b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/settings.json index 9dd2ddf441..64be16a4f7 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/settings.json +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_AADAccessReviewPolicy/settings.json @@ -14,7 +14,7 @@ "application": { "read": [ { - "name": "Policy.ReadWrite.AccessReview" + "name": "Policy.Read.All" } ], "update": [ From c93afb835b6c342de808ab6fcd25509549ea3dbc Mon Sep 17 00:00:00 2001 From: Ricardo Mestre Date: Thu, 24 Oct 2024 11:27:59 +0100 Subject: [PATCH 54/60] Fix Test-TargetResource --- CHANGELOG.md | 2 ++ .../MSFT_EXOTenantAllowBlockListItems.psm1 | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 394b4af421..bfe3d92853 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ * Fixed missing permissions in settings.json * EXOMailboxAuditBypassAssociation * Initial release. +* EXOTenantAllowBlockListItems + * Fixed `Test-TargetResource` to correctly mark when this resource is removed * IntuneDerivedCredential * Fixed export and deployment when `NotificationType` had more than one option selected diff --git a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOTenantAllowBlockListItems/MSFT_EXOTenantAllowBlockListItems.psm1 b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOTenantAllowBlockListItems/MSFT_EXOTenantAllowBlockListItems.psm1 index 08fd6b85e3..01778e9c2b 100644 --- a/Modules/Microsoft365DSC/DSCResources/MSFT_EXOTenantAllowBlockListItems/MSFT_EXOTenantAllowBlockListItems.psm1 +++ b/Modules/Microsoft365DSC/DSCResources/MSFT_EXOTenantAllowBlockListItems/MSFT_EXOTenantAllowBlockListItems.psm1 @@ -378,18 +378,21 @@ function Test-TargetResource $CurrentValues = Get-TargetResource @PSBoundParameters $ValuesToCheck = ([Hashtable]$PSBoundParameters).Clone() - $ValuesToCheck.Remove('Entries') | Out-Null - if ($null -ne $ValuesToCheck.ExpirationDate -and $ValuesToCheck.ExpirationDate.Kind -eq 'Local') - { - $ValuesToCheck.ExpirationDate = $ValuesToCheck.ExpirationDate.ToUniversalTime().ToString() - } - if ($CurrentValues.Ensure -eq 'Absent') + if ($CurrentValues.Ensure -ne $Ensure) { Write-Verbose -Message "Test-TargetResource returned $false" return $false } + if ($null -ne $ValuesToCheck.ExpirationDate -and $ValuesToCheck.ExpirationDate.Kind -eq 'Local') + { + $ValuesToCheck.ExpirationDate = $ValuesToCheck.ExpirationDate.ToUniversalTime().ToString() + } + + $ValuesToCheck = Remove-M365DSCAuthenticationParameter -BoundParameters $ValuesToCheck + $ValuesToCheck.Remove('Entries') | Out-Null + Write-Verbose -Message "Current Values: $(Convert-M365DscHashtableToString -Hashtable $CurrentValues)" Write-Verbose -Message "Target Values: $(Convert-M365DscHashtableToString -Hashtable $ValuesToCheck)" From 755ad91a547d95b1cd947ec7d150eb87b1e47507 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 24 Oct 2024 10:44:44 +0000 Subject: [PATCH 55/60] Updated Resources and Cmdlet documentation pages --- .../azure-ad/AADAccessReviewPolicy.md | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 docs/docs/resources/azure-ad/AADAccessReviewPolicy.md diff --git a/docs/docs/resources/azure-ad/AADAccessReviewPolicy.md b/docs/docs/resources/azure-ad/AADAccessReviewPolicy.md new file mode 100644 index 0000000000..51e25ff402 --- /dev/null +++ b/docs/docs/resources/azure-ad/AADAccessReviewPolicy.md @@ -0,0 +1,85 @@ +# AADAccessReviewPolicy + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **IsSingleInstance** | Key | String | Only valid value is 'Yes'. | `Yes` | +| **IsGroupOwnerManagementEnabled** | Write | Boolean | If true, group owners can create and manage access reviews on groups they own. | | +| **Credential** | Write | PSCredential | Credentials of the workload's Admin | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | +| **AccessTokens** | Write | StringArray[] | Access token used for authentication. | | + + +## Description + +Use this resource to monitor the access review policy object. + +## Permissions + +### Microsoft Graph + +To authenticate with the Microsoft Graph API, this resource required the following permissions: + +#### Delegated permissions + +- **Read** + + - None + +- **Update** + + - None + +#### Application permissions + +- **Read** + + - Policy.Read.All + +- **Update** + + - Policy.ReadWrite.AccessReview + +## Examples + +### Example 1 + +This example is used to test new resources and showcase the usage of new resources being worked on. +It is not meant to use as a production baseline. + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + Import-DscResource -ModuleName Microsoft365DSC + node localhost + { + AADAccessReviewPolicy "AADAccessReviewPolicy" + { + ApplicationId = $ConfigurationData.NonNodeData.ApplicationId; + CertificateThumbprint = $ConfigurationData.NonNodeData.CertificateThumbprint; + IsGroupOwnerManagementEnabled = $False; + IsSingleInstance = "Yes"; + TenantId = $OrganizationName; + } + + } +} +``` + From 40feb76cefaf8f341a7935aa39359a8c1ef823ca Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 24 Oct 2024 10:45:11 +0000 Subject: [PATCH 56/60] Updated {Update} AAD Integration Tests --- .../M365DSCIntegration.AAD.Update.Tests.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Update.Tests.ps1 b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Update.Tests.ps1 index a7082349e2..cfc7c836f5 100644 --- a/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Update.Tests.ps1 +++ b/Tests/Integration/Microsoft365DSC/M365DSCIntegration.AAD.Update.Tests.ps1 @@ -125,6 +125,14 @@ TenantId = $TenantId CertificateThumbprint = $CertificateThumbprint } + AADAccessReviewPolicy 'AADAccessReviewPolicy' + { + ApplicationId = $ConfigurationData.NonNodeData.ApplicationId; + CertificateThumbprint = $ConfigurationData.NonNodeData.CertificateThumbprint; + IsGroupOwnerManagementEnabled = $False; + IsSingleInstance = "Yes"; + TenantId = $OrganizationName; + } AADAdminConsentRequestPolicy 'AADAdminConsentRequestPolicy' { ApplicationId = $ApplicationId; From 10c5cdad22509994af705c3047d57057360678e5 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 24 Oct 2024 10:47:15 +0000 Subject: [PATCH 57/60] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index b1d3c54e71..d2055bd85f 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -339,6 +339,51 @@ } ] }, + { + "ClassName": "MSFT_AADAccessReviewPolicy", + "Parameters": [ + { + "CIMType": "String", + "Name": "IsSingleInstance", + "Option": "Key" + }, + { + "CIMType": "Boolean", + "Name": "IsGroupOwnerManagementEnabled", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Credential", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ApplicationId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TenantId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificateThumbprint", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "ManagedIdentity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "AccessTokens", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_AADActivityBasedTimeoutPolicy", "Parameters": [ From e04eaa151935460a057d228f808d3684ff269155 Mon Sep 17 00:00:00 2001 From: RiyanshGoyal <159444353+RiyanshGoyal@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:14:18 +0530 Subject: [PATCH 58/60] Update Manifest.psd1 --- Modules/Microsoft365DSC/Dependencies/Manifest.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 index ad88da500b..a1205e2187 100644 --- a/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 +++ b/Modules/Microsoft365DSC/Dependencies/Manifest.psd1 @@ -58,7 +58,7 @@ }, @{ ModuleName = 'Microsoft.Graph.Beta.NetworkAccess' - RequiredVersion = '2.23.0' + RequiredVersion = '2.24.0' }, @{ ModuleName = 'Microsoft.Graph.Beta.Identity.DirectoryManagement' From 1865b5fd6ba0b72aa6e97fc55ad2843ea1d53785 Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 24 Oct 2024 12:36:25 +0000 Subject: [PATCH 59/60] Updated Resources and Cmdlet documentation pages --- .../AADNetworkAccessForwardingProfile.md | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 docs/docs/resources/azure-ad/AADNetworkAccessForwardingProfile.md diff --git a/docs/docs/resources/azure-ad/AADNetworkAccessForwardingProfile.md b/docs/docs/resources/azure-ad/AADNetworkAccessForwardingProfile.md new file mode 100644 index 0000000000..6f14c1d50b --- /dev/null +++ b/docs/docs/resources/azure-ad/AADNetworkAccessForwardingProfile.md @@ -0,0 +1,115 @@ +# AADNetworkAccessForwardingProfile + +## Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Name** | Key | String | Profile Name. Required. | | +| **Id** | Write | String | Id of the profile. Unique Identifier | | +| **State** | Write | String | status of the profile | | +| **Policies** | Write | MSFT_MicrosoftGraphNetworkaccessPolicyLink[] | Traffic forwarding policies associated with this profile. | | +| **Credential** | Write | PSCredential | Credentials of the Admin | | +| **ApplicationId** | Write | String | Id of the Azure Active Directory application to authenticate with. | | +| **TenantId** | Write | String | Id of the Azure Active Directory tenant used for authentication. | | +| **ApplicationSecret** | Write | PSCredential | Secret of the Azure Active Directory tenant used for authentication. | | +| **CertificateThumbprint** | Write | String | Thumbprint of the Azure Active Directory application's authentication certificate to use for authentication. | | +| **ManagedIdentity** | Write | Boolean | Managed ID being used for authentication. | | +| **AccessTokens** | Write | StringArray[] | Access token used for authentication. | | + +### MSFT_MicrosoftGraphNetworkaccessPolicyLink + +#### Parameters + +| Parameter | Attribute | DataType | Description | Allowed Values | +| --- | --- | --- | --- | --- | +| **Name** | Write | String | Policy Name. Required | | +| **PolicyLinkId** | Write | String | Policy Link Id | | +| **state** | Write | String | status | | + + +## Description + +This resource configure the Azure AD Network Access Forwarding Profile + + +## Permissions + +### Microsoft Graph + +To authenticate with the Microsoft Graph API, this resource required the following permissions: + +#### Delegated permissions + +- **Read** + + - NetworkAccess.Read.All + +- **Update** + + - NetworkAccess.ReadWrite.All + +#### Application permissions + +- **Read** + + - NetworkAccess.Read.All + +- **Update** + + - NetworkAccess.ReadWrite.All + +## Examples + +### Example 1 + + +```powershell +Configuration Example +{ + param( + [Parameter()] + [System.String] + $ApplicationId, + + [Parameter()] + [System.String] + $TenantId, + + [Parameter()] + [System.String] + $CertificateThumbprint + ) + + Import-DscResource -ModuleName 'Microsoft365DSC' + + Node localhost + { + AADNetworkAccessForwardingProfile "AADNetworkAccessForwardingProfile-Internet traffic forwarding profile" + { + + ApplicationId = $ApplicationId + TenantId = $TenantId + CertificateThumbprint = $CertificateThumbprint + Name = "Internet traffic forwarding profile"; + Policies = @(MSFT_MicrosoftGraphNetworkaccessPolicyLink { + State = 'disabled' + PolicyLinkId = 'f8a43f3f-3f44-4738-8025-088bb095a711' + Name = 'Custom Bypass' + } +MSFT_MicrosoftGraphNetworkaccessPolicyLink { + State = 'enabled' + PolicyLinkId = 'b45d1db0-9965-487b-afb1-f4d25174e9db' + Name = 'Default Bypass' + } +MSFT_MicrosoftGraphNetworkaccessPolicyLink { + State = 'enabled' + PolicyLinkId = 'dfd9cd59-90ca-44fc-b997-7cc71f08e438' + Name = 'Default Acquire' + } + ); + State = "disabled"; + } + } +} +``` + From 2cf778afe3e4a94b71ff4b9314f033dfd03a874e Mon Sep 17 00:00:00 2001 From: NikCharlebois Date: Thu, 24 Oct 2024 12:41:56 +0000 Subject: [PATCH 60/60] Updated Schema Definition --- Modules/Microsoft365DSC/SchemaDefinition.json | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/Modules/Microsoft365DSC/SchemaDefinition.json b/Modules/Microsoft365DSC/SchemaDefinition.json index d2055bd85f..cb4353ab47 100644 --- a/Modules/Microsoft365DSC/SchemaDefinition.json +++ b/Modules/Microsoft365DSC/SchemaDefinition.json @@ -6229,6 +6229,86 @@ } ] }, + { + "ClassName": "MSFT_MicrosoftGraphNetworkaccessPolicyLink", + "Parameters": [ + { + "CIMType": "String", + "Name": "Name", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "PolicyLinkId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "state", + "Option": "Write" + } + ] + }, + { + "ClassName": "MSFT_AADNetworkAccessForwardingProfile", + "Parameters": [ + { + "CIMType": "String", + "Name": "Name", + "Option": "Key" + }, + { + "CIMType": "String", + "Name": "Id", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "State", + "Option": "Write" + }, + { + "CIMType": "MSFT_MicrosoftGraphNetworkaccessPolicyLink[]", + "Name": "Policies", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "Credential", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "ApplicationId", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "TenantId", + "Option": "Write" + }, + { + "CIMType": "MSFT_Credential", + "Name": "ApplicationSecret", + "Option": "Write" + }, + { + "CIMType": "String", + "Name": "CertificateThumbprint", + "Option": "Write" + }, + { + "CIMType": "Boolean", + "Name": "ManagedIdentity", + "Option": "Write" + }, + { + "CIMType": "String[]", + "Name": "AccessTokens", + "Option": "Write" + } + ] + }, { "ClassName": "MSFT_MicrosoftGraphCertificateAuthority", "Parameters": [