This repository demonstrates two different patterns for securing Azure Function App access to Azure Storage Accounts, plus API Management integration:
- Function App 1 (
amun-fd-func-1): Uses IP-based access restrictions tostgsnefffds1 - Function App 2 (
amun-fd-func-2): Uses VNet integration and private endpoint to accessstgsnefffds2 - APIM Integration: Automatically creates and configures API operations for Function App 2
- Function App 1 → Storage Account 1 (Public endpoint with IP allowlist)
- Access controlled by function app's outbound IP addresses
- Suitable for scenarios where VNet integration is not required
- Function App 2 → Storage Account 2 (Private endpoint only)
- Access via Azure Virtual Network with private DNS resolution
- Complete network isolation from public internet
- APIM API automatically created for Function App 2
- All function endpoints exposed as APIM operations
- No manual API configuration required
- See APIM-INTEGRATION.md for details
- Azure Subscription with appropriate permissions
- GitHub Repository with federated identity configured
- Terraform State Storage Account named
snefftfdemo - GitHub Secrets configured:
AZURE_CLIENT_IDAZURE_TENANT_IDAZURE_SUBSCRIPTION_ID
├── terraform/
│ ├── main.tf # Main Terraform configuration (includes APIM API setup)
│ ├── variables.tf # Variable definitions
│ ├── outputs.tf # Output values (includes APIM endpoints)
│ ├── locals.tf # Local values and tagging
│ ├── backend.tf # Terraform backend configuration
│ ├── networking.tf # VNet, subnets, private endpoints
│ └── security.tf # Network security rules
├── scripts/
│ ├── Test-FunctionApp1-Connectivity.ps1 # Connectivity test for Pattern 1
│ ├── Test-FunctionApp2-Connectivity.ps1 # Connectivity test for Pattern 2
│ └── Test-APIM-Integration.ps1 # Test APIM API operations (NEW)
├── .github/workflows/
│ └── terraform-deploy.yml # CI/CD pipeline
├── APIM-INTEGRATION.md # APIM integration documentation (NEW)
└── README.md
git clone https://github.com/nanigan/fd-terraform-demo.git
cd fd-terraform-demoEnsure the Terraform state storage account snefftfdemo exists with appropriate permissions for your federated identity.
Edit terraform/variables.tf if you need to customize:
- Resource group name
- Storage account names
- Function app names
- Network addressing
- Push changes to the
mainbranch - Monitor the workflow in GitHub Actions
- Review the Terraform plan and approve if needed
cd terraform
# Initialize Terraform
terraform init
# Format and validate
terraform fmt -recursive
terraform validate
# Plan deployment
terraform plan -out=tfplan
# Apply changes
terraform apply tfplanTest all Function App 2 endpoints through APIM:
# Test all APIM endpoints
.\scripts\Test-APIM-Integration.ps1
# Test with subscription key (if enabled)
.\scripts\Test-APIM-Integration.ps1 -SubscriptionKey "your-key-here"
# Verbose output
.\scripts\Test-APIM-Integration.ps1 -VerboseOr test manually:
# Get APIM endpoints from Terraform output
terraform output apim_api_endpoints
# Test a specific endpoint
Invoke-RestMethod -Uri "https://apim-fd-public-test2.azure-api.net/funcapp2/HealthCheck"-
Function App 1 Test:
- Deploy
scripts/Test-FunctionApp1-Connectivity.ps1as an HTTP-triggered function - Access via:
https://amun-fd-func-1.azurewebsites.net/api/test-connectivity
- Deploy
-
Function App 2 Test:
- Deploy
scripts/Test-FunctionApp2-Connectivity.ps1as an HTTP-triggered function - Access via:
https://amun-fd-func-2.azurewebsites.net/api/test-connectivity - Or via APIM:
https://apim-fd-public-test2.azure-api.net/funcapp2/...
- Deploy
- ✅ DNS Resolution to public IP
- ✅ HTTPS connectivity via public endpoint
- ✅ Storage access using function app's outbound IP
- ℹ️ Outbound IP addresses listed
- ✅ DNS Resolution to private IP (10.x.x.x)
- ✅ HTTPS connectivity via private endpoint
- ✅ VNet integration confirmed
- ✅ Storage access via private network
- ✅ Public endpoint access blocked
| Resource | Name | Type | Purpose |
|---|---|---|---|
| Resource Group | rg-fd-demo-test |
Resource Group | Container for all resources |
| Storage Account 1 | stgsnefffds1 |
Storage Account | Public with IP restrictions |
| Storage Account 2 | stgsnefffds2 |
Storage Account | Private endpoint only |
| Function App 1 | amun-fd-func-1 |
Function App | Tests IP-based access |
| Function App 2 | amun-fd-func-2 |
Function App | Tests private endpoint access |
| App Service Plan | plan-fd-demo-test-eus |
App Service Plan | S1 SKU for both functions |
| APIM Instance | apim-fd-public-test2 |
API Management | Gateway for Function App 2 |
| APIM API | func-app-2-api |
APIM API | Auto-configured operations |
| Virtual Network | vnet-fd-demo-test |
Virtual Network | 10.0.0.0/16 |
| Function Subnet | snet-functions |
Subnet | 10.0.1.0/24 |
| Private Endpoint Subnet | snet-private-endpoints |
Subnet | 10.0.2.0/24 |
| Private Endpoint | pe-stgsnefffds2 |
Private Endpoint | Storage account private access |
| Private DNS Zone | privatelink.blob.core.windows.net |
DNS Zone | Private DNS resolution |
- Function App 1: No VNet integration, uses public outbound IPs
- Function App 2: VNet integrated with route all traffic enabled
- Storage Account 1: Public endpoint with IP allowlist for Function App 1 IPs
- Storage Account 2: Private endpoint only, no public access
- Storage account denies all traffic by default
- Function app's outbound IPs are explicitly allowed
- Public endpoint remains accessible but restricted
- Storage account has public access completely disabled
- Access only via private endpoint in VNet
- DNS resolution points to private IP addresses
- Function app routes all traffic through VNet
terraform output- Function app URLs and outbound IPs
- Storage account names and endpoints
- Private endpoint IP address
- Resource group and VNet names
-
IP Restrictions Not Working:
- Check if function app IPs are correctly added to storage account
- Verify function app has generated outbound IPs
-
Private Endpoint Resolution Issues:
- Ensure private DNS zone is linked to VNet
- Verify function app has VNet integration enabled
-
Function Deployment Issues:
- Check if App Service Plan has sufficient capacity
- Verify storage account connectivity for function runtime
To remove all resources:
terraform destroy- ✅ Remote state backend with OIDC authentication
- ✅ Consistent resource naming and tagging
- ✅ Modular configuration files
- ✅ Proper variable validation
- ✅ Comprehensive outputs
- ✅ Managed Identity for authentication
- ✅ Network isolation patterns
- ✅ Least privilege access
- ✅ Private DNS resolution
- ✅ Secure communication channels
- ✅ Infrastructure as Code
- ✅ Automated CI/CD pipeline
- ✅ Environment separation
- ✅ Plan approval workflow
- ✅ Artifact management
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
This demo is provided for educational purposes. Modify as needed for your requirements.
For questions about this demo environment, please review the test scripts and Terraform configuration for troubleshooting guidance.

