diff --git a/src/ALZ/Private/Config-Helpers/Edit-ALZConfigurationFilesInPlace.ps1 b/src/ALZ/Private/Config-Helpers/Edit-ALZConfigurationFilesInPlace.ps1 index 45c9fecb..3111bcbd 100644 --- a/src/ALZ/Private/Config-Helpers/Edit-ALZConfigurationFilesInPlace.ps1 +++ b/src/ALZ/Private/Config-Helpers/Edit-ALZConfigurationFilesInPlace.ps1 @@ -45,6 +45,7 @@ function Edit-ALZConfigurationFilesInPlace { if ($bicepConfigNode -is [array]) { # If this is an array - use the property as an array index... if ($propertyNames[$index] -match "[0-9]+" -eq $false) { + Write-ToConsoleLog "Configuration specifies an array, but the index value '${$propertyNames[$index]}' is not a number. Property path: $($propertyNames -join '.')" -IsError throw "Configuration specifies an array, but the index value '${$propertyNames[$index]}' is not a number" } diff --git a/src/ALZ/Private/Config-Helpers/Get-ALZConfig.ps1 b/src/ALZ/Private/Config-Helpers/Get-ALZConfig.ps1 index 3f974cbf..4e23bc84 100644 --- a/src/ALZ/Private/Config-Helpers/Get-ALZConfig.ps1 +++ b/src/ALZ/Private/Config-Helpers/Get-ALZConfig.ps1 @@ -9,7 +9,7 @@ function Get-ALZConfig { ) if (!(Test-Path $configFilePath)) { - Write-Error "The config file does not exist at $configFilePath" + Write-ToConsoleLog "The config file does not exist at $configFilePath" -IsError throw "The config file does not exist at $configFilePath" } @@ -25,7 +25,7 @@ function Get-ALZConfig { $config = [PSCustomObject](Get-Content -Path $configFilePath | ConvertFrom-Yaml -Ordered) } catch { $errorMessage = "Failed to parse YAML inputs. Please check the YAML file for errors and try again. $_" - Write-Error $errorMessage + Write-ToConsoleLog $errorMessage -IsError throw $errorMessage } @@ -34,7 +34,7 @@ function Get-ALZConfig { $config = [PSCustomObject](Get-Content -Path $configFilePath | ConvertFrom-Json) } catch { $errorMessage = "Failed to parse JSON inputs. Please check the JSON file for errors and try again. $_" - Write-Error $errorMessage + Write-ToConsoleLog $errorMessage -IsError throw $errorMessage } } elseif ($extension -eq ".tfvars") { @@ -42,10 +42,11 @@ function Get-ALZConfig { $config = [PSCustomObject](& $hclParserToolPath $configFilePath | ConvertFrom-Json) } catch { $errorMessage = "Failed to parse HCL inputs. Please check the HCL file for errors and try again. $_" - Write-Error $errorMessage + Write-ToConsoleLog $errorMessage -IsError throw $errorMessage } } else { + Write-ToConsoleLog "Unsupported config file extension '$extension' for file '$configFilePath'. Supported extensions: .json, .yml, .yaml, .tfvars" -IsError throw "The config file must be a json, yaml/yml or tfvars file" } diff --git a/src/ALZ/Private/Config-Helpers/Set-Config.ps1 b/src/ALZ/Private/Config-Helpers/Set-Config.ps1 index 7e437cfa..1ba232d7 100644 --- a/src/ALZ/Private/Config-Helpers/Set-Config.ps1 +++ b/src/ALZ/Private/Config-Helpers/Set-Config.ps1 @@ -78,7 +78,7 @@ function Set-Config { if($inputConfigItemValue.Length -le $index) { Write-Verbose "Input config item $($inputConfigName) does not have an index of $index." if($index -eq 0) { - Write-Error "At least one value is required for input config item $($inputConfigName)." + Write-ToConsoleLog "At least one value is required for input config item $($inputConfigName)." -IsError throw "At least one value is required for input config item $($inputConfigName)." } } else { @@ -87,7 +87,7 @@ function Set-Config { } catch { Write-Verbose "Error accessing index $index for input config item $($inputConfigName): $_" if($index -eq 0) { - Write-Error "At least one value is required for input config item $($inputConfigName)." + Write-ToConsoleLog "At least one value is required for input config item $($inputConfigName)." -IsError throw "At least one value is required for input config item $($inputConfigName)." } } @@ -98,7 +98,7 @@ function Set-Config { } else { Write-Verbose "Input config item $($inputConfigName) with index $index is null." if($index -eq 0) { - Write-Error "At least one value is required for input config item $($inputConfigName)." + Write-ToConsoleLog "At least one value is required for input config item $($inputConfigName)." -IsError throw "At least one value is required for input config item $($inputConfigName)." } } @@ -111,7 +111,7 @@ function Set-Config { $mapItem = $inputConfigItemValue.PsObject.Properties | Where-Object { $_.Name -eq $indexString } } catch { Write-Verbose "Error accessing map item $indexString for input config item $($inputConfigName): $_" - Write-Error "At least one value is required for input config item $($inputConfigName)." + Write-ToConsoleLog "At least one value is required for input config item $($inputConfigName)." -IsError throw "At least one value is required for input config item $($inputConfigName)." } if($null -ne $mapItem) { @@ -121,17 +121,17 @@ function Set-Config { continue } else { Write-Verbose "Input config item $($inputConfigName) with index $indexString is null." - Write-Error "At least one value is required for input config item $($inputConfigName)." + Write-ToConsoleLog "At least one value is required for input config item $($inputConfigName)." -IsError throw "At least one value is required for input config item $($inputConfigName)." } } else { Write-Verbose "Input config item $($inputConfigName) does not have an index of $indexString." - Write-Error "At least one value is required for input config item $($inputConfigName)." + Write-ToConsoleLog "At least one value is required for input config item $($inputConfigName)." -IsError throw "At least one value is required for input config item $($inputConfigName)." } } } else { - Write-Error "Input config item $($inputConfigName) not found." + Write-ToConsoleLog "Input config item $($inputConfigName) not found." -IsError throw "Input config item $($inputConfigName) not found." } } diff --git a/src/ALZ/Private/Deploy-Accelerator-Helpers/Copy-ParameterFileCollection.ps1 b/src/ALZ/Private/Deploy-Accelerator-Helpers/Copy-ParameterFileCollection.ps1 index a5f189ac..652fbe3a 100644 --- a/src/ALZ/Private/Deploy-Accelerator-Helpers/Copy-ParameterFileCollection.ps1 +++ b/src/ALZ/Private/Deploy-Accelerator-Helpers/Copy-ParameterFileCollection.ps1 @@ -21,7 +21,7 @@ function Copy-ParametersFileCollection { Copy-Item -Path $sourcePath -Destination $destinationPath -Recurse -Force | Out-String | Write-Verbose } } else { - Write-Warning "The file $sourcePath does not exist." + Write-ToConsoleLog "The file $sourcePath does not exist." -IsWarning } } } diff --git a/src/ALZ/Private/Deploy-Accelerator-Helpers/Invoke-Terraform.ps1 b/src/ALZ/Private/Deploy-Accelerator-Helpers/Invoke-Terraform.ps1 index c7296f95..5e52017a 100644 --- a/src/ALZ/Private/Deploy-Accelerator-Helpers/Invoke-Terraform.ps1 +++ b/src/ALZ/Private/Deploy-Accelerator-Helpers/Invoke-Terraform.ps1 @@ -46,7 +46,7 @@ function Invoke-Terraform { } if ($null -eq $subscriptionId -or $subscriptionId -eq "") { - Write-Error "Subscription ID not found. Please ensure you are logged in to Azure and have selected a subscription, or provide bootstrap_subscription_id. Use 'az account show' to check." + Write-ToConsoleLog "Subscription ID not found. Please ensure you are logged in to Azure and have selected a subscription, or provide bootstrap_subscription_id. Use 'az account show' to check." -IsError return } $env:ARM_SUBSCRIPTION_ID = $subscriptionId diff --git a/src/ALZ/Private/Deploy-Accelerator-Helpers/New-ModuleSetup.ps1 b/src/ALZ/Private/Deploy-Accelerator-Helpers/New-ModuleSetup.ps1 index b697f010..fad1292c 100644 --- a/src/ALZ/Private/Deploy-Accelerator-Helpers/New-ModuleSetup.ps1 +++ b/src/ALZ/Private/Deploy-Accelerator-Helpers/New-ModuleSetup.ps1 @@ -89,6 +89,7 @@ function New-ModuleSetup { $shouldDownload = $false if($isAutoVersion -and $upgrade.IsPresent -and $null -eq $latestReleaseTag) { + Write-ToConsoleLog "Cannot perform upgrade to latest version of '$targetFolder' as unable to determine latest release from GitHub. Current version: $currentVersion" -IsError throw "Cannot perform upgrade to latest version as unable to determine latest release from GitHub." } diff --git a/src/ALZ/Private/Deploy-Accelerator-Helpers/Request-ALZConfigurationValue.ps1 b/src/ALZ/Private/Deploy-Accelerator-Helpers/Request-ALZConfigurationValue.ps1 index 99687f33..7b4d619c 100644 --- a/src/ALZ/Private/Deploy-Accelerator-Helpers/Request-ALZConfigurationValue.ps1 +++ b/src/ALZ/Private/Deploy-Accelerator-Helpers/Request-ALZConfigurationValue.ps1 @@ -170,7 +170,7 @@ function Request-ALZConfigurationValue { # Load the schema file $schemaPath = Join-Path $PSScriptRoot "AcceleratorInputSchema.json" if (-not (Test-Path $schemaPath)) { - Write-Warning "Schema file not found at $schemaPath. Proceeding without descriptions." + Write-ToConsoleLog "Schema file not found at $schemaPath. Proceeding without descriptions." -IsWarning $schema = $null } else { $schema = Get-Content -Path $schemaPath -Raw | ConvertFrom-Json diff --git a/src/ALZ/Private/Shared/Get-GithubReleaseTag.ps1 b/src/ALZ/Private/Shared/Get-GithubReleaseTag.ps1 index 5abd6310..00e2123a 100644 --- a/src/ALZ/Private/Shared/Get-GithubReleaseTag.ps1 +++ b/src/ALZ/Private/Shared/Get-GithubReleaseTag.ps1 @@ -72,11 +72,12 @@ function Get-GithubReleaseTag { Write-Verbose "Status code: $statusCode" if ($statusCode -eq 404) { - Write-Error "The release $release does not exist in the GitHub repository $githubRepoUrl - $repoReleaseUrl" + Write-ToConsoleLog "The release $release does not exist in the GitHub repository $githubRepoUrl - $repoReleaseUrl. HTTP status code: $statusCode" -IsError throw "The release $release does not exist in the GitHub repository $githubRepoUrl - $repoReleaseUrl" } if ($statusCode -ne 200) { + Write-ToConsoleLog "Unable to query repository version from $repoReleaseUrl. HTTP status code: $statusCode" -IsError throw "Unable to query repository version, please check your internet connection and try again..." } diff --git a/src/ALZ/Private/Shared/Get-OsArchitecture.ps1 b/src/ALZ/Private/Shared/Get-OsArchitecture.ps1 index 5f3e3115..b7db0cef 100644 --- a/src/ALZ/Private/Shared/Get-OsArchitecture.ps1 +++ b/src/ALZ/Private/Shared/Get-OsArchitecture.ps1 @@ -34,7 +34,7 @@ function Get-OSArchitecture { ) if($supportedOsAndArchitectures -notcontains $osAndArchitecture) { - Write-Error "Unsupported OS and architecture combination: $osAndArchitecture" + Write-ToConsoleLog "Unsupported OS and architecture combination: $osAndArchitecture" -IsError return } diff --git a/src/ALZ/Private/Shared/Invoke-HttpRequestWithRetry.ps1 b/src/ALZ/Private/Shared/Invoke-HttpRequestWithRetry.ps1 index b17c98ce..52a3abef 100644 --- a/src/ALZ/Private/Shared/Invoke-HttpRequestWithRetry.ps1 +++ b/src/ALZ/Private/Shared/Invoke-HttpRequestWithRetry.ps1 @@ -124,10 +124,14 @@ function Invoke-HttpRequestWithRetry { $commonParams["Headers"] = $Headers } + Write-Verbose "HTTP $Method $Uri (MaxRetries=$MaxRetryCount, RetryInterval=${RetryIntervalSeconds}s$(if ($PSBoundParameters.ContainsKey('TimeoutSec')) { ", Timeout=${TimeoutSec}s" })$(if ($isDownload) { ", OutFile=$OutFile" }))" + for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { + Write-Verbose "HTTP $Method $Uri - Attempt $attempt of $maxAttempts" try { if ($isDownload) { Invoke-WebRequest @commonParams -OutFile $OutFile + Write-Verbose "HTTP $Method $Uri - Download complete (saved to $OutFile)" return } @@ -137,11 +141,14 @@ function Invoke-HttpRequestWithRetry { $code = [int]$response.StatusCode if ($code -in $transientStatusCodes -and $attempt -lt $maxAttempts) { - Write-Warning "Request to $Uri returned status $code (attempt $attempt of $maxAttempts). Retrying in $RetryIntervalSeconds seconds..." + Write-Verbose "HTTP $Method $Uri - Transient status $code on attempt $attempt" + Write-ToConsoleLog "Request to $Uri returned status $code (attempt $attempt of $maxAttempts). Retrying in $RetryIntervalSeconds seconds..." -IsWarning Start-Sleep -Seconds $RetryIntervalSeconds continue } + Write-Verbose "HTTP $Method $Uri - Completed with status $code" + if ($ReturnStatusCode) { return @{ Result = $response @@ -161,10 +168,33 @@ function Invoke-HttpRequestWithRetry { $isTransient = $responseCode -in $transientStatusCodes + Write-Verbose "HTTP $Method $Uri - Error on attempt ${attempt}: Status=$responseCode, Message=$($_.Exception.Message)" + if ($isTransient -and $attempt -lt $maxAttempts) { - Write-Warning "Request to $Uri failed with status $responseCode (attempt $attempt of $maxAttempts). Retrying in $RetryIntervalSeconds seconds..." + Write-ToConsoleLog "Request to $Uri failed with status $responseCode (attempt $attempt of $maxAttempts). Retrying in $RetryIntervalSeconds seconds..." -IsWarning Start-Sleep -Seconds $RetryIntervalSeconds } else { + $errorDetails = "HTTP $Method $Uri failed after $attempt attempt(s)." + if ($null -ne $responseCode) { + $errorDetails += " Status code: $responseCode." + } + $errorDetails += " Error: $($_.Exception.Message)" + if ($_.Exception.Response) { + try { + $stream = $_.Exception.Response.GetResponseStream() + if ($null -ne $stream) { + $reader = [System.IO.StreamReader]::new($stream) + $responseBody = $reader.ReadToEnd() + $reader.Dispose() + if (-not [string]::IsNullOrWhiteSpace($responseBody)) { + $errorDetails += " Response body: $responseBody" + } + } + } catch { + Write-Verbose "Failed to read response body: $($_.Exception.Message)" + } + } + Write-ToConsoleLog $errorDetails -IsError throw } } diff --git a/src/ALZ/Private/Tools/Get-TerraformTool.ps1 b/src/ALZ/Private/Tools/Get-TerraformTool.ps1 index 57c7515e..df549200 100644 --- a/src/ALZ/Private/Tools/Get-TerraformTool.ps1 +++ b/src/ALZ/Private/Tools/Get-TerraformTool.ps1 @@ -26,6 +26,7 @@ function Get-TerraformTool { } $versionResponse = Invoke-HttpRequestWithRetry @httpParams if($versionResponse.StatusCode -ne "200") { + Write-ToConsoleLog "Unable to query latest Terraform version from HashiCorp API. HTTP status code: $($versionResponse.StatusCode)" -IsError throw "Unable to query Terraform version, please check your internet connection and try again..." } $releases = ($versionResponse).Content | ConvertFrom-Json | Where-Object -Property is_prerelease -EQ $false @@ -43,6 +44,7 @@ function Get-TerraformTool { } $versionResponse = Invoke-HttpRequestWithRetry @httpParams if($versionResponse.StatusCode -ne "200") { + Write-ToConsoleLog "Unable to query Terraform version '$version' from HashiCorp API. HTTP status code: $($versionResponse.StatusCode)" -IsError throw "Unable to query Terraform version, please check the supplied version and try again..." } $release = ($versionResponse).Content diff --git a/src/ALZ/Public/Grant-SubscriptionCreatorRole.ps1 b/src/ALZ/Public/Grant-SubscriptionCreatorRole.ps1 index 8dfb7adb..052e7f44 100644 --- a/src/ALZ/Public/Grant-SubscriptionCreatorRole.ps1 +++ b/src/ALZ/Public/Grant-SubscriptionCreatorRole.ps1 @@ -58,7 +58,7 @@ Grant-SubscriptionCreatorRole -servicePrincipalObjectId "bd42568a-7dd8-489b-bbbb if($null -eq $servicePrincipalObjectId -or $servicePrincipalObjectId -eq "") { $errorMessage = "The 'Service Principal Object ID' parameter is required. Please provide a valid value and try again." - Write-Error $errorMessage + Write-ToConsoleLog $errorMessage -IsError throw $errorMessage } @@ -79,7 +79,7 @@ Grant-SubscriptionCreatorRole -servicePrincipalObjectId "bd42568a-7dd8-489b-bbbb Write-ToConsoleLog "Billing Resource ID or required parameters provided..." -IsSuccess } else { $errorMessage = "No Billing Resource ID or required parameters provided." - Write-Error $errorMessage + Write-ToConsoleLog $errorMessage -IsError throw $errorMessage } @@ -90,7 +90,7 @@ Grant-SubscriptionCreatorRole -servicePrincipalObjectId "bd42568a-7dd8-489b-bbbb if ($null -eq $getbillingResourceID) { $errorMessage = "The specified billing account resource ID '$($billingResourceID)' does not exist or you do not have access to it. Please check the value and try again. Also ensure you are logged in as the Account Owner for the specified billing account." - Write-Error $errorMessage + Write-ToConsoleLog $errorMessage -IsError throw $errorMessage } else { Write-ToConsoleLog "The specified billing account ID '$($billingResourceID)' exists. Continuing..." -IsSuccess @@ -103,7 +103,7 @@ Grant-SubscriptionCreatorRole -servicePrincipalObjectId "bd42568a-7dd8-489b-bbbb if ($null -eq $getexistingSpnMiObjectId) { $errorMessage = "The specified service principal 'Object ID' '$($existingSpnMiObjectId)' does not exist. Please check the value and try again." - Write-Error $errorMessage + Write-ToConsoleLog $errorMessage -IsError throw $errorMessage } else { $finalSpnMiObjectId = $getexistingSpnMiObjectId.id @@ -138,7 +138,7 @@ Grant-SubscriptionCreatorRole -servicePrincipalObjectId "bd42568a-7dd8-489b-bbbb if ($null -eq $grantRbac) { $errorMessage = "The 'SubscriptionCreator' role could not be granted to the service principal. Please check the error message above and try again." - Write-Error $errorMessage + Write-ToConsoleLog $errorMessage -IsError throw $errorMessage } else { Write-ToConsoleLog "The 'SubscriptionCreator' role has been granted to the service principal." -IsSuccess diff --git a/src/ALZ/Public/New-AcceleratorFolderStructure.ps1 b/src/ALZ/Public/New-AcceleratorFolderStructure.ps1 index ef133657..fa414725 100644 --- a/src/ALZ/Public/New-AcceleratorFolderStructure.ps1 +++ b/src/ALZ/Public/New-AcceleratorFolderStructure.ps1 @@ -47,9 +47,11 @@ function New-AcceleratorFolderStructure { try { Remove-Item -Recurse -Force -Path $targetFolderPath -ErrorAction Stop | Write-Verbose | Out-Null } catch { + Write-ToConsoleLog "Failed to remove existing folder at '$targetFolderPath'. Error: $($_.Exception.Message)" -IsError throw "Failed to remove existing folder at '$targetFolderPath'. The folder may be locked by another process or you may not have permission to remove it. Please close any applications that may be using files in this folder and try again. Error: $($_.Exception.Message)" } } else { + Write-ToConsoleLog "Target folder '$targetFolderPath' already exists and the -Force flag was not set. Please specify a different folder path or remove the existing folder." -IsError throw "Target folder $targetFolderPath already exists. Please specify a different folder path or remove the existing folder." } }