Public/psf-config.ps1

function Export-FalconConfig {
    [CmdletBinding(DefaultParameterSetName = 'ExportItems')]
    param(
        [Parameter(ParameterSetName = 'ExportItems', Position = 1)]
        [ValidateSet('HostGroup', 'IoaGroup', 'FirewallGroup', 'DeviceControlPolicy', 'FirewallPolicy',
            'PreventionPolicy', 'ResponsePolicy', 'SensorUpdatePolicy', 'Ioc', 'IoaExclusion', 'MlExclusion',
            'SvExclusion')]
        [array] $Items
    )
    begin {
        function Get-ItemContent ($Item) {
            # Request content for provided 'Item'
            Write-Host "Exporting '$Item'..."
            $ItemFile = Join-Path -Path $Location -ChildPath "$Item.json"
            $Param = @{
                Detailed = $true
                All = $true
            }
            $FileContent = if ($Item -match '^(DeviceControl|Firewall|Prevention|Response|SensorUpdate)Policy$') {
                # Create policy exports in 'platform_name' order to retain precedence
                @('Windows','Mac','Linux').foreach{
                    & "Get-Falcon$($Item)" @Param -Filter "platform_name:'$_'+name:!'platform_default'" 2>$null
                }
            } else {
                & "Get-Falcon$($Item)" @Param 2>$null
            }
            if ($FileContent -and $Item -eq 'FirewallPolicy') {
                # Export firewall settings
                Write-Host "Exporting 'FirewallSetting'..."
                $Settings = Get-FalconFirewallSetting -Ids $FileContent.id 2>$null
                foreach ($Result in $Settings) {
                    ($FileContent | Where-Object { $_.id -eq $Result.policy_id }).PSObject.Properties.Add(
                        (New-Object PSNoteProperty('settings', $Result)))
                }
            }
            if ($FileContent) {
                # Export results to json file and output created file name
                ConvertTo-Json -InputObject @( $FileContent ) -Depth 32 | Out-File -FilePath $ItemFile -Append
                $ItemFile
            }
        }
        # Get current location
        $Location = (Get-Location).Path
        $Export = if ($PSBoundParameters.Items) {
            # Use specified items
            $PSBoundParameters.Items
        } else {
            # Use items in 'ValidateSet' when not provided
            (Get-Command $MyInvocation.MyCommand.Name).ParameterSets.Where({ $_.Name -eq
            'ExportItems' }).Parameters.Where({ $_.Name -eq 'Items' }).Attributes.ValidValues
        }
        # Set output archive path
        $ArchiveFile = Join-Path $Location -ChildPath "FalconConfig_$(Get-Date -Format FileDate).zip"
    }
    process {
        if (Test-Path $ArchiveFile) {
            throw "An item with the specified name $ArchiveFile already exists."
        }
        [array] $Export += switch ($Export) {
            { $_ -match '^((Ioa|Ml|Sv)Exclusion|Ioc)$' -and $Export -notcontains 'HostGroup' } {
                # Force 'HostGroup' when exporting Exclusions or IOCs
                'HostGroup'
            }
            { $_ -contains 'FirewallGroup' } {
                # Force 'FirewallRule' when exporting 'FirewallGroup'
                'FirewallRule'
            }
        }
        $JsonFiles = foreach ($Item in $Export) {
            # Retrieve results, export to Json and capture file name
            ,(Get-ItemContent -Item $Item)
        }
        if ($JsonFiles) {
            # Archive Json exports with content
            $Param = @{
                Path = (Get-ChildItem | Where-Object { $JsonFiles -contains $_.FullName -and
                    $_.Length -gt 0 }).FullName
                DestinationPath = $ArchiveFile
            }
            Compress-Archive @Param
            if (Test-Path $ArchiveFile) {
                # Display created archive
                Get-ChildItem $ArchiveFile
            }
            if (Test-Path $JsonFiles) {
                # Remove Json files when archived
                Remove-Item -Path $JsonFiles -Force
            }
        }
    }
}
function Import-FalconConfig {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 1)]
        [ValidatePattern('\.zip$')]
        [ValidateScript({
            if (Test-Path $_) { $true } else { throw "Cannot find path '$_' because it does not exist." }
        })]
        [string] $Path,

        [Parameter(Position = 2)]
        [switch] $Force
    )
    begin {
        # List of fields to capture/exclude/compare/export during import process
        $ConfigFields = @{
            DeviceControlPolicy = @{
                Import = @('id', 'platform_name', 'name', 'description', 'settings', 'enabled', 'groups')
            }
            FirewallGroup = @{
                Import = @('id', 'name', 'enabled', 'rule_ids', 'description')
            }
            FirewallPolicy = @{
                Import = @('id', 'name', 'platform_name', 'description', 'enabled', 'groups', 'settings')
            }
            FirewallRule = @{
                Import = @('id', 'family', 'name', 'description', 'enabled', 'platform_ids', 'direction',
                    'action', 'address_family', 'local_address', 'remote_address', 'protocol', 'local_port',
                    'remote_port', 'icmp', 'monitor', 'fields', 'rule_group')
            }
            FirewallSetting = @{
                Import = @('policy_id', 'platform_id', 'default_inbound', 'default_outbound', 'enforce',
                    'test_mode', 'rule_group_ids', 'is_default_policy')
            }
            HostGroup = @{
                Import = @('id', 'name', 'description', 'group_type', 'assignment_rule')
            }
            IoaExclusion = @{
                Import = @('id', 'cl_regex', 'ifn_regex', 'name', 'pattern_id', 'pattern_name', 'groups',
                    'comment', 'description', 'applied_globally')
            }
            IoaGroup = @{
                Import = @('id', 'platform', 'name', 'description', 'rules', 'enabled', 'version')
            }
            IoaRule = @{
                Create = @('name', 'pattern_severity', 'ruletype_id', 'disposition_id', 'field_values',
                    'description', 'comment', 'enabled')
                Export = @('instance_id', 'name')
            }
            Ioc = @{
                Import = @('id', 'type', 'value', 'action', 'platforms', 'source', 'severity', 'description',
                    'tags', 'applied_globally', 'host_groups', 'expiration')
                Compare = @('type', 'value')
            }
            MlExclusion = @{
                Import = @('id', 'value', 'excluded_from', 'groups', 'applied_globally')
                Compare = @('value')
            }
            PreventionPolicy = @{
                Import = @('id', 'platform_name', 'name', 'description', 'prevention_settings',
                    'enabled', 'groups', 'ioa_rule_groups')
            }
            ResponsePolicy = @{
                Import = @('id', 'platform_name', 'name', 'description', 'settings', 'enabled', 'groups')
            }
            SensorUpdatePolicy = @{
                Import = @('id', 'platform_name', 'name', 'settings', 'enabled', 'description',
                    'groups')
            }
            SvExclusion = @{
                Import = @('id', 'value', 'groups', 'applied_globally')
                Compare = @('value')
            }
        }
        function Compare-ImportData ($Item) {
            if ($ConfigData.$Item.Cid) {
                $CompareFields = if ($ConfigFields.$Item.Compare) {
                    # Use defined fields for comparison with CID
                    $ConfigFields.$Item.Compare
                } else {
                    # Use 'name'
                    @('name')
                }
                $FilterText = (($CompareFields).foreach{
                    "`$ConfigData.$($Item).Cid.$($_) -notcontains `$_.$($_)" }) -join ' -and '
                $Param = @{
                    Item         = $Item
                    Type         = 'Import'
                    FilterScript = [scriptblock]::Create($FilterText)
                }
                Get-ConfigItem @Param
            } elseif ($ConfigData.$Item.Import) {
                # Output all items
                @($ConfigData.$Item.Import).foreach{
                    Write-Verbose "[Compare-ImportData] $($Item).Import: $($_.id)"
                    $_
                }
            }
        }
        function Compress-Object ($Object) {
            foreach ($Item in $Object) {
                if ($Item.groups) {
                    # Keep 'id' and 'name' from 'groups'
                    $Item.groups = $Item.groups | Select-Object id, name
                }
                if ($Item.prevention_settings.settings) {
                    # Keep 'id' and 'value' from 'prevention_settings'
                    $Item.prevention_settings = $Item.prevention_settings.settings | Select-Object id, value
                }
                if ($Item.settings.classes) {
                    foreach ($Class in ($Item.settings.classes | Where-Object { $_.exceptions })) {
                        # Remove 'id' from 'exceptions'
                        $Class.exceptions = @($Class.exceptions).foreach{
                            $_.PSObject.Properties.Remove('id')
                            $_
                        }
                    }
                }
                if ($Item.rule_group) {
                    # Keep 'id', 'policy_ids' and 'name' from Firewall rules
                    $Item.rule_group = $Item.rule_group | Select-Object id, policy_ids, name
                }
                if ($Item.settings.settings) {
                    # Keep 'id' and 'value' from settings
                    $Item.settings = $Item.settings.settings | Select-Object id, value
                }
                if ($Item.field_values) {
                    # Keep 'name', 'label', 'type' and 'values' from 'field_values'
                    $Item.field_values = $Item.field_values | Select-Object name, label, type, values
                }
            }
            $Object
        }
        function Get-CidValue ($Item) {
            try {
                # Retrieve existing configurations from CID, excluding 'platform_default'
                $Param = @{
                    Detailed = $true
                    All      = $true
                }
                if ($Item -match 'Policy$') {
                    $Param['Filter'] = "name:!'platform_default'"
                }
                Write-Host "Retrieving '$Item'..."
                Compress-Object (& "Get-Falcon$($Item)" @Param) | Select-Object $ConfigFields.$Item.Import
            } catch {
                throw $_
            }
        }
        function Get-ConfigItem ($Item, $Type, $FilterScript) {
            @($ConfigData.$Item.$Type | Where-Object -FilterScript $FilterScript).foreach{
                Write-Verbose "[Get-ConfigItem] $($Item).$($Type): $($_.id)"
                $_
            }
        }
        function Import-ConfigData ($FilePath) {
            # Load 'FalconConfig' archive into memory
            $Output = @{}
            $ByteStream = if ($PSVersionTable.PSVersion.Major -ge 6) {
                Get-Content -Path $FilePath -AsByteStream
            } else {
                Get-Content -Path $FilePath -Encoding Byte -Raw
            }
            [System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression') | Out-Null
            $FileStream = New-Object System.IO.MemoryStream
            $FileStream.Write($ByteStream,0,$ByteStream.Length)
            $ConfigArchive = New-Object System.IO.Compression.ZipArchive($FileStream)
            foreach ($FullName in $ConfigArchive.Entries.FullName) {
                # Import Json and remove unnecessary properties
                $Filename = $ConfigArchive.GetEntry($FullName)
                $Item = ($FullName | Split-Path -Leaf).Split('.')[0]
                $Import = ConvertFrom-Json -InputObject ((New-Object System.IO.StreamReader(
                    $Filename.Open())).ReadToEnd())
                $Import = $Import | Select-Object $ConfigFields.$Item.Import
                $Output[$Item] = @{
                    Import = Compress-Object $Import
                }
            }
            if ($FileStream) {
                $FileStream.Dispose()
            }
            $Output
        }
        function Invoke-ConfigArray ($Item) {
            [array] $Content = if ($Item -match 'Policy$') {
                # Filter to required fields for creating policies
                ,($ConfigData.$Item.Import | Select-Object platform_name, name, description)
            } elseif ($Item -eq 'Ioc') {
                foreach ($Import in $ConfigData.$Item.Import) {
                    $Fields = ($ConfigFields.$Item.Import).foreach{
                        if ($_ -ne 'id' -and $Import.$_) {
                            # Use 'Create' fields that are populated
                            $_
                        }
                    }
                    $Filtered = $Import | Select-Object $Fields
                    if ($Filtered.applied_globally -eq $true) {
                        # Output IOC if 'applied_globally' is true
                        $Filtered
                    } elseif ($Filtered.host_groups) {
                        # Use imported 'id' to find 'name'
                        $Param = @{
                            Item         = 'HostGroup'
                            Type         = 'Import'
                            FilterScript = { $Filtered.host_groups -contains $_.id }
                        }
                        [array] $GroupIds = foreach ($Name in (Get-ConfigItem @Param).Name) {
                            # Use 'name' to find Host Group 'id'
                            $Param = @{
                                Item         = 'HostGroup'
                                Type         = 'Created'
                                FilterScript = { $_.name -eq $Name }
                            }
                            $CreatedId = (Get-ConfigItem @Param).id
                            if ($CreatedId) {
                                ,$CreatedId
                            } elseif ($ForceEnabled -eq $true) {
                                ,(Get-ConfigItem @Param -Type 'Cid').id
                            }
                        }
                        if ($GroupIds) {
                            # Output IOC with updated 'host_groups'
                            $Filtered.host_groups = $GroupIds
                            ,$Filtered
                        } else {
                            Write-Warning ("Unable to create '$($Filtered.type):$($Filtered.value)' " +
                                "[missing_assignment]")
                        }
                    }
                }
            } else {
                # Select fields for 'HostGroup'
                @(Compare-ImportData $Item | Select-Object name, group_type, description, assignment_rule).foreach{
                    # Remove 'assignment_rule' from 'static' host groups
                    if ($_.group_type -eq 'static') {
                        $_.PSObject.Properties.Remove('assignment_rule')
                    }
                    ,$_
                }
            }
            if ($Content) {
                try {
                    for ($i = 0; $i -lt ($Content | Measure-Object).Count; $i += 50) {
                        # Create objects in batches of 50
                        $Request = & "New-Falcon$Item" -Array @($Content[$i..($i + 49)])
                        if ($Request) {
                            if ($ConfigFields.$Item.Import) {
                                $Request | Select-Object $ConfigFields.$Item.Import
                            } else {
                                $Request
                            }
                        }
                    }
                } catch {
                    throw $_
                }
            }
        }
        function Invoke-ConfigItem ($Command, $Content) {
            $Type = $Command -replace '\w+\-Falcon', $null -replace 'Setting', 'Policy'
            try {
                # Create/modify/enable item and notify host
                $Request = & $Command @Content
                if ($Request) {
                    if ($ConfigFields.$Type.Import -and $Type -ne 'FirewallGroup') {
                        # Output 'import' fields, unless creating a firewall rule group
                        $Request | Select-Object $ConfigFields.$Type.Import
                    } else {
                        $Request
                    }
                }
            } catch {
                throw $_
            }
        }
        # Convert 'Path' to absolute and set 'OutputFile'
        $ArchivePath = $Script:Falcon.Api.Path($PSBoundParameters.Path)
        $Param = @{
            Path      = (Get-Location).Path
            ChildPath = "FalconConfig_$(Get-Date -Format FileDate).csv"
        }
        $OutputFile = Join-Path @Param
        $ForceEnabled = if ($PSBoundParameters.Force) {
            $true
        } else {
            $false
        }
    }
    process {
        # Create 'ConfigData' and import configuration files
        $ConfigData = Import-ConfigData -FilePath $ArchivePath
        foreach ($Pair in $ConfigData.GetEnumerator()) {#$ConfigData.GetEnumerator().foreach{
            # Retrieve data from CID
            $Pair.Value['Cid'] = [array] (Get-CidValue $Pair.Key)
            if ($Pair.Key -match 'Policy$') {
                $Pair.Value.Import = foreach ($ImpPol in $Pair.Value.Import) {
                    # Check policies by platform name and name
                    if (!($ConfigData.($Pair.Key).Cid | Where-Object { $_.name -eq $ImpPol.name -and
                    $_.platform_name -eq $ImpPol.platform_name })) {
                        $ImpPol
                    }
                }
            } elseif ($Pair.Key -ne 'HostGroup') {
                # Remove existing items from 'Import', except for 'HostGroup'
                $Pair.Value.Import = Compare-ImportData $Pair.Key
            }
        }
        if ($ConfigData.SensorUpdatePolicy.Import) {
            $Builds = try {
                Write-Host "Retrieving available sensor builds..."
                Get-FalconBuild
            } catch {
                throw "Failed to retrieve available builds for 'SensorUpdate' policy creation."
            }
            foreach ($Policy in $ConfigData.SensorUpdatePolicy.Import) {
                # Update tagged builds with current tagged build versions
                if ($Policy.settings.build -match '^\d+\|') {
                    $Tag = ($Policy.settings.build -split '\|', 2)[-1]
                    $CurrentBuild = ($Builds | Where-Object { ($_.build -like "*|$Tag") -and
                        ($_.platform -eq $Policy.platform_name) }).build
                    if ($Policy.settings.build -ne $CurrentBuild) {
                        $Policy.settings.build = $CurrentBuild
                    }
                }
                if ($Policy.settings.variants) {
                    # Update tagged 'variant' builds with current tagged build versions
                    @($Policy.settings.variants | Where-Object { $_.build }).foreach{
                        $Tag = ($_.build -split '\|', 2)[-1]
                        $CurrentBuild = ($Builds | Where-Object { ($_.build -like "*|$Tag") -and
                            ($_.platform -eq $Policy.platform_name) }).build
                        if ($_.build -ne $CurrentBuild) {
                            $_.build = $CurrentBuild
                        }
                    }
                }
            }
        }
        foreach ($Pair in $ConfigData.GetEnumerator().Where({ $_.Key -eq 'HostGroup' -and $_.Value.Import })) {
            # Create Host Groups
            ($Pair.Value)['Created'] = Invoke-ConfigArray $Pair.Key
            if (($Pair.Value).Created) {
                foreach ($Item in ($Pair.Value).Created) {
                    Write-Host "Created $($Pair.Key) '$($Item.name)'."
                }
            }
        }
        foreach ($Pair in $ConfigData.GetEnumerator().Where({ $_.Key -match '^(Ioc|(Ioa|Ml|Sv)Exclusion)$' -and
        $_.Value.Import })) {
            # Create IOCs and exclusions if assigned to 'all' or can be assigned to Host Groups
            if ($Pair.Key -eq 'Ioc') {
                ($Pair.Value)['Created'] = Invoke-ConfigArray $Pair.Key
                if (($Pair.Value).Created) {
                    foreach ($Item in ($Pair.Value).Created) {
                        Write-Host "Created $($Pair.Key) '$($Item.type):$($Item.value)'."
                    }
                }
            } elseif ($Pair.Key -eq 'IoaExclusion') {
                $ConfigData.($Pair.Key)['Created'] = foreach ($Import in $Pair.Value.Import) {
                    # Create Ioa exclusions
                    $Content = @{
                        Name        = $Import.name
                        PatternId   = $Import.pattern_id
                        PatternName = $Import.pattern_name
                        ClRegex     = $Import.cl_regex
                        IfnRegex    = $Import.ifn_regex
                    }
                    @('description', 'comment').foreach{
                        if ($Import.$_) {
                            $Content[$_] = $Import.$_
                        }
                    }
                    if ($Import.groups) {
                        $Content['GroupIds'] = foreach ($Name in $Import.groups.name) {
                            # Get Host Group identifier
                            $Param = @{
                                Item         = 'HostGroup'
                                Type         = 'Created'
                                FilterScript = { $_.name -eq $Name }
                            }
                            $CreatedId = (Get-ConfigItem @Param).id
                            if ($CreatedId) {
                                ,$CreatedId
                            } elseif ($ForceEnabled -eq $true) {
                                ,(Get-ConfigItem @Param -Type 'Cid').id
                            }
                        }
                    }
                    if ($Import.applied_globally -eq $true -or $Content.GroupIds) {
                        $Param = @{
                            Command = "New-Falcon$($Pair.Key)"
                            Content = $Content
                        }
                        $Created = Invoke-ConfigItem @Param
                        if ($Created) {
                            Write-Host "Created $($Pair.Key) '$($Created.name)'."
                        }
                        $Created
                    } else {
                        Write-Warning "Unable to create '$($Content.name)' [missing_assignment]"
                    }
                }
            } else {
                $ConfigData.($Pair.Key)['Created'] = foreach ($Import in $Pair.Value.Import) {
                    # Create Ml/Sv exclusions
                    $Content = @{
                        Value = $Import.value
                    }
                    if ($Import.excluded_from) {
                        $Content['ExcludedFrom'] = $Import.excluded_from
                    }
                    $Content['GroupIds'] = if ($Import.applied_globally -eq $true) {
                        'all'
                    } else {
                        foreach ($Name in $Import.groups.name) {
                            # Get Host Group identifier
                            $Param = @{
                                Item         = 'HostGroup'
                                Type         = 'Created'
                                FilterScript = { $_.name -eq $Name }
                            }
                            $CreatedId = (Get-ConfigItem @Param).id
                            if ($CreatedId) {
                                ,$CreatedId
                            } elseif ($ForceEnabled -eq $true) {
                                ,(Get-ConfigItem @Param -Type 'Cid').id
                            }
                        }
                    }
                    if ($Content.GroupIds) {
                        $Param = @{
                            Command = "New-Falcon$($Pair.Key)"
                            Content = $Content
                        }
                        $Created = Invoke-ConfigItem @Param
                        if ($Created) {
                            Write-Host "Created $($Pair.Key) '$($Created.value)'."
                        }
                        $Created
                    } else {
                        Write-Warning "Unable to create '$($Content.value)' [missing_assignment]"
                    }
                }
            }
        }
        if ($ConfigData.FirewallGroup.Import) {
            # Create Firewall Rule Groups
            $ConfigData.FirewallGroup['Created'] = foreach ($Import in $ConfigData.FirewallGroup.Import) {
                # Set required fields
                $Content = @{
                    Name    = $Import.name
                    Enabled = $Import.enabled
                }
                switch ($Import) {
                    # Add optional fields
                    { $_.description } { $Content['Description'] = $_.description }
                    { $_.comment }     { $Content['Comment'] = $_.comment }
                }
                if ($Import.rule_ids) {
                    # Select required fields for each individual rule
                    $CreateFields = $ConfigFields.FirewallRule.Import | Where-Object { $_ -ne 'id' -and
                        $_ -ne 'family' }
                    $Rules = $ConfigData.FirewallRule.Import | Where-Object {
                        $Import.rule_ids -contains $_.family } | Select-Object $CreateFields
                    @($Rules).foreach{
                        if ($_.name.length -gt 64) {
                            # Trim rule names to 64 characters
                            $_.name = ($_.name).SubString(0,63)
                        }
                    }
                    $Content['Rules'] = $Rules
                }
                $Param = @{
                    Command = 'New-FalconFirewallGroup'
                    Content = $Content
                }
                $NewGroup = Invoke-ConfigItem @Param
                if ($NewGroup) {
                    # Output object with 'id' and 'name'
                    [PSCustomObject] @{
                        id   = $NewGroup
                        name = $Import.name
                    }
                    $Message = "Created FirewallGroup '$($Import.name)'"
                    if ($Rules) {
                        $Message += " with $(($Rules | Measure-Object).Count) rules"
                    }
                    Write-Host "$Message."
                }
            }
        }
        if ($ConfigData.IoaGroup.Import) {
            # Create IOA Rule groups
            $ConfigData.IoaGroup['Created'] = foreach ($Import in $ConfigData.IoaGroup.Import) {
                # Set required fields
                $Content = @{
                    Platform = $Import.platform
                    Name     = $Import.name
                }
                switch ($Import) {
                    # Add optional fields
                    { $_.description } { $Content['Description'] = $_.description }
                    { $_.comment }     { $Content['Comment'] = $_.comment }
                }
                $Param = @{
                    Command = 'New-FalconIoaGroup'
                    Content = $Content
                }
                $NewGroup = Invoke-ConfigItem @Param
                if ($NewGroup) {
                    Write-Host "Created $($NewGroup.platform) IoaGroup '$($NewGroup.name)'."
                    # Get date for adding 'comment' fields
                    $FileDate = Get-Date -Format FileDate
                    if ($Import.rules) {
                        $NewRules = Compress-Object ($Import.rules | Select-Object $ConfigFields.IoaRule.Create)
                        if ($NewRules) {
                            $NewGroup.rules = foreach ($Rule in $NewRules) {
                                # Create IOA Rule within IOA Group
                                $Content = @{
                                    RulegroupId     = $NewGroup.id
                                    Name            = $Rule.name
                                    PatternSeverity = $Rule.pattern_severity
                                    RuletypeId      = $Rule.ruletype_id
                                    DispositionId   = $Rule.disposition_id
                                    FieldValues     = [array] $Rule.field_values
                                }
                                @('description', 'comment').foreach{
                                    if ($Rule.$_) {
                                        $Content[$_] = $Rule.$_
                                    }
                                }
                                $Param = @{
                                    Command = 'New-FalconIoaRule'
                                    Content = $Content
                                }
                                $Created = Invoke-ConfigItem @Param
                                if ($Created) {
                                    Write-Host "Created IoaRule '$($Created.name)'."
                                }
                                if ($Created.enabled -eq $false -and $Rule.enabled -eq $true) {
                                    # Enable IOA Rule
                                    $Created.enabled = $true
                                    $Param = @{
                                        Command = 'Edit-FalconIoaRule'
                                        Content = @{
                                            RulegroupId = $NewGroup.id
                                            RuleUpdates = $Created
                                            Comment     = if ($Rule.comment) {
                                                $Rule.comment
                                            } else {
                                                "Enabled $FileDate by 'Import-FalconConfig'"
                                            }
                                        }
                                    }
                                    $Enabled = Invoke-ConfigItem @Param
                                    if ($Enabled) {
                                        # Output enable rule request result
                                        $Enabled
                                        Write-Host "Enabled IoaRule '$($Created.name)'."
                                    }
                                } else {
                                    # Output create rule request result
                                    $Created
                                }
                            }
                        }
                    }
                    if ($Import.enabled -eq $true) {
                        # Enable IOA Group
                        $Param = @{
                            Command = 'Edit-FalconIoaGroup'
                            Content = @{
                                Id          = $NewGroup.id
                                Name        = $NewGroup.name
                                Enabled     = $true
                                Description = if ($NewGroup.description) {
                                    $NewGroup.description
                                } else {
                                    "Imported $FileDate by 'Import-FalconConfig'"
                                }
                                Comment     = if ($NewGroup.comment) {
                                    $NewGroup.comment
                                } else {
                                    "Enabled $FileDate by 'Import-FalconConfig'"
                                }
                            }
                        }
                        $Enabled = Invoke-ConfigItem @Param
                        if ($Enabled) {
                            # Output enabled result
                            $Enabled
                            Write-Host "Enabled IoaGroup '$($Enabled.name)'."
                        }
                    } else {
                        # Output group creation result
                        $NewGroup
                    }
                }
            }
        }
        foreach ($Pair in $ConfigData.GetEnumerator().Where({ $_.Key -match '^.*Policy$' -and $_.Value.Import })) {
            # Create policies
            $Created = Invoke-ConfigArray -Item $Pair.Key
            if ($Created) {
                foreach ($Item in $Created) {
                    Write-Host "Created $($Item.platform_name) $($Pair.Key) '$($Item.name)'."
                }
                $ConfigData.($Pair.Key)['Created'] = foreach ($Policy in $Created) {
                    $Param = @{
                        Item = $Pair.Key
                        Type = 'Import'
                        FilterScript = { $_.platform_name -eq $Policy.platform_name -and $_.name -eq $Policy.name }
                    }
                    $Import = Get-ConfigItem @Param
                    if ($Import.settings -or $Import.prevention_settings) {
                        if ($Pair.Key -eq 'FirewallPolicy') {
                            # Update 'FirewallPolicy' with settings
                            $Content = @{
                                PolicyId        = $Policy.id
                                PlatformId      = $Import.settings.platform_id
                                Enforce         = $Import.settings.enforce
                                DefaultInbound  = $Import.settings.default_inbound
                                DefaultOutbound = $Import.settings.default_outbound
                                MonitorMode     = $Import.settings.test_mode
                            }
                            [array] $RuleGroupIds = if ($Import.settings.rule_group_ids) {
                                # Using 'rule_group_id', match 'name' of imported group
                                $Param = @{
                                    Item         = 'FirewallGroup'
                                    Type         = 'Import'
                                    FilterScript = { $Import.settings.rule_group_ids -contains $_.id }
                                }
                                $GroupNames = (Get-ConfigItem @Param).name
                                foreach ($Name in $GroupNames) {
                                    $Param = @{
                                        Item         = 'FirewallGroup'
                                        Type         = 'Created'
                                        FilterScript = { $_.Name -eq $Name }
                                    }
                                    # Match 'name' to created rule group id
                                    ,(Get-ConfigItem @Param).id
                                }
                            }
                            if ($RuleGroupIds) {
                                # Add created Rule Groups
                                $Content['RuleGroupIds'] = $RuleGroupIds
                            }
                            $Param = @{
                                Command = 'Edit-FalconFirewallSetting'
                                Content = $Content
                            }
                            @(Invoke-ConfigItem @Param).foreach{
                                if ($_.writes.resources_affected -eq 1) {
                                    # Append 'settings' to 'FirewallPolicy'
                                    Add-Property -Object $Policy -Name 'settings' -Value $Import.settings
                                    Write-Host "Applied settings to $($Pair.Key) '$($Policy.name)'."
                                }
                            }
                        } else {
                            # Update policies with settings
                            $Param = @{
                                Command = "Edit-Falcon$($Pair.Key)"
                                Content = @{
                                    Id       = $Policy.id
                                    Settings = if ($Import.prevention_settings) {
                                        $Import.prevention_settings
                                    } else {
                                        $Import.settings
                                    }
                                }
                            }
                            @(Invoke-ConfigItem @Param).foreach{
                                if ($_.settings -or $_.prevention_settings) {
                                    Write-Host "Applied settings to $($Pair.Key) '$($Policy.name)'."
                                }
                            }
                        }
                    }
                    if ($Pair.Key -eq 'PreventionPolicy') {
                        foreach ($IoaGroup in $Import.ioa_rule_groups) {
                            # Assign IOA rule groups to Prevention policies
                            $Param = @{
                                Item         = 'IoaGroup'
                                Type         = 'Created'
                                FilterScript = { $_.name -eq $IoaGroup.name }
                            }
                            $IoaGroupCreatedId = (Get-ConfigItem @Param).id
                            $IoaGroupId = if ($IoaGroupCreatedId) {
                                $IoaGroupCreatedId
                            } elseif ($ForceEnabled -eq $true) {
                                (Get-ConfigItem @Param -Type 'Cid').id
                            }
                            if ($IoaGroupId) {
                                $Param = @{
                                    Command = "Invoke-Falcon$($Pair.Key)Action"
                                    Content = @{
                                        Name    = 'add-rule-group'
                                        Id      = $Policy.id
                                        GroupId = $IoaGroupId
                                    }
                                }
                                @(Invoke-ConfigItem @Param).foreach{
                                    if ($_.ioa_rule_groups) {
                                        # Update 'ioa_rule_groups' for policy
                                        $Policy.ioa_rule_groups = $_.ioa_rule_groups
                                        Write-Host ("Assigned '$($IoaGroup.name)' to $($Pair.Key) " +
                                            "'$($Policy.name)'.")
                                    }
                                }
                            }
                        }
                    }
                    foreach ($HostGroup in $Import.groups) {
                        # Assign host groups to policies
                        $Param = @{
                            Item         = 'HostGroup'
                            Type         = 'Created'
                            FilterScript = { $_.name -eq $HostGroup.name }
                        }
                        $HostGroupCreatedId = (Get-ConfigItem @Param).id
                        $HostGroupId = if ($HostGroupCreatedId) {
                            $HostGroupCreatedId
                        } elseif ($ForceEnabled -eq $true) {
                            (Get-ConfigItem @Param -Type 'Cid').id
                        }
                        if ($HostGroupId) {
                            $Param = @{
                                Command = "Invoke-Falcon$($Pair.Key)Action"
                                Content = @{
                                    Name    = 'add-host-group'
                                    Id      = $Policy.id
                                    GroupId = $HostGroupId
                                }
                            }
                            @(Invoke-ConfigItem @Param).foreach{
                                if ($_.groups) {
                                    # Update 'group' for policy
                                    $Policy.groups = $_.groups
                                    Write-Host "Assigned '$($HostGroup.name)' to $($Pair.Key) '$($Policy.name)'."
                                }
                            }
                        }
                    }
                    if ($Import.enabled -eq $true -and $Policy.enabled -eq $false) {
                        $Param = @{
                            Command = "Invoke-Falcon$($Pair.Key)Action"
                            Content = @{
                                Id   = $Policy.id
                                Name = 'enable'
                            }
                        }
                        @(Invoke-ConfigItem @Param).foreach{
                            if ($_.enabled -eq $true) {
                                # Update 'enabled' for policy
                                $Policy.enabled = $_.enabled
                                Write-Host "Enabled $($Pair.Key) '$($Policy.Name)'."
                            }
                        }
                    }
                    # Output updated policy
                    $Policy
                }
            }
        }
        foreach ($Pair in $ConfigData.GetEnumerator().Where({ $_.Key -match '^.*Policy$' })) {
            if ($Pair.Value.Created -and $Pair.Value.Cid) {
                # Output precedence warning if existing policies were found in CID
                Write-Warning "Existing '$($Pair.Key)' items were found. Verify precedence!"
            }
        }
    }
    end {
        if ($ConfigData.Values.Created) {
            foreach ($Pair in $ConfigData.GetEnumerator().Where({ $_.Value.Created })) {
                @($Pair.Value.Created).foreach{
                    # Output 'created' results to CSV
                    [PSCustomObject] @{
                        type = $Pair.Key
                        id = if ($_.instance_id) {
                            $_.instance_id
                        } else {
                            $_.id
                        }
                        name = if ($_.type -and $_.value) {
                            "$($_.type):$($_.value)"
                        } elseif ($_.value) {
                            $_.value
                        } else {
                            $_.name
                        }
                        platform_name = if ($_.platform_name) {
                            $_.platform_name
                        } elseif ($_.platform) {
                            $_.platform
                        } else {
                            $null
                        }
                    } | Export-Csv -Path $OutputFile -NoTypeInformation -Append
                }
            }
            if (Test-Path $OutputFile) {
                Get-ChildItem -Path $OutputFile
            }
        } else {
            Write-Warning 'No items created.'
        }
    }
}