generated from terraform-linters/tflint-ruleset-template
-
Notifications
You must be signed in to change notification settings - Fork 32
Feat appinsights hidden tags on app services #430
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
Open
pregress
wants to merge
15
commits into
terraform-linters:master
Choose a base branch
from
pregress:feat_appinsights_hidden_tags
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 10 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
1c58d08
azurerm_linux_web_app_app_insights_hidden_link
pregress 233dc01
azurerm_windows_web_app_app_insights_hidden_link
pregress c177bce
Consolidate into single rule
pregress 425e312
rename to app service and handle functions
pregress fa87636
Site config for functions
pregress 1184190
remove relic
pregress d4f1d1d
remove relic 2
pregress 90be9c6
More robust
pregress b4e1995
PR feedback
pregress c8b5b39
Merge branch 'master' into feat_appinsights_hidden_tags
pregress ecc1102
Update rules/azurerm_app_service_app_insights_hidden_link.go
pregress 66352f4
Update docs/rules/azurerm_app_service_app_insights_hidden_link.md
pregress b53f830
Update rules/azurerm_app_service_app_insights_hidden_link.go
pregress 7b1b225
Update docs/rules/azurerm_app_service_app_insights_hidden_link.md
pregress ec4e259
prfeedback + flex consumption func
pregress File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
127 changes: 127 additions & 0 deletions
127
docs/rules/azurerm_app_service_app_insights_hidden_link.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| # azurerm_app_service_app_insights_hidden_link | ||
|
|
||
| Disallow missing lifecycle ignore_changes for Application Insights hidden-link tags. | ||
|
|
||
| This rule applies to all Azure App Service resource types (Web Apps and Function Apps): | ||
| - `azurerm_linux_web_app` | ||
| - `azurerm_linux_web_app_slot` | ||
| - `azurerm_windows_web_app` | ||
| - `azurerm_windows_web_app_slot` | ||
| - `azurerm_linux_function_app` | ||
| - `azurerm_linux_function_app_slot` | ||
| - `azurerm_windows_function_app` | ||
| - `azurerm_windows_function_app_slot` | ||
|
|
||
| ## Configuration | ||
|
|
||
| ```hcl | ||
| rule "azurerm_app_service_app_insights_hidden_link" { | ||
| enabled = true | ||
| } | ||
| ``` | ||
|
|
||
| ## Example | ||
|
|
||
| ### Terraform Configuration | ||
|
|
||
| ```hcl | ||
| # Non-compliant: Linux Web App with Application Insights configured without ignore_changes | ||
| resource "azurerm_linux_web_app" "example" { | ||
| app_settings = { | ||
| "APPLICATIONINSIGHTS_CONNECTION_STRING" = "example" | ||
| } | ||
| } | ||
|
|
||
| # Non-compliant: Windows Web App Slot with Application Insights configured without ignore_changes | ||
| resource "azurerm_windows_web_app_slot" "example" { | ||
| name = "example-slot" | ||
| app_service_id = azurerm_windows_web_app.example.id | ||
|
|
||
| app_settings = { | ||
| "APPINSIGHTS_INSTRUMENTATIONKEY" = "example-key" | ||
| } | ||
| } | ||
|
|
||
| # Non-compliant: Windows Function App with Application Insights configured without ignore_changes | ||
| resource "azurerm_windows_function_app" "example" { | ||
| app_settings = { | ||
| "APPINSIGHTS_INSTRUMENTATIONKEY" = "example-key" | ||
| } | ||
| } | ||
|
|
||
| # Non-compliant: Linux Function App with Application Insights in site_config without ignore_changes | ||
| resource "azurerm_linux_function_app" "example" { | ||
| site_config { | ||
| application_insights_connection_string = "example-connection" | ||
| } | ||
| } | ||
|
|
||
| # Compliant: Linux Function App with proper ignore_changes | ||
| resource "azurerm_linux_function_app" "example" { | ||
| app_settings = { | ||
| "APPLICATIONINSIGHTS_CONNECTION_STRING" = "example" | ||
| } | ||
|
|
||
| lifecycle { | ||
| ignore_changes = [ | ||
| tags["hidden-link: /app-insights-conn-string"], | ||
| tags["hidden-link: /app-insights-instrumentation-key"], | ||
| tags["hidden-link: /app-insights-resource-id"], | ||
| ] | ||
| } | ||
| } | ||
|
|
||
| # Compliant: Windows Web App Slot with proper ignore_changes | ||
| resource "azurerm_windows_web_app_slot" "example" { | ||
| app_settings = { | ||
| "APPINSIGHTS_INSTRUMENTATIONKEY" = "example" | ||
| } | ||
|
|
||
| lifecycle { | ||
| ignore_changes = [ | ||
| tags["hidden-link: /app-insights-conn-string"], | ||
| tags["hidden-link: /app-insights-instrumentation-key"], | ||
| tags["hidden-link: /app-insights-resource-id"], | ||
| ] | ||
| } | ||
| } | ||
|
|
||
| # Compliant: Windows Function App with Application Insights in site_config and proper ignore_changes | ||
| resource "azurerm_windows_function_app" "example" { | ||
| site_config { | ||
| application_insights_key = "example-key" | ||
| } | ||
|
|
||
| lifecycle { | ||
| ignore_changes = [ | ||
| "tags["hidden-link: /app-insights-conn-string"]", | ||
| "tags["hidden-link: /app-insights-instrumentation-key"]", | ||
| "tags["hidden-link: /app-insights-resource-id"]", | ||
| ] | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Why | ||
|
|
||
| When Application Insights is configured for Azure App Service resources (Web Apps, Web App Slots, Function Apps, or Function App Slots for both Linux and Windows), Azure automatically adds hidden-link tags to the resource. These tags can change during deployments and may cause unnecessary Terraform diffs. Using `lifecycle { ignore_changes }` for these specific tags prevents Terraform from attempting to manage these Azure-managed tags. | ||
|
|
||
| Application Insights can be configured in two ways: | ||
| 1. **app_settings**: Using `APPLICATIONINSIGHTS_CONNECTION_STRING` or `APPINSIGHTS_INSTRUMENTATIONKEY` keys | ||
| 2. **site_config**: Using `application_insights_connection_string` or `application_insights_key` attributes (only applicable to Function Apps) | ||
|
|
||
| This rule checks for the presence of Application Insights configuration in both locations and ensures that the corresponding hidden-link tags are properly ignored. | ||
|
|
||
| ## How to Fix | ||
|
|
||
| Add a `lifecycle` block with `ignore_changes` containing the Application Insights hidden-link tags: | ||
|
|
||
| ```hcl | ||
| lifecycle { | ||
| ignore_changes = [ | ||
| "tags["hidden-link: /app-insights-conn-string"]", | ||
| "tags["hidden-link: /app-insights-instrumentation-key"]", | ||
| "tags["hidden-link: /app-insights-resource-id"]", | ||
pregress marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ] | ||
| } | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,229 @@ | ||
| package rules | ||
|
|
||
| import ( | ||
| "strings" | ||
|
|
||
| "github.com/hashicorp/hcl/v2" | ||
| "github.com/terraform-linters/tflint-plugin-sdk/hclext" | ||
| "github.com/terraform-linters/tflint-plugin-sdk/logger" | ||
| "github.com/terraform-linters/tflint-plugin-sdk/tflint" | ||
| "github.com/terraform-linters/tflint-ruleset-azurerm/project" | ||
| "github.com/zclconf/go-cty/cty" | ||
| ) | ||
|
|
||
| // AzurermAppServiceAppInsightsHiddenLinkRule checks whether lifecycle ignore_changes includes hidden-link tags when Application Insights is configured | ||
| type AzurermAppServiceAppInsightsHiddenLinkRule struct { | ||
| tflint.DefaultRule | ||
| } | ||
|
|
||
| const ( | ||
| appServiceIgnoreChangesAttrName = "ignore_changes" | ||
| appServiceAppSettingsAttrName = "app_settings" | ||
| appServiceSiteConfigAttrName = "site_config" | ||
| appServiceAppInsightsConnectionKey = "APPLICATIONINSIGHTS_CONNECTION_STRING" | ||
| appServiceAppInsightsInstrumentKey = "APPINSIGHTS_INSTRUMENTATIONKEY" | ||
| appServiceSiteConfigAppInsightsConnectionKey = "application_insights_connection_string" | ||
| appServiceSiteConfigAppInsightsKey = "application_insights_key" | ||
| ) | ||
|
|
||
| var appServiceRequiredHiddenLinkTags = []string{ | ||
| "hidden-link: /app-insights-conn-string", | ||
| "hidden-link: /app-insights-instrumentation-key", | ||
| "hidden-link: /app-insights-resource-id", | ||
| } | ||
|
|
||
| var appServiceResourceTypes = []string{ | ||
| "azurerm_linux_web_app", | ||
| "azurerm_linux_web_app_slot", | ||
| "azurerm_windows_web_app", | ||
| "azurerm_windows_web_app_slot", | ||
| "azurerm_linux_function_app", | ||
| "azurerm_linux_function_app_slot", | ||
| "azurerm_windows_function_app", | ||
| "azurerm_windows_function_app_slot", | ||
| } | ||
|
|
||
| // NewAzurermAppServiceAppInsightsHiddenLinkRule returns new rule for checking Application Insights hidden-link configuration | ||
| func NewAzurermAppServiceAppInsightsHiddenLinkRule() *AzurermAppServiceAppInsightsHiddenLinkRule { | ||
| return &AzurermAppServiceAppInsightsHiddenLinkRule{} | ||
| } | ||
|
|
||
| // Name returns the rule name | ||
| func (r *AzurermAppServiceAppInsightsHiddenLinkRule) Name() string { | ||
| return "azurerm_app_service_app_insights_hidden_link" | ||
| } | ||
|
|
||
| // Enabled returns whether the rule is enabled by default | ||
| func (r *AzurermAppServiceAppInsightsHiddenLinkRule) Enabled() bool { | ||
| return true | ||
| } | ||
|
|
||
| // Severity returns the rule severity | ||
| func (r *AzurermAppServiceAppInsightsHiddenLinkRule) Severity() tflint.Severity { | ||
| return tflint.WARNING | ||
| } | ||
|
|
||
| // Link returns the rule reference link | ||
| func (r *AzurermAppServiceAppInsightsHiddenLinkRule) Link() string { | ||
| return project.ReferenceLink(r.Name()) | ||
| } | ||
|
|
||
| // checkResourceType checks a specific resource type for Application Insights hidden-link configuration | ||
| func (r *AzurermAppServiceAppInsightsHiddenLinkRule) checkResourceType(runner tflint.Runner, resourceType string) error { | ||
| resources, err := runner.GetResourceContent(resourceType, &hclext.BodySchema{ | ||
| Attributes: []hclext.AttributeSchema{ | ||
| {Name: appServiceAppSettingsAttrName}, | ||
| }, | ||
| Blocks: []hclext.BlockSchema{ | ||
| { | ||
| Type: "lifecycle", | ||
| Body: &hclext.BodySchema{ | ||
| Attributes: []hclext.AttributeSchema{ | ||
| {Name: appServiceIgnoreChangesAttrName}, | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| Type: "site_config", | ||
| Body: &hclext.BodySchema{ | ||
| Attributes: []hclext.AttributeSchema{ | ||
| {Name: appServiceSiteConfigAppInsightsConnectionKey}, | ||
| {Name: appServiceSiteConfigAppInsightsKey}, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, nil) | ||
|
|
||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| for _, resource := range resources.Blocks { | ||
| logger.Debug("checking", "resource type", resource.Labels[0], "resource name", resource.Labels[1]) | ||
|
|
||
| // Check if Application Insights is configured | ||
| hasAppInsights := false | ||
|
|
||
| // Check in app_settings | ||
| if appSettingsAttr, exists := resource.Body.Attributes[appServiceAppSettingsAttrName]; exists { | ||
| err := runner.EvaluateExpr(appSettingsAttr.Expr, func(val map[string]string) error { | ||
| for key := range val { | ||
| if strings.EqualFold(key, appServiceAppInsightsConnectionKey) || strings.EqualFold(key, appServiceAppInsightsInstrumentKey) { | ||
| hasAppInsights = true | ||
| break | ||
| } | ||
| } | ||
| return nil | ||
| }, nil) | ||
|
|
||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| // Check in site_config block | ||
| if !hasAppInsights { | ||
| for _, block := range resource.Body.Blocks { | ||
| if block.Type == "site_config" { | ||
| // Check for application_insights_connection_string | ||
| if attr, exists := block.Body.Attributes[appServiceSiteConfigAppInsightsConnectionKey]; exists { | ||
| err := runner.EvaluateExpr(attr.Expr, func(val string) error { | ||
| if val != "" { | ||
| hasAppInsights = true | ||
| } | ||
| return nil | ||
| }, nil) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| // Check for application_insights_key | ||
| if !hasAppInsights { | ||
| if attr, exists := block.Body.Attributes[appServiceSiteConfigAppInsightsKey]; exists { | ||
| err := runner.EvaluateExpr(attr.Expr, func(val string) error { | ||
| if val != "" { | ||
| hasAppInsights = true | ||
| } | ||
| return nil | ||
| }, nil) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| } | ||
| break | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // If Application Insights is not configured, skip this resource | ||
| if !hasAppInsights { | ||
| logger.Debug("no Application Insights configuration found", "resource type", resource.Labels[0], "resource name", resource.Labels[1]) | ||
| continue | ||
| } | ||
|
|
||
| // Check if lifecycle block exists and contains proper ignore_changes | ||
| hasProperIgnoreChanges := false | ||
|
|
||
| for _, block := range resource.Body.Blocks { | ||
| if block.Type == "lifecycle" { | ||
| if ignoreChangesAttr, ok := block.Body.Attributes[appServiceIgnoreChangesAttrName]; ok { | ||
| // Parse the ignore_changes expression to find ignored tag keys | ||
| foundTags := 0 | ||
| tagsIgnored := false | ||
| if exprs, diags := hcl.ExprList(ignoreChangesAttr.Expr); diags == nil { | ||
pregress marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| for _, expr := range exprs { | ||
| if traversal, diags := hcl.AbsTraversalForExpr(expr); diags == nil { | ||
| if len(traversal) == 1 { | ||
| if root, ok := traversal[0].(hcl.TraverseRoot); ok && root.Name == "tags" { | ||
| tagsIgnored = true | ||
| break | ||
| } | ||
| } else if len(traversal) == 2 { | ||
| if root, ok := traversal[0].(hcl.TraverseRoot); ok && root.Name == "tags" { | ||
| if index, ok := traversal[1].(hcl.TraverseIndex); ok && index.Key.Type() == cty.String { | ||
| tagKey := index.Key.AsString() | ||
| for _, requiredTag := range appServiceRequiredHiddenLinkTags { | ||
| if tagKey == requiredTag { | ||
| foundTags++ | ||
| break | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| if tagsIgnored || foundTags == len(appServiceRequiredHiddenLinkTags) { | ||
| hasProperIgnoreChanges = true | ||
| } | ||
| break | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Emit issue if Application Insights is configured but hidden-link tags are not properly ignored | ||
| if !hasProperIgnoreChanges { | ||
| issue := "When Application Insights is configured, lifecycle { ignore_changes } should include all hidden-link tags: tags[\"hidden-link: /app-insights-conn-string\"], tags[\"hidden-link: /app-insights-instrumentation-key\"], tags[\"hidden-link: /app-insights-resource-id\"]" | ||
pregress marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if err := runner.EmitIssue(r, issue, resource.DefRange); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // Check checks whether hidden-link tags are ignored when Application Insights is configured | ||
| func (r *AzurermAppServiceAppInsightsHiddenLinkRule) Check(runner tflint.Runner) error { | ||
| for _, resourceType := range appServiceResourceTypes { | ||
| if err := r.checkResourceType(runner, resourceType); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.