Skip to main content

CSV Configuration Best Practices

This page provides cross-product guidance for working with CSV files effectively, whether you're deploying Entra Internet Access or Entra Private Access.

Editing CSV Files

Excel vs Text Editors

When to Use Excel ✅

  • Best for: Initial review, sorting, filtering complex datasets
  • Advantages: Visual layout, easy filtering, column management
  • Best practices:
    • Open via Data → From Text/CSV (not double-click)
    • Set all columns to Text format (not General)
    • Save as "CSV UTF-8 (Comma delimited)"
    • Be cautious of auto-formatting

Safe Excel Workflow:

1. Excel → Data tab → Get Data → From Text/CSV
2. Select your CSV file
3. Set Delimiter: Comma
4. Click Transform Data
5. Select all columns → Right-click → Change Type → Text
6. Close & Load
7. Make edits
8. File → Save As → CSV UTF-8 (Comma delimited) (*.csv)

When to Use Text Editors ✅

  • Best for: Small changes, bulk find/replace, avoiding formatting issues

  • Recommended editors:

    • VS Code (with Rainbow CSV extension)
    • Notepad++
    • Sublime Text
    • Any plain text editor
  • Advantages: No auto-formatting, precise control, version control friendly

  • Best for:

    • Replacing placeholders across many rows
    • Fixing delimiter issues
    • Ensuring UTF-8 encoding

VS Code CSV Tips:

  1. Install "Rainbow CSV" extension for colored columns
  2. Use Find/Replace (Ctrl+H) for bulk updates
  3. Enable "Files: Encoding" → UTF-8
  4. Use Edit Column mode (Alt+Click) for vertical editing

Common Excel Pitfalls to Avoid

IssueCauseSolution
Leading zeros removed0800123456 becomes 800123456Format column as Text before import
Dates auto-converted2-5 becomes Feb 5Format column as Text before import
Scientific notation1234567890 becomes 1.23E+09Format column as Text before import
Comma loss in port lists80,443,8080 becomes 80 443 8080Quote the cell: "80,443,8080"
Extra spaces addedExcel adds trailing spacesUse text editor or Find/Replace to remove

Delimiter Management

CSV delimiter is COMMA—but values containing commas must be quoted:

# Correct - ports with commas are quoted:
PolicyName,PolicyType,PolicyAction,RuleDestinations
Block_Social,WebContentFiltering,Block,"facebook.com;twitter.com"

# Also correct - ports with commas quoted:
SegmentId,EnterpriseAppName,Ports
SEG-001,GSA-WebApp,"80,443,8080"

Semicolon is used WITHIN cells for lists:

# Multiple groups separated by semicolon:
EntraGroups
HR-Users;HR-Admins;Finance-Team

# Multiple destinations separated by semicolon:
RuleDestinations
github.com;*.github.io;stackoverflow.com

Testing Strategy

Phase 1: Syntax Validation

Before provisioning, validate CSV syntax:

# For EIA: Import CSV and check for errors
$policies = Import-Csv -Path ".\policies.csv"
$profiles = Import-Csv -Path ".\security_profiles.csv"

# Validate required columns exist
$requiredPolicyCols = @('PolicyName', 'PolicyType', 'PolicyAction', 'RuleType', 'RuleDestinations', 'RuleName', 'Provision')
$policyCols = $policies[0].PSObject.Properties.Name
$missing = $requiredPolicyCols | Where-Object { $_ -notin $policyCols }
if ($missing) { Write-Warning "Missing columns: $($missing -join ', ')" }

# Check for placeholder values
$policies | Where-Object { $_.EntraGroups -like '*_Replace_Me*' } |
Select-Object PolicyName, EntraGroups

Phase 2: Preview with WhatIf

Always use -WhatIf first to see what will be created without actually creating it:

EIA Example:

Start-EntraInternetAccessProvisioning `
-PoliciesCsvPath ".\policies.csv" `
-SecurityProfilesCsvPath ".\security_profiles.csv" `
-WhatIf

EPA Example:

Start-EntraPrivateAccessProvisioning `
-ProvisioningConfigPath ".\epa_config.csv" `
-WhatIf

Review the output carefully:

  • ✅ Object names look correct
  • ✅ No placeholder values visible
  • ✅ Group assignments are as expected
  • ✅ Priorities/orders make sense

Phase 3: Pilot Deployment

Start small and expand gradually:

Set Provision=no for most items, enable only a few for testing:

# EIA: Test with one policy first
PolicyName,PolicyType,PolicyAction,...,Provision
Test_Policy,WebContentFiltering,Allow,...,yes
Block_Social,WebContentFiltering,Block,...,no
Block_Malware,WebContentFiltering,Block,...,no
# EPA: Test with one application first
SegmentId,EnterpriseAppName,...,Provision
SEG-001,GSA-TestApp,...,Yes
SEG-002,GSA-ProductionApp,...,No
SEG-003,GSA-CriticalApp,...,No

Option B: Test Security Profile/Group

Create a dedicated test profile assigned to a small pilot group:

# EIA: Add test profile with priority 999 (lowest)
SecurityProfileName,Priority,SecurityProfileLinks,CADisplayName,EntraGroups,Provision
Test_Profile,999,Block_Malware:100,CA_Test_Pilot,IT_Test_Group,yes
Standard_Profile,100,Block_Social:100;Block_Malware:200,CA_Standard,All_Users,no

Pilot group strategy:

  1. Create Entra group: GSA-Pilot-Users
  2. Add 3-5 volunteers from IT team
  3. Deploy configuration assigned only to pilot group
  4. Monitor for 2-3 days
  5. Gather feedback and adjust
  6. Expand to broader population

Phase 4: Incremental Rollout

Don't deploy everything at once. Use a phased approach:

Week 1: Security threats only (malware, phishing)

Provision=yes: Block_Malware, Block_Phishing
Provision=no: Everything else

Week 2: Add productivity policies

Provision=yes: Block_Malware, Block_Phishing, Block_Social
Provision=no: Everything else

Week 3: Add TLS inspection

Provision=yes: All WebContentFiltering + TLS policies

Week 4: Full deployment

Provision=yes: Everything

Phase 5: Production Monitoring

After deployment, monitor these areas:

EIA Monitoring:

  • Entra Portal → Global Secure Access → Internet Access → Traffic logs
  • Watch for unexpected blocks
  • Review user feedback/helpdesk tickets
  • Check policy hit counts

EPA Monitoring:

  • Entra Portal → Enterprise Applications → Check sign-in logs
  • Verify connector health
  • Test application access from different users
  • Check for connectivity issues

Version Control

Why Version Control CSV Files?

  • Track changes: Who changed what and when
  • Rollback capability: Restore previous working configuration
  • Collaboration: Multiple team members can propose changes
  • Audit trail: Compliance and change management documentation
  • Branching: Test changes in dev/test branches before production

Git Workflow for CSV Configurations

# Initialize repository
cd C:\GSAConfigs
git init
git add policies.csv security_profiles.csv
git commit -m "Initial EIA configuration"

# Create development branch for testing
git checkout -b test-social-media-block
# Make changes to CSVs...
git add policies.csv
git commit -m "Added social media blocking policy"

# Test in non-production environment
# If successful, merge to main
git checkout main
git merge test-social-media-block

# Tag production releases
git tag -a v1.0-prod -m "Production release 2026-02-27"

CSV-Friendly Git Practices

Use descriptive commit messages:

❌ git commit -m "update"
✅ git commit -m "Added TLS bypass for banking sites in Finance profile"

Review diffs before committing:

git diff policies.csv

Use .gitignore for working files:

# .gitignore
*.log
*_backup*.csv
.DS_Store
*.tmp

Backup Strategy

Local backups with timestamps:

# PowerShell: Create timestamped backup before editing
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
Copy-Item "policies.csv" "backups/policies_$timestamp.csv"
Copy-Item "security_profiles.csv" "backups/security_profiles_$timestamp.csv"

Cloud backups:

  • Store CSVs in OneDrive/SharePoint (with versioning enabled)
  • Use Azure DevOps Repos or GitHub (private repos for sensitive data)
  • Scheduled backups to blob storage

Multi-Tenant Deployment

Scenario: Dev → Test → Prod Promotion

Challenge: Deploy same configuration across multiple tenants

Solution: Parameterized CSVs

Step 1: Create Template CSV with Placeholders

# policies_template.csv
PolicyName,PolicyType,PolicyAction,...,EntraGroups
Block_Social,WebContentFiltering,Block,...,{{ALL_USERS_GROUP}}
Allow_DevTools,WebContentFiltering,Allow,...,{{DEVELOPERS_GROUP}}

Step 2: Environment-Specific Parameter Files

# dev_params.ps1
$params = @{
ALL_USERS_GROUP = "Dev-All-Users"
DEVELOPERS_GROUP = "Dev-Developers"
}

# test_params.ps1
$params = @{
ALL_USERS_GROUP = "Test-All-Users"
DEVELOPERS_GROUP = "Test-Developers"
}

# prod_params.ps1
$params = @{
ALL_USERS_GROUP = "All-Company-Users"
DEVELOPERS_GROUP = "IT-Department"
}

Step 3: Replace Placeholders Before Provisioning

# deploy_to_env.ps1
param(
[Parameter(Mandatory)]
[ValidateSet('dev', 'test', 'prod')]
[string]$Environment
)

# Load parameters
. ".\$Environment_params.ps1"

# Read template
$content = Get-Content ".\policies_template.csv" -Raw

# Replace placeholders
foreach ($key in $params.Keys) {
$content = $content -replace "{$($key)}", $params[$key]
}

# Save environment-specific CSV
$content | Out-File ".\policies_$Environment.csv" -Encoding UTF8

# Connect to appropriate tenant
Connect-MgGraph -TenantId $params.TenantId

# Deploy
Start-EntraInternetAccessProvisioning `
-PoliciesCsvPath ".\policies_$Environment.csv" `
-SecurityProfilesCsvPath ".\security_profiles_$Environment.csv"

Tenant-to-Tenant Migration

Use case: Backup/restore, corporate acquisitions, environment replication

EIA Export → Edit → Import:

# Source tenant: Export
Connect-MgGraph -TenantId "source-tenant-id"
Export-EIAConfig -OutputPath "C:\Backup\"

# Edit CSVs: Update group names for target tenant

# Target tenant: Import
Connect-MgGraph -TenantId "target-tenant-id"
Start-EntraInternetAccessProvisioning `
-PoliciesCsvPath "C:\Backup\policies.csv" `
-SecurityProfilesCsvPath "C:\Backup\security_profiles.csv"

EPA Export → Edit → Import:

# Source tenant: Export
Connect-MgGraph -TenantId "source-tenant-id"
Export-EPAConfig -OutputPath "C:\Backup\"

# Edit CSV: Update connector groups and group names for target tenant

# Target tenant: Import
Connect-MgGraph -TenantId "target-tenant-id"
Start-EntraPrivateAccessProvisioning `
-ProvisioningConfigPath "C:\Backup\epa_config.csv"

Selective Provisioning Patterns

Pattern 1: Department-by-Department Rollout

# Mark policies for specific departments with Provision=yes
PolicyName,PolicyType,PolicyAction,...,Provision
# Week 1: IT Department only
IT_Policies,...,yes

# Week 2-4: Other departments (set to no initially)
Finance_Policies,...,no
HR_Policies,...,no
Sales_Policies,...,no

Pattern 2: Policy-Type Phasing

# Phase 1: Security threats only
Block_Malware,WebContentFiltering,Block,...,yes
Block_Phishing,WebContentFiltering,Block,...,yes

# Phase 2: Add productivity controls (change to yes later)
Block_Social,WebContentFiltering,Block,...,no
Block_Entertainment,WebContentFiltering,Block,...,no

# Phase 3: Add TLS inspection (change to yes later)
TLS_Bypass_Finance,TLSInspection,Bypass,...,no

Pattern 3: Priority-Based Testing

EIA: Test high-priority profiles first (lower priority number = higher precedence):

SecurityProfileName,Priority,...,Provision
Test_Profile,100,...,yes # Deploy first (highest priority)
Standard_Profile,200,...,no # Deploy after testing
Guest_Profile,300,...,no # Deploy last

Pattern 4: Canary Deployment

Create two identical profiles with different groups:

# Canary group (5% of users)
Canary_Profile,100,Block_Social:100,...,GSA-Canary-Group,yes

# Main population (wait 1 week)
Production_Profile,200,Block_Social:100,...,All-Users,no

After 1 week of monitoring canary:

  • If successful → Set Production_Profile Provision=yes
  • If issues found → Fix policies and re-test canary

Performance & Scale Considerations

EIA Limits

  • Filtering Policies: Maximum 100 policies per tenant
  • Rules per Policy: Recommended < 100 rules per policy (no hard limit)
  • Security Profiles: Recommended < 20 profiles (no hard limit)

Optimization strategies:

  1. Use web categories instead of FQDNs: One category rule can replace hundreds of FQDN rules
  2. Consolidate similar policies: Merge rules into fewer policiespolicies
  3. Remove unused policies: Clean up test/deprecated policies regularly

EPA Limits

  • Enterprise Applications: No documented hard limit (thousands supported)
  • Segments per Application: Recommended < 500 segments per app
  • Connector Groups: Plan for geographic distribution and redundancy

Optimization strategies:

  1. Group related segments: Multi-segment apps instead of many single-segment apps
  2. Use Quick Access for broad access: Instead of hundreds of individual segments
  3. Plan connector placement: Co-locate connectors with target applications

Troubleshooting Workflow

When provisioning fails or produces unexpected results:

Step 1: Check CSV Syntax

# Import and validate structure
$data = Import-Csv -Path ".\policies.csv"
$data | Format-Table -AutoSize | Out-String -Width 200

Step 2: Review Provisioning Logs

Provisioning functions create detailed log files:

  • EIA: YYYYMMDD_HHMMSS_EIA_Provisioning.log
  • EPA: YYYYMMDD_HHMMSS_EPA_Provisioning.log

Look for:

  • ERROR messages
  • WARNING about placeholders
  • INFO about skipped rows (Provision=no)
  • HTTP response codes from Graph API

Step 3: Verify Graph Permissions

# Check current scopes
(Get-MgContext).Scopes

# Reconnect with required scopes if missing
Connect-MgGraph -Scopes "NetworkAccessPolicy.ReadWrite.All","Application.ReadWrite.All","Group.Read.All"

Step 4: Incremental Testing

Set all Provision=no except ONE row, then provision:

# Test one row at a time
PolicyName,...,Provision
Test_Policy_1,...,yes
Test_Policy_2,...,no
Test_Policy_3,...,no

This isolates which specific row is causing the issue.

Step 5: Manual Verification

After provisioning, verify in Entra Portal:

  • EIA: Global Secure Access → Internet Access → Filtering policies
  • EPA: Enterprise Applications → Search for your app names
  • CA Policies: Entra ID → Security → Conditional Access

Next Steps

Ready to apply these best practices?


Pro Tip

Combine these best practices with the Migration Workflow for a comprehensive deployment strategy. Start small, test thoroughly, and expand incrementally!