-
Notifications
You must be signed in to change notification settings - Fork 267
Auto-detect infrastructure provider from infra directory files #6461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: spboyer <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements automatic detection of infrastructure providers (Bicep or Terraform) when infra.provider is not explicitly set in azure.yaml. The detection scans the infra directory for characteristic file extensions to determine the appropriate provider.
Key changes:
- Added
detectProviderFromFilesfunction that scans infra directory for.bicep/.bicepparamor.tf/.tfvarsfiles - Updated
ProjectInfrastructureto automatically detect and set the provider when not specified - Added comprehensive unit tests covering all detection scenarios including error cases
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
cli/azd/pkg/project/importer.go |
Adds detectProviderFromFiles function and integrates auto-detection into ProjectInfrastructure workflow |
cli/azd/pkg/project/importer_test.go |
Adds comprehensive table-driven unit tests for provider detection, including edge cases for mixed files, empty directories, and directory exclusion |
.gitignore |
Adds cli/azd/azd binary to gitignore (duplicate entry) |
| func TestDetectProviderFromFiles(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| files []string | ||
| expectedResult provisioning.ProviderKind | ||
| expectError bool | ||
| errorContains string | ||
| }{ | ||
| { | ||
| name: "only bicep files", | ||
| files: []string{"main.bicep", "modules.bicep"}, | ||
| expectedResult: provisioning.Bicep, | ||
| expectError: false, | ||
| }, | ||
| { | ||
| name: "only bicepparam files", | ||
| files: []string{"main.bicepparam"}, | ||
| expectedResult: provisioning.Bicep, | ||
| expectError: false, | ||
| }, | ||
| { | ||
| name: "only terraform files", | ||
| files: []string{"main.tf", "variables.tf"}, | ||
| expectedResult: provisioning.Terraform, | ||
| expectError: false, | ||
| }, | ||
| { | ||
| name: "only tfvars files", | ||
| files: []string{"terraform.tfvars"}, | ||
| expectedResult: provisioning.Terraform, | ||
| expectError: false, | ||
| }, | ||
| { | ||
| name: "both bicep and terraform files", | ||
| files: []string{"main.bicep", "main.tf"}, | ||
| expectedResult: provisioning.NotSpecified, | ||
| expectError: true, | ||
| errorContains: "both Bicep and Terraform files detected", | ||
| }, | ||
| { | ||
| name: "no IaC files", | ||
| files: []string{"readme.md", "config.json"}, | ||
| expectedResult: provisioning.NotSpecified, | ||
| expectError: false, | ||
| }, | ||
| { | ||
| name: "empty directory", | ||
| files: []string{}, | ||
| expectedResult: provisioning.NotSpecified, | ||
| expectError: false, | ||
| }, | ||
| { | ||
| name: "mixed with bicep and non-IaC files", | ||
| files: []string{"main.bicep", "readme.md", "config.json"}, | ||
| expectedResult: provisioning.Bicep, | ||
| expectError: false, | ||
| }, | ||
| { | ||
| name: "mixed with terraform and non-IaC files", | ||
| files: []string{"main.tf", "readme.md", "LICENSE"}, | ||
| expectedResult: provisioning.Terraform, | ||
| expectError: false, | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| // Create temporary directory | ||
| tmpDir, err := os.MkdirTemp("", "test-detect-provider-*") | ||
| require.NoError(t, err) | ||
| defer os.RemoveAll(tmpDir) | ||
|
|
||
| // Create test files | ||
| for _, fileName := range tt.files { | ||
| filePath := filepath.Join(tmpDir, fileName) | ||
| err := os.WriteFile(filePath, []byte("test content"), 0600) | ||
| require.NoError(t, err) | ||
| } | ||
|
|
||
| // Test detectProviderFromFiles | ||
| result, err := detectProviderFromFiles(tmpDir) | ||
|
|
||
| if tt.expectError { | ||
| require.Error(t, err) | ||
| require.Contains(t, err.Error(), tt.errorContains) | ||
| } else { | ||
| require.NoError(t, err) | ||
| } | ||
|
|
||
| require.Equal(t, tt.expectedResult, result) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestDetectProviderFromFilesNonExistentDirectory(t *testing.T) { | ||
| // Test with non-existent directory | ||
| result, err := detectProviderFromFiles("/nonexistent/path/that/does/not/exist") | ||
| require.NoError(t, err, "should not error when directory doesn't exist") | ||
| require.Equal(t, provisioning.NotSpecified, result) | ||
| } | ||
|
|
||
| func TestDetectProviderFromFilesIgnoresDirectories(t *testing.T) { | ||
| // Create temporary directory structure | ||
| tmpDir, err := os.MkdirTemp("", "test-detect-provider-dirs-*") | ||
| require.NoError(t, err) | ||
| defer os.RemoveAll(tmpDir) | ||
|
|
||
| // Create subdirectories with IaC-like names | ||
| err = os.Mkdir(filepath.Join(tmpDir, "main.bicep"), 0755) | ||
| require.NoError(t, err) | ||
| err = os.Mkdir(filepath.Join(tmpDir, "main.tf"), 0755) | ||
| require.NoError(t, err) | ||
|
|
||
| // Create a real Bicep file | ||
| err = os.WriteFile(filepath.Join(tmpDir, "resources.bicep"), []byte("test"), 0600) | ||
| require.NoError(t, err) | ||
|
|
||
| // Should detect Bicep and ignore directories | ||
| result, err := detectProviderFromFiles(tmpDir) | ||
| require.NoError(t, err) | ||
| require.Equal(t, provisioning.Bicep, result) | ||
| } |
Copilot
AI
Jan 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While detectProviderFromFiles is well-tested in isolation, there's no integration test that verifies the complete auto-detection flow through ProjectInfrastructure. Consider adding a test similar to TestImportManagerProjectInfrastructure that creates a temporary project directory with Bicep or Terraform files and verifies that ProjectInfrastructure correctly auto-detects and sets the provider when infra.provider is not specified in the project configuration.
Enables automatic detection of Bicep or Terraform as the infrastructure provider when
infra.provideris not explicitly set inazure.yaml. The detection scans the infra directory for.bicep/.bicepparamor.tf/.tfvarsfiles.Implementation
Added
detectProviderFromFilesfunctionprovisioning.Bicepif only Bicep files presentprovisioning.Terraformif only Terraform files presentprovisioning.NotSpecifiedfor empty/non-existent directoriesUpdated
ProjectInfrastructurefunctioninfraRootpath wheninfraOptions.Provider == provisioning.NotSpecifiedauto-detected infrastructure provider: <bicep|terraform>Example behavior
Backward Compatibility
Explicit
infra.providerinazure.yamlalways takes precedence. Auto-detection only runs when provider isNotSpecified. All existing projects continue to work unchanged.Original prompt
This pull request was created from Copilot chat.
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.