Public/Import-IntuneBaseline.ps1

function Import-IntuneBaseline {
    <#
    .SYNOPSIS
        Imports OpenIntuneBaseline policies using IntuneManagement module
    .DESCRIPTION
        Downloads OpenIntuneBaseline from GitHub and imports all policies using the IntuneManagement module.
        Uses IntuneManagement's silent batch mode for automated imports.
    .PARAMETER BaselinePath
        Path to the OpenIntuneBaseline directory (will download if not specified)
    .PARAMETER IntuneManagementPath
        Path to IntuneManagement module (will download if not specified)
    .PARAMETER TenantId
        Target tenant ID (uses connected tenant if not specified)
    .PARAMETER ImportMode
        Import mode: SkipIfExists (default - skip policies that already exist)
    .PARAMETER IncludeAssignments
        Include policy assignments during import
    .EXAMPLE
        Import-IntuneBaseline
    .EXAMPLE
        Import-IntuneBaseline -BaselinePath ./OpenIntuneBaseline -ImportMode SkipIfExists
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter()]
        [string]$BaselinePath,

        [Parameter()]
        [string]$TenantId,

        [Parameter()]
        [ValidateSet('SkipIfExists')]
        [string]$ImportMode = 'SkipIfExists',

        [Parameter()]
        [switch]$RemoveExisting
    )

    # Use connected tenant if not specified
    if (-not $TenantId -and $script:HydrationState.TenantId) {
        $TenantId = $script:HydrationState.TenantId
    }

    if (-not $TenantId) {
        throw "TenantId is required. Either connect using Connect-IntuneHydration or specify -TenantId parameter."
    }

    # Download OpenIntuneBaseline if not provided
    if (-not $BaselinePath -or -not (Test-Path -Path $BaselinePath)) {
        $BaselinePath = Get-OpenIntuneBaseline
    }

    # OpenIntuneBaseline uses OS-based folder structure:
    # - OS/IntuneManagement/ - Exported by IntuneManagement tool (requires Windows GUI to import)
    # - OS/NativeImport/ - Settings Catalog policies that can be imported via Graph API
    # - BYOD/AppProtection/ - App protection policies

    # Map folder names to Graph API endpoints
    $endpointMap = @{
        'NativeImport'                      = 'deviceManagement/configurationPolicies'
        'AppProtection'                     = 'deviceAppManagement/managedAppPolicies'
        'Administrative Templates'           = 'deviceManagement/groupPolicyConfigurations'
        'Compliance'                        = 'deviceManagement/deviceCompliancePolicies'
        'Compliance Policies'               = 'deviceManagement/deviceCompliancePolicies'
        'Configuration Profiles'            = 'deviceManagement/deviceConfigurations'
        'Device Configuration'              = 'deviceManagement/deviceConfigurations'
        'Device Enrollment Configurations'  = 'deviceManagement/deviceEnrollmentConfigurations'
        'Endpoint Security'                 = 'deviceManagement/intents'
        'Settings Catalog'                  = 'deviceManagement/configurationPolicies'
        'Scripts'                           = 'deviceManagement/deviceManagementScripts'
        'Proactive Remediations'            = 'deviceManagement/deviceHealthScripts'
        'Windows Autopilot'                 = 'deviceManagement/windowsAutopilotDeploymentProfiles'
        'App Configuration'                 = 'deviceAppManagement/mobileAppConfigurations'
        'App Protection'                    = 'deviceAppManagement/managedAppPolicies'
        'App Protection Policies'           = 'deviceAppManagement/managedAppPolicies'
    }

    # Map @odata.type to Graph API endpoints for IntuneManagement exports
    $odataTypeToEndpoint = @{
        # Device Configurations
        '#microsoft.graph.windowsHealthMonitoringConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.windows10GeneralConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.windows10EndpointProtectionConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.windows10CustomConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.windowsDeliveryOptimizationConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.windowsUpdateForBusinessConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.windowsIdentityProtectionConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.windowsKioskConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.editionUpgradeConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.sharedPCConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.windowsWifiConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.windowsWiredNetworkConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.macOSGeneralDeviceConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.macOSCustomConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.macOSEndpointProtectionConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.iosGeneralDeviceConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.iosCustomConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.androidGeneralDeviceConfiguration' = 'deviceManagement/deviceConfigurations'
        '#microsoft.graph.androidWorkProfileGeneralDeviceConfiguration' = 'deviceManagement/deviceConfigurations'
        # Compliance Policies
        '#microsoft.graph.windows10CompliancePolicy' = 'deviceManagement/deviceCompliancePolicies'
        '#microsoft.graph.windows81CompliancePolicy' = 'deviceManagement/deviceCompliancePolicies'
        '#microsoft.graph.macOSCompliancePolicy' = 'deviceManagement/deviceCompliancePolicies'
        '#microsoft.graph.iosCompliancePolicy' = 'deviceManagement/deviceCompliancePolicies'
        '#microsoft.graph.androidCompliancePolicy' = 'deviceManagement/deviceCompliancePolicies'
        '#microsoft.graph.androidWorkProfileCompliancePolicy' = 'deviceManagement/deviceCompliancePolicies'
        '#microsoft.graph.androidDeviceOwnerCompliancePolicy' = 'deviceManagement/deviceCompliancePolicies'
        # Settings Catalog / Configuration Policies
        '#microsoft.graph.deviceManagementConfigurationPolicy' = 'deviceManagement/configurationPolicies'
        # Windows Update for Business - Driver Updates
        '#microsoft.graph.windowsDriverUpdateProfile' = 'deviceManagement/windowsDriverUpdateProfiles'
    }

    # Folders that previously required IntuneManagement tool - now we try to import via Graph API
    $intuneManagementFolders = @('IntuneManagement')

    $results = @()

    # Remove existing baseline policies if requested
    # SAFETY: Only delete policies that have "Imported by Intune-Hydration-Kit" in description
    if ($RemoveExisting) {
        # Delete from main endpoints used by baselines
        $deleteEndpoints = @(
            'beta/deviceManagement/configurationPolicies',
            'beta/deviceManagement/deviceConfigurations',
            'beta/deviceManagement/deviceCompliancePolicies',
            'beta/deviceAppManagement/androidManagedAppProtections',
            'beta/deviceAppManagement/iosManagedAppProtections'
        )

        foreach ($endpoint in $deleteEndpoints) {
            try {
                $listUri = $endpoint
                do {
                    $existing = Invoke-MgGraphRequest -Method GET -Uri $listUri -ErrorAction Stop
                    foreach ($policy in $existing.value) {
                        $policyName = if ($policy.displayName) { $policy.displayName } elseif ($policy.name) { $policy.name } else { "Unknown" }
                        $policyId = $policy.id

                        # Safety check: Only delete if created by this kit (has hydration marker in description)
                        if (-not (Test-HydrationKitObject -Description $policy.description -ObjectName $policyName)) {
                            Write-Verbose "Skipping '$policyName' - not created by Intune-Hydration-Kit"
                            continue
                        }

                        if ($PSCmdlet.ShouldProcess($policyName, "Delete baseline policy")) {
                            try {
                                Invoke-MgGraphRequest -Method DELETE -Uri "$endpoint/$policyId" -ErrorAction Stop
                                Write-HydrationLog -Message " Deleted: $policyName" -Level Info
                                $results += New-HydrationResult -Name $policyName -Type 'BaselinePolicy' -Action 'Deleted' -Status 'Success'
                            }
                            catch {
                                $errMessage = Get-GraphErrorMessage -ErrorRecord $_
                                Write-HydrationLog -Message " Failed: $policyName - $errMessage" -Level Warning
                                $results += New-HydrationResult -Name $policyName -Type 'BaselinePolicy' -Action 'Failed' -Status "Delete failed: $errMessage"
                            }
                        }
                        else {
                            Write-HydrationLog -Message " WouldDelete: $policyName" -Level Info
                            $results += New-HydrationResult -Name $policyName -Type 'BaselinePolicy' -Action 'WouldDelete' -Status 'DryRun'
                        }
                    }
                    $listUri = $existing.'@odata.nextLink'
                } while ($listUri)
            }
            catch {
                Write-Warning "Failed to process endpoint $endpoint : $_"
            }
        }

        return $results
    }

    # Find all policy type subfolders within OS folders (WINDOWS, MACOS, BYOD, WINDOWS365)
    # OpenIntuneBaseline structure: OS/PolicyType/policy.json
    $osFolders = Get-ChildItem -Path $BaselinePath -Directory | Where-Object {
        $_.Name -notmatch '^\.'
    }

    $totalPolicies = 0
    $policyTypefolders = @()

    foreach ($osFolder in $osFolders) {
        # Get policy type subfolders within each OS folder
        $subFolders = Get-ChildItem -Path $osFolder.FullName -Directory | Where-Object {
            $_.Name -notmatch '^\.' -and (Get-ChildItem -Path $_.FullName -Filter "*.json" -File -Recurse).Count -gt 0
        }

        foreach ($subFolder in $subFolders) {
            $jsonFiles = Get-ChildItem -Path $subFolder.FullName -Filter "*.json" -File -Recurse
            $totalPolicies += $jsonFiles.Count
            $policyTypefolders += @{
                Folder = $subFolder
                OsFolder = $osFolder.Name
                PolicyType = $subFolder.Name
            }
        }
    }

    if ($PSCmdlet.ShouldProcess("$totalPolicies policies from OpenIntuneBaseline", "Import to Intune")) {

        # Pre-fetch existing policies from all unique endpoints to avoid repeated API calls
        $endpointPolicyCache = @{}
        $uniqueEndpoints = $odataTypeToEndpoint.Values | Sort-Object -Unique
        foreach ($cacheEndpoint in $uniqueEndpoints) {
            $endpointPolicyCache[$cacheEndpoint] = @{}
            try {
                $listUri = "beta/$cacheEndpoint"
                do {
                    $cacheResponse = Invoke-MgGraphRequest -Method GET -Uri $listUri -ErrorAction Stop
                    foreach ($policy in $cacheResponse.value) {
                        # Use 'name' for configurationPolicies, 'displayName' for others
                        $policyDisplayName = if ($cacheEndpoint -eq 'deviceManagement/configurationPolicies') {
                            $policy.name
                        } else {
                            if ($policy.displayName) { $policy.displayName } elseif ($policy.name) { $policy.name } else { $null }
                        }
                        if ($policyDisplayName -and -not $endpointPolicyCache[$cacheEndpoint].ContainsKey($policyDisplayName)) {
                            $endpointPolicyCache[$cacheEndpoint][$policyDisplayName] = $policy.id
                        }
                    }
                    $listUri = $cacheResponse.'@odata.nextLink'
                } while ($listUri)
            }
            catch {
                # Endpoint might not support listing, continue without cache for this endpoint
                Write-Verbose "Could not cache policies from $cacheEndpoint - will check individually"
            }
        }

        foreach ($policyFolder in $policyTypefolders) {
            $folder = $policyFolder.Folder
            $folderName = $policyFolder.PolicyType
            $osName = $policyFolder.OsFolder
            $jsonFiles = Get-ChildItem -Path $folder.FullName -Filter "*.json" -File -Recurse

            # For IntuneManagement folders, try to import using @odata.type routing
            if ($folderName -in $intuneManagementFolders) {
                foreach ($jsonFile in $jsonFiles) {
                    $policyName = [System.IO.Path]::GetFileNameWithoutExtension($jsonFile.Name)

                    try {
                        $policyContent = Get-Content -Path $jsonFile.FullName -Raw | ConvertFrom-Json
                        $odataType = $policyContent.'@odata.type'

                        # Determine endpoint from @odata.type
                        $typeEndpoint = $odataTypeToEndpoint[$odataType]
                        if (-not $typeEndpoint) {
                            Write-Warning " Skipping $policyName - unsupported @odata.type: $odataType"
                            $results += New-HydrationResult -Name $policyName -Path $jsonFile.FullName -Type "$osName/$folderName" -Action 'Skipped' -Status "Unsupported @odata.type: $odataType"
                            continue
                        }

                        # Get display name
                        $displayName = $policyContent.displayName
                        if (-not $displayName) {
                            $displayName = $policyName
                        }

                        # Check if policy exists using pre-fetched cache
                        $existingPolicy = $endpointPolicyCache[$typeEndpoint].ContainsKey($displayName)

                        if ($existingPolicy -and $ImportMode -eq 'SkipIfExists') {
                            Write-HydrationLog -Message " Skipped: $displayName" -Level Info
                            $results += New-HydrationResult -Name $displayName -Path $jsonFile.FullName -Type "$osName/$folderName" -Action 'Skipped' -Status 'Already exists'
                            continue
                        }

                        # Prepare import body - remove read-only and assignment properties
                        $importBody = Copy-DeepObject -InputObject $policyContent
                        Remove-ReadOnlyGraphProperties -InputObject $importBody -AdditionalProperties @(
                            'supportsScopeTags', 'deviceManagementApplicabilityRuleOsEdition',
                            'deviceManagementApplicabilityRuleOsVersion',
                            'deviceManagementApplicabilityRuleDeviceMode',
                            '@odata.id', '@odata.editLink',
                            'creationSource', 'settingCount', 'priorityMetaData',
                            'assignments', 'settingDefinitions', 'isAssigned'
                        )

                        # Add hydration kit tag to description
                        $existingDesc = if ($importBody.description) { $importBody.description } else { "" }
                        $importBody.description = if ($existingDesc) { "$existingDesc - Imported by Intune-Hydration-Kit" } else { "Imported by Intune-Hydration-Kit" }

                        # Remove properties with @odata annotations (metadata) except @odata.type
                        # Also remove #microsoft.graph.* action properties
                        $metadataProps = @($importBody.PSObject.Properties | Where-Object {
                            ($_.Name -match '^@odata\.' -and $_.Name -ne '@odata.type') -or
                            ($_.Name -match '@odata\.') -or
                            ($_.Name -match '^#microsoft\.graph\.')
                        })
                        foreach ($prop in $metadataProps) {
                            if ($prop.Name -ne '@odata.type') {
                                $importBody.PSObject.Properties.Remove($prop.Name)
                            }
                        }

                        # Special handling for Settings Catalog (configurationPolicies)
                        if ($typeEndpoint -eq 'deviceManagement/configurationPolicies') {
                            Write-Verbose " Processing Settings Catalog policy: $displayName"
                            Write-Verbose " Original properties: $($importBody.PSObject.Properties.Name -join ', ')"

                            # Build a clean body with only the required properties
                            $cleanBody = @{
                                name = $importBody.name
                                description = $importBody.description
                                platforms = $importBody.platforms
                                technologies = $importBody.technologies
                                settings = @()
                            }

                            Write-Verbose " Building clean body with: name, description, platforms, technologies"

                            # Add optional properties if present
                            if ($importBody.roleScopeTagIds) {
                                $cleanBody.roleScopeTagIds = $importBody.roleScopeTagIds
                                Write-Verbose " Added roleScopeTagIds"
                            }
                            if ($importBody.templateReference -and $importBody.templateReference.templateId) {
                                $cleanBody.templateReference = @{
                                    templateId = $importBody.templateReference.templateId
                                }
                                Write-Verbose " Added templateReference with templateId: $($importBody.templateReference.templateId)"
                            }

                            # Clean settings - remove id and odata navigation properties from each setting
                            if ($importBody.settings) {
                                Write-Verbose " Processing $($importBody.settings.Count) settings"
                                $settingIndex = 0
                                foreach ($setting in $importBody.settings) {
                                    $settingJson = $setting | ConvertTo-Json -Depth 100 -Compress
                                    $cleanSetting = $settingJson | ConvertFrom-Json

                                    # Remove 'id' and odata navigation link properties from the setting
                                    $propsToRemoveFromSetting = @($cleanSetting.PSObject.Properties | Where-Object {
                                        $_.Name -eq 'id' -or
                                        $_.Name -match '@odata\.' -or
                                        $_.Name -match 'settingDefinitions'
                                    })

                                    if ($propsToRemoveFromSetting.Count -gt 0) {
                                        Write-Verbose " Setting[$settingIndex] - Removing properties: $($propsToRemoveFromSetting.Name -join ', ')"
                                    }

                                    foreach ($prop in $propsToRemoveFromSetting) {
                                        $cleanSetting.PSObject.Properties.Remove($prop.Name)
                                    }

                                    $cleanBody.settings += $cleanSetting
                                    $settingIndex++
                                }
                            }

                            $importBody = [PSCustomObject]$cleanBody

                            # Debug: Show final body properties
                            Write-Verbose " Final body properties: $($importBody.PSObject.Properties.Name -join ', ')"

                            # Debug: Show first 500 chars of JSON being sent
                            $debugJson = $importBody | ConvertTo-Json -Depth 100 -Compress
                            Write-Verbose " Request body preview (first 500 chars): $($debugJson.Substring(0, [Math]::Min(500, $debugJson.Length)))"
                        }

                        # Clean up scheduledActionsForRule - remove nested @odata.context and IDs
                        if ($importBody.scheduledActionsForRule) {
                            $cleanedActions = @()
                            foreach ($action in $importBody.scheduledActionsForRule) {
                                $cleanAction = @{
                                    ruleName = $action.ruleName
                                }
                                if ($action.scheduledActionConfigurations) {
                                    $cleanConfigs = @()
                                    foreach ($config in $action.scheduledActionConfigurations) {
                                        # Ensure notificationMessageCCList is always an array, never null
                                        $ccList = @()
                                        if ($null -ne $config.notificationMessageCCList -and $config.notificationMessageCCList.Count -gt 0) {
                                            $ccList = @($config.notificationMessageCCList)
                                        }
                                        $cleanConfig = @{
                                            actionType = $config.actionType
                                            gracePeriodHours = [int]$config.gracePeriodHours
                                            notificationTemplateId = if ($config.notificationTemplateId) { $config.notificationTemplateId } else { "" }
                                            notificationMessageCCList = $ccList
                                        }
                                        $cleanConfigs += $cleanConfig
                                    }
                                    $cleanAction.scheduledActionConfigurations = $cleanConfigs
                                }
                                $cleanedActions += $cleanAction
                            }
                            $importBody.scheduledActionsForRule = $cleanedActions
                        }

                        # Create the policy
                        $null = Invoke-MgGraphRequest -Method POST -Uri "beta/$typeEndpoint" -Body ($importBody | ConvertTo-Json -Depth 100) -ContentType 'application/json' -ErrorAction Stop

                        Write-HydrationLog -Message " Created: $displayName" -Level Info
                        $results += New-HydrationResult -Name $displayName -Path $jsonFile.FullName -Type "$osName/$folderName" -Action 'Created' -Status 'Success'
                    }
                    catch {
                        $errorMsg = Get-GraphErrorMessage -ErrorRecord $_
                        Write-HydrationLog -Message " Failed: $policyName - $errorMsg" -Level Warning
                        $results += New-HydrationResult -Name $policyName -Path $jsonFile.FullName -Type "$osName/$folderName" -Action 'Failed' -Status $errorMsg
                    }

                    Start-Sleep -Milliseconds 100
                }
                continue
            }

            # Determine API endpoint based on policy type folder name
            $endpoint = $endpointMap[$folderName]
            if (-not $endpoint) {
                Write-Warning "No endpoint mapping for folder: $osName/$folderName - skipping"
                foreach ($jsonFile in $jsonFiles) {
                    $policyName = [System.IO.Path]::GetFileNameWithoutExtension($jsonFile.Name)
                    $results += New-HydrationResult -Name $policyName -Path $jsonFile.FullName -Type "$osName/$folderName" -Action 'Skipped' -Status "No endpoint mapping for $folderName"
                }
                continue
            }

            # Progress tracking for this folder
            $folderTotal = $jsonFiles.Count
            $folderCurrent = 0

            # Pre-fetch existing policies for this endpoint to avoid repeated API calls (page through all results)
            $existingPolicies = @{}
            try {
                $listUri = "beta/$endpoint"
                do {
                    $existingResponse = Invoke-MgGraphRequest -Method GET -Uri $listUri -ErrorAction Stop
                    foreach ($policy in $existingResponse.value) {
                        $policyDisplayName = if ($policy.displayName) { $policy.displayName } elseif ($policy.name) { $policy.name } else { $null }
                        if ($policyDisplayName -and -not $existingPolicies.ContainsKey($policyDisplayName)) {
                            $existingPolicies[$policyDisplayName] = $policy.id
                        }
                    }
                    $listUri = $existingResponse.'@odata.nextLink'
                } while ($listUri)
            }
            catch {
                # Endpoint might not support listing, continue without cache
                Write-Verbose "Could not cache policies from $endpoint - will check individually"
            }

            foreach ($jsonFile in $jsonFiles) {
                $folderCurrent++
                Write-Progress -Activity "Importing $osName/$folderName" -Status "$folderCurrent of $folderTotal" -PercentComplete (($folderCurrent / $folderTotal) * 100)

                $policyName = [System.IO.Path]::GetFileNameWithoutExtension($jsonFile.Name)

                try {
                    # Read and parse JSON
                    $policyContent = Get-Content -Path $jsonFile.FullName -Raw | ConvertFrom-Json

                    # Get display name from policy
                    $displayName = $policyContent.displayName
                    if (-not $displayName) {
                        $displayName = $policyContent.name
                    }
                    if (-not $displayName) {
                        $displayName = $policyName
                    }

                    # Check if policy exists using cached list
                    $existingPolicy = $existingPolicies.ContainsKey($displayName)

                    if ($existingPolicy -and $ImportMode -eq 'SkipIfExists') {
                        Write-HydrationLog -Message " Skipped: $displayName" -Level Info
                        $results += New-HydrationResult -Name $displayName -Path $jsonFile.FullName -Type "$osName/$folderName" -Action 'Skipped' -Status 'Already exists'
                        continue
                    }

                    # Clean up import properties that shouldn't be sent
                    $importBody = Copy-DeepObject -InputObject $policyContent

                    # Remove read-only and system properties
                    Remove-ReadOnlyGraphProperties -InputObject $importBody -AdditionalProperties @(
                        'supportsScopeTags', 'deviceManagementApplicabilityRuleOsEdition',
                        'deviceManagementApplicabilityRuleOsVersion',
                        'deviceManagementApplicabilityRuleDeviceMode',
                        'creationSource', 'settingCount', 'priorityMetaData'
                    )

                    # Add hydration kit tag to description
                    $existingDesc = if ($importBody.description) { $importBody.description } else { "" }
                    $importBody.description = if ($existingDesc) { "$existingDesc - Imported by Intune-Hydration-Kit" } else { "Imported by Intune-Hydration-Kit" }

                    # Create the policy
                    $null = Invoke-MgGraphRequest -Method POST -Uri "beta/$endpoint" -Body ($importBody | ConvertTo-Json -Depth 100) -ContentType 'application/json' -ErrorAction Stop

                    Write-HydrationLog -Message " Created: $displayName" -Level Info

                    $results += New-HydrationResult -Name $displayName -Path $jsonFile.FullName -Type "$osName/$folderName" -Action 'Created' -Status 'Success'
                }
                catch {
                    $errorMsg = Get-GraphErrorMessage -ErrorRecord $_
                    Write-HydrationLog -Message " Failed: $policyName - $errorMsg" -Level Warning

                    $results += New-HydrationResult -Name $policyName -Path $jsonFile.FullName -Type "$osName/$folderName" -Action 'Failed' -Status $errorMsg
                }

                # Small delay to avoid rate limiting
                Start-Sleep -Milliseconds 100
            }
            Write-Progress -Activity "Importing $osName/$folderName" -Completed
        }

    }
    else {
        # WhatIf mode - just report what would be imported
        foreach ($policyFolder in $policyTypefolders) {
            $folder = $policyFolder.Folder
            $osName = $policyFolder.OsFolder
            $folderName = $policyFolder.PolicyType
            $jsonFiles = Get-ChildItem -Path $folder.FullName -Filter "*.json" -File -Recurse

            foreach ($jsonFile in $jsonFiles) {
                $policyName = [System.IO.Path]::GetFileNameWithoutExtension($jsonFile.Name)

                $results += New-HydrationResult -Name $policyName -Path $jsonFile.FullName -Type "$osName/$folderName" -Action 'WouldCreate' -Status 'DryRun'
            }
        }
    }

    return $results
}