diff --git a/src/internal/classes/AzOpsScope.ps1 b/src/internal/classes/AzOpsScope.ps1 index 812dcb34..b11b1b8a 100644 --- a/src/internal/classes/AzOpsScope.ps1 +++ b/src/internal/classes/AzOpsScope.ps1 @@ -94,6 +94,8 @@ #> $this.StateRoot = $StateRoot $this.ChildResource = $ChildResource.resourceProvider + '-' + $ChildResource.resourceName + # Check and update generated name exceeding maximum length + $this.ChildResource = Set-AzOpsStringLength -String $this.ChildResource Write-PSFMessage -Level Verbose -String 'AzOpsScope.ChildResource.InitializeMemberVariables' -StringValues $ChildResource.ResourceProvider, $ChildResource.ResourceName, $scope -FunctionName AzOpsScope -ModuleName AzOps $this.InitializeMemberVariables($Scope) } diff --git a/src/internal/functions/ConvertTo-AzOpsState.ps1 b/src/internal/functions/ConvertTo-AzOpsState.ps1 index 95903833..5a9058e9 100644 --- a/src/internal/functions/ConvertTo-AzOpsState.ps1 +++ b/src/internal/functions/ConvertTo-AzOpsState.ps1 @@ -117,7 +117,7 @@ Write-PSFMessage -Level Verbose -String 'ConvertTo-AzOpsState.File.UseExisting' -StringValues $objectFilePath } - # if the export file path ends with parameter + # If export file path ends with parameter $generateTemplateParameter = $objectFilePath.EndsWith('.parameters.json') ? $true : $false Write-PSFMessage -Level Verbose -String 'ConvertTo-AzOpsState.GenerateTemplateParameter' -StringValues "$generateTemplateParameter" -FunctionName 'ConvertTo-AzOpsState' diff --git a/src/internal/functions/Set-AzOpsStringLength.ps1 b/src/internal/functions/Set-AzOpsStringLength.ps1 new file mode 100644 index 00000000..61c5f161 --- /dev/null +++ b/src/internal/functions/Set-AzOpsStringLength.ps1 @@ -0,0 +1,60 @@ +function Set-AzOpsStringLength { + + <# + .SYNOPSIS + Takes string input and shortens it according to maximum length. + .DESCRIPTION + Takes string input and shortens it according to maximum length. + .PARAMETER String + String to shorten. + .PARAMETER MaxStringLength + Set the maximum length for returned string, default is 255 characters. + Maximum filename length is based on underlying execution environments maximum allowed filename character limit of 255 and the additional characters added by AzOps for files measured by buffer <.parameters> <.json> or <.bicep>. + .EXAMPLE + > Set-AzOpsStringLength -String "microsoft.recoveryservices_vaults_replicationfabrics_replicationprotectioncontainers_replicationprotectioncontainermappings-test1-migratevault-1470815024_1541289ea1c5c535f89c0788063b3f5af00e91a2c63438851d90ef7143747149_cloud_308af796-701f-4d5d-ba68-a2434abb3c84_defaultrecplicationvm-containermapping" + Setting the string length for the above example with default character limit returns the following: + microsoft.recoveryservices_vaults_replicationfabrics_replicationprotectioncontainers_replicationprotectioncontainermappings-test1-migratevault-1470815024_1541289ea1c5c535f89c-BD89FDDC1A27FAD7C0E62CE2AA0A4513193D4F13907CDCF08E540BB5EFBBA9BA + #> + + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string] + $String, + [Parameter(Mandatory = $false)] + [ValidateRange(155,255)] + [int] + $MaxStringLength = "255" + ) + + process { + # Determine required buffer for ending, set buffer according to space required + $buffer = ".parameters".Length + $(Get-PSFConfigValue -FullName 'AzOps.Core.TemplateParameterFileSuffix').Length + # Check if string exceed maximum length + if (($String.Length + $buffer) -gt $MaxStringLength){ + $overSize = $String.Length + $buffer - $MaxStringLength + Write-PSFMessage -Level Verbose -String 'Set-AzOpsStringLength.ToLong' -StringValues $String,$MaxStringLength,$overSize -FunctionName 'Set-AzOpsStringLength' + # Generate 64-character hash based on input string + $stringStream = [IO.MemoryStream]::new([byte[]][char[]]$String) + $stringHash = Get-FileHash -InputStream $stringStream -Algorithm SHA256 + $startOfNameLength = $MaxStringLength - $stringHash.Hash.Length - $buffer - 1 + # Process new name + if ($startOfNameLength -gt 1) { + $startOfName = $String.Substring(0,($startOfNameLength)) + $newName = $startofName + '-' + $stringHash.Hash + } + else { + $newName = $stringHash.Hash + $endofName + } + # Construct new string with modified name + $String = $String.Replace($String,$newName) + Write-PSFMessage -Level Verbose -String 'Set-AzOpsStringLength.Shortened' -StringValues $String,$MaxStringLength -FunctionName 'Set-AzOpsStringLength' + return $String + } + else { + # Return original string, it is within limit + Write-PSFMessage -Level Verbose -String 'Set-AzOpsStringLength.WithInLimit' -StringValues $String,$MaxStringLength -FunctionName 'Set-AzOpsStringLength' + return $String + } + } +} \ No newline at end of file diff --git a/src/localized/en-us/Strings.psd1 b/src/localized/en-us/Strings.psd1 index 9efea588..f2d735a6 100644 --- a/src/localized/en-us/Strings.psd1 +++ b/src/localized/en-us/Strings.psd1 @@ -58,6 +58,7 @@ 'ConvertTo-AzOpsState.Exporting.Default' = 'Exporting input resource to AzOpsState to {0}' # $resourceData.ObjectFilePath 'ConvertTo-AzOpsState.File.Create' = 'AzOpsState file not found. Creating new: {0}' # $ObjectFilePath 'ConvertTo-AzOpsState.File.InvalidCharacter' = 'The specified AzOpsState file contains invalid characters (remove any "[" or "]" characters)! Skipping {0}' # $ObjectFilePath + 'ConvertTo-AzOpsState.File.JQError' = 'Jq filter error {0}' # $Resource.ObjectFilePath 'ConvertTo-AzOpsState.File.UseExisting' = 'AzOpsState file is found. Using existing file: {0}' # $ObjectFilePath 'ConvertTo-AzOpsState.NoExportPath' = 'No export path found for {0}. Ensure the original data type remains intact or specify an -ExportPath' # $Resource 'ConvertTo-AzOpsState.Processing' = 'Processing input: {0}' # $Resource @@ -65,7 +66,6 @@ 'ConvertTo-AzOpsState.Starting' = 'Starting conversion to AzOps State object' # 'ConvertTo-AzOpsState.StateConfig.Error' = 'Cannot load {0}, is the json schema valid and does the file exist?' # (Get-PSFConfigValue -FullName 'AzOps.General.StateConfig') 'ConvertTo-AzOpsState.StatePath' = 'Resolve path to resource state {0}' # $resourceData.ObjectFilePath - 'ConvertTo-AzOpsState.File.JQError' = 'Jq filter error {0}' # $Resource.ObjectFilePath 'ConvertTo-AzOpsState.GenerateTemplateParameter' = 'Generating template parameter: {0}' # $generateTemplateParameter 'ConvertTo-AzOpsState.GenerateTemplate' = 'Generating template: {0}' # $generateTemplateParameter 'ConvertTo-AzOpsState.GenerateTemplate.ProviderNamespace' = 'Provider namespace: {0}' # $providerNamespace @@ -258,6 +258,13 @@ 'Save-AzOpsManagementGroupChildren.Subscription.NotFound' = 'Unable to locate subscription: {0} within AzOpsSubscriptions object' #child.Name 'Set-AzOpsContext.Change' = 'Changing active subscription from {0} to {1} ({2})' # $context.Subscription.Name, $ScopeObject.SubscriptionDisplayName, $ScopeObject.Subscription + + 'Set-AzOpsStringLength.IsString' = 'Is string {0}' # $String + 'Set-AzOpsStringLength.Shortened' = 'New shortened string {0} in-line with limit of {1}' # $String, $MaxStringLength + 'Set-AzOpsStringLength.StringIsPath' = 'String contains state path {0}' # $String + 'Set-AzOpsStringLength.ToLong' = 'String {0} exceeding limit of {1} by {2} characters' # $String, $MaxStringLength, $overSize + 'Set-AzOpsStringLength.WithInLimit' = 'String {0} within limit of {1}' # $String + 'Set-AzOpsWhatIfOutput.WhatIfFile' = 'Creating WhatIf markdown and json files' # 'Set-AzOpsWhatIfOutput.WhatIfResults' = 'WhatIf Output {0}' } diff --git a/src/tests/integration/Repository.Tests.ps1 b/src/tests/integration/Repository.Tests.ps1 index db6afcfb..0fdb8a21 100644 --- a/src/tests/integration/Repository.Tests.ps1 +++ b/src/tests/integration/Repository.Tests.ps1 @@ -157,6 +157,7 @@ Describe "Repository" { $script:policyExemptions = Get-AzPolicyExemption -Name "PolicyExemptionTest" -Scope "/subscriptions/$script:subscriptionId" $script:routeTable = (Get-AzResource -Name "RouteTable" -ResourceGroupName $($script:resourceGroup).ResourceGroupName) $script:ruleCollectionGroups = (Get-AzResource -ExpandProperties -Name "TestPolicy" -ResourceGroupName $($script:resourceGroup).ResourceGroupName).Properties.ruleCollectionGroups.id.split("/")[-1] + $script:logAnalyticsWorkspace = (Get-AzResource -Name "thisisalongloganalyticsworkspacename123456789011121314151617181" -ResourceGroupName $($script:resourceGroup).ResourceGroupName) } catch { @@ -259,6 +260,11 @@ Describe "Repository" { $script:ruleCollectionGroupsFile = ($script:ruleCollectionGroupsPath).FullName $script:ruleCollectionDeploymentName = "AzOps-{0}-{1}" -f $($script:ruleCollectionGroupsPath.Name.Replace(".json", '')).Substring(0, 53), $deploymentLocationId Write-PSFMessage -Level Debug -Message "RuleCollectionGroupsFile: $($script:ruleCollectionGroupsFile)" -FunctionName "BeforeAll" + + $script:logAnalyticsWorkspaceSavedSearchesPath = ($filePaths | Where-Object Name -eq "microsoft.operationalinsights_workspaces_savedsearches-$(($script:logAnalyticsWorkspace.Name).toLower())_logmanagement(thisisalongloganalyticsworkspacename12345-ee7c6e90e26a87cec75b83b2ed548077fed5750162e50d80fdda5d8ce27f3478.json") + $script:logAnalyticsWorkspaceSavedSearchesDirectory = ($script:logAnalyticsWorkspaceSavedSearchesPath).Directory + $script:logAnalyticsWorkspaceSavedSearchesFile = ($script:logAnalyticsWorkspaceSavedSearchesPath).FullName + Write-PSFMessage -Level Debug -Message "logAnalyticsWorkspaceSavedSearchesFile: $($script:logAnalyticsWorkspaceSavedSearchesFile)" -FunctionName "BeforeAll" #endregion Paths #Test push based on pulled resources @@ -679,6 +685,35 @@ Describe "Repository" { $ruleCollectionDeployment.ProvisioningState | Should -Be "Succeeded" } #endregion + + #region Scope - logAnalyticsWorkspaceSavedSearchesPath (./root/tenant root group/test/platform/management/subscription-0/application/thisisalongloganalyticsworkspacename123456789011121314151617181) + It "LogAnalyticsWorkspaceSavedSearches directory should exist" { + Test-Path -Path $script:logAnalyticsWorkspaceSavedSearchesDirectory | Should -BeTrue + } + It "LogAnalyticsWorkspaceSavedSearches file should exist" { + Test-Path -Path $script:logAnalyticsWorkspaceSavedSearchesFile | Should -BeTrue + } + It "LogAnalyticsWorkspaceSavedSearches resource type should exist" { + $fileContents = Get-Content -Path $script:logAnalyticsWorkspaceSavedSearchesFile -Raw | ConvertFrom-Json -Depth 25 + $fileContents.resources[0].type | Should -BeTrue + } + It "LogAnalyticsWorkspaceSavedSearches resource name should exist" { + $fileContents = Get-Content -Path $script:logAnalyticsWorkspaceSavedSearchesFile -Raw | ConvertFrom-Json -Depth 25 + $fileContents.resources[0].name | Should -BeTrue + } + It "LogAnalyticsWorkspaceSavedSearches resource apiVersion should exist" { + $fileContents = Get-Content -Path $script:logAnalyticsWorkspaceSavedSearchesFile -Raw | ConvertFrom-Json -Depth 25 + $fileContents.resources[0].apiVersion | Should -BeTrue + } + It "LogAnalyticsWorkspaceSavedSearches resource properties should exist" { + $fileContents = Get-Content -Path $script:logAnalyticsWorkspaceSavedSearchesFile -Raw | ConvertFrom-Json -Depth 25 + $fileContents.resources[0].properties | Should -BeTrue + } + It "LogAnalyticsWorkspaceSavedSearches resource type should match" { + $fileContents = Get-Content -Path $script:logAnalyticsWorkspaceSavedSearchesFile -Raw | ConvertFrom-Json -Depth 25 + $fileContents.resources[0].type | Should -Be "Microsoft.OperationalInsights/workspaces/savedSearches" + } + #endregion } AfterAll { @@ -781,6 +816,7 @@ Describe "Repository" { Write-PSFMessage -Level Verbose -Message "Removing Resource Group deployments" -FunctionName "AfterAll" $script:ruleCollectionDeployment | Remove-AzResourceGroupDeployment -Confirm:$false $script:routeTableDeployment | Remove-AzResourceGroupDeployment -Confirm:$false + $script:logAnalyticsWorkspaceSavedSearchesDeployment | Remove-AzResourceGroupDeployment -Confirm:$false Write-PSFMessage -Level Verbose -Message "Removing Subscription deployments" -FunctionName "AfterAll" $script:resourceGroupDeployment | Remove-AzSubscriptionDeployment -Confirm:$false $script:roleAssignmentDeployment | Remove-AzSubscriptionDeployment -Confirm:$false diff --git a/src/tests/templates/azuredeploy.jsonc b/src/tests/templates/azuredeploy.jsonc index cf0e8abd..7a20a946 100644 --- a/src/tests/templates/azuredeploy.jsonc +++ b/src/tests/templates/azuredeploy.jsonc @@ -258,8 +258,19 @@ ], "firewalls": [] } + }, + { + "type": "Microsoft.OperationalInsights/workspaces", + "name": "thisisalongloganalyticsworkspacename123456789011121314151617181", + "apiVersion": "2017-03-15-preview", + "location": "eastus", + "tags": {}, + "properties": { + "sku": { + "name": "pergb2018" + } + } } - ], "outputs": { }