diff --git a/.build/BuildHelper/Start-PesterTest.ps1 b/.build/BuildHelper/Start-PesterTest.ps1 index 630af99c5..fce34a324 100644 --- a/.build/BuildHelper/Start-PesterTest.ps1 +++ b/.build/BuildHelper/Start-PesterTest.ps1 @@ -14,6 +14,7 @@ function Start-PesterTest $rootPath = ((Get-Item -Path $PSScriptRoot).Parent.Parent).FullName $pesterArgs = New-PesterConfiguration $pesterArgs.Output.Verbosity = 'Detailed' + $pesterArgs.Run.Exit = $true switch ($Type) { diff --git a/.editorconfig b/.editorconfig index 4f25f4e70..3f86673fd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,15 @@ trim_trailing_whitespace = true [*.ps1] indent_size = 4 +charset = utf-8-bom + +[*.psm1] +indent_size = 4 +charset = utf-8-bom + +[*.psd1] +indent_size = 4 +charset = utf-8-bom [*.md] max_line_length = off diff --git a/.vscode/settings.json b/.vscode/settings.json index 6b6db9a8d..ee15a3cf9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,7 +16,9 @@ }, "[powershell]": { "editor.formatOnSave": true, - "editor.defaultFormatter": "ms-vscode.powershell" + "editor.defaultFormatter": "ms-vscode.powershell", + "files.encoding": "utf8bom", + "files.trimTrailingWhitespace": true }, "cSpell.words": [ "ADLS", diff --git a/src/powershell/Private/Invoke-Rest.ps1 b/src/powershell/Private/Invoke-Rest.ps1 index f0ae99f94..f7d7449f7 100644 --- a/src/powershell/Private/Invoke-Rest.ps1 +++ b/src/powershell/Private/Invoke-Rest.ps1 @@ -62,9 +62,9 @@ function Invoke-Rest # TODO: Remove after Az PowerShell 13.0 # Temporarily suppress warnings for Get-AzAccessToken - $prevWarningPreference = $WarningPreference - $WarningPreference = "SilentlyContinue" - $token = (Get-AzAccessToken -AsSecureString).Token | ConvertFrom-SecureString -AsPlainText + $prevWarningPreference = $WarningPreference + $WarningPreference = "SilentlyContinue" + $token = (Get-AzAccessToken -AsSecureString).Token | ConvertFrom-SecureString -AsPlainText $WarningPreference = $prevWarningPreference $arm = (Get-AzContext).Environment.ResourceManagerUrl diff --git a/src/powershell/Private/Save-FinOpsHubTemplate.ps1 b/src/powershell/Private/Save-FinOpsHubTemplate.ps1 index 3d37e3487..cdc8f6ecb 100644 --- a/src/powershell/Private/Save-FinOpsHubTemplate.ps1 +++ b/src/powershell/Private/Save-FinOpsHubTemplate.ps1 @@ -70,7 +70,7 @@ function Save-FinOpsHubTemplate Write-Information $LocalizedData.Hub_Deploy_02to021 $Version = '0.3' } - + # Get the version if ($Version.ToLower() -eq 'latest') { diff --git a/src/powershell/Private/Split-AzureResourceId.ps1 b/src/powershell/Private/Split-AzureResourceId.ps1 index 85a49a564..e000efdd5 100644 --- a/src/powershell/Private/Split-AzureResourceId.ps1 +++ b/src/powershell/Private/Split-AzureResourceId.ps1 @@ -53,7 +53,7 @@ function Split-AzureResourceId if (-not $Id) { - return @{ ResourceId = $null } + return [AzureResourceIdInfo]@{ ResourceId = $null } } # Add leading slash diff --git a/src/powershell/Public/Get-FinOpsCostExport.ps1 b/src/powershell/Public/Get-FinOpsCostExport.ps1 index ca6b97590..190c92d44 100644 --- a/src/powershell/Public/Get-FinOpsCostExport.ps1 +++ b/src/powershell/Public/Get-FinOpsCostExport.ps1 @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. +# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. <# diff --git a/src/powershell/Public/Initialize-FinOpsHubDeployment.ps1 b/src/powershell/Public/Initialize-FinOpsHubDeployment.ps1 index 17c1bae7e..0eed7260f 100644 --- a/src/powershell/Public/Initialize-FinOpsHubDeployment.ps1 +++ b/src/powershell/Public/Initialize-FinOpsHubDeployment.ps1 @@ -21,5 +21,8 @@ function Initialize-FinOpsHubDeployment [CmdletBinding(SupportsShouldProcess)] param() - Register-FinOpsHubProviders -WhatIf:$WhatIfPreference | Out-Null + if ($PSCmdlet.ShouldProcess('FinOps hub resource providers', 'Register')) + { + Register-FinOpsHubProviders | Out-Null + } } diff --git a/src/powershell/Public/New-FinOpsCostExport.ps1 b/src/powershell/Public/New-FinOpsCostExport.ps1 index 527946264..5e8924700 100644 --- a/src/powershell/Public/New-FinOpsCostExport.ps1 +++ b/src/powershell/Public/New-FinOpsCostExport.ps1 @@ -136,7 +136,7 @@ function New-FinOpsCostExport { [Diagnostics.CodeAnalysis.SuppressMessage("PSReviewUnusedParameter", "", Justification = "False positive rule")] - [CmdletBinding(DefaultParameterSetName = "Scheduled")] + [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Scheduled")] param ( [Parameter(Mandatory = $true)] @@ -446,23 +446,26 @@ function New-FinOpsCostExport } # Create/update export - $createResponse = Invoke-Rest -Method PUT -Uri $uri -Body $properties @commandDetails - if ($createResponse.Failure) + if ($PSCmdlet.ShouldProcess($Name, 'Create cost export')) { - Write-Error "Unable to create export $Name in scope $Scope. Error: $($createResponse.Content.error.message) ($($createResponse.Content.error.code))" -ErrorAction Stop - return - } + $createResponse = Invoke-Rest -Method PUT -Uri $uri -Body $properties @commandDetails + if ($createResponse.Failure) + { + Write-Error "Unable to create export $Name in scope $Scope. Error: $($createResponse.Content.error.message) ($($createResponse.Content.error.code))" -ErrorAction Stop + return + } - # Run now if requested - if ($Backfill -gt 0 -and $OneTime -eq $false) - { - Start-FinOpsCostExport -Name $Name -Scope $Scope -Backfill $Backfill - } - elseif ($Execute -eq $true -or $OneTime -eq $true) - { - Start-FinOpsCostExport -Name $Name -Scope $Scope - } + # Run now if requested + if ($Backfill -gt 0 -and $OneTime -eq $false) + { + Start-FinOpsCostExport -Name $Name -Scope $Scope -Backfill $Backfill + } + elseif ($Execute -eq $true -or $OneTime -eq $true) + { + Start-FinOpsCostExport -Name $Name -Scope $Scope + } - return (Get-FinOpsCostExport -Name $Name -Scope $Scope -ApiVersion $ApiVersion) + return (Get-FinOpsCostExport -Name $Name -Scope $Scope -ApiVersion $ApiVersion) + } } } diff --git a/src/powershell/Public/Remove-FinOpsCostExport.ps1 b/src/powershell/Public/Remove-FinOpsCostExport.ps1 index 8b17ae56e..7cf84f00f 100644 --- a/src/powershell/Public/Remove-FinOpsCostExport.ps1 +++ b/src/powershell/Public/Remove-FinOpsCostExport.ps1 @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. +# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. <# diff --git a/src/powershell/Public/Remove-FinOpsHub.ps1 b/src/powershell/Public/Remove-FinOpsHub.ps1 index 7d5f04510..480fe172d 100644 --- a/src/powershell/Public/Remove-FinOpsHub.ps1 +++ b/src/powershell/Public/Remove-FinOpsHub.ps1 @@ -103,8 +103,8 @@ function Remove-FinOpsHub } # List all resources to be deleted - Write-Host "The following resources will be deleted:" - $resources | ForEach-Object { Write-Host "- $($_.ResourceId)" } + Write-Information "The following resources will be deleted:" + $resources | ForEach-Object { Write-Information "- $($_.ResourceId)" } # Prompt the user for confirmation if ($PSCmdlet.ShouldProcess($Name, 'DeleteFinOpsHub')) @@ -165,7 +165,7 @@ function Remove-FinOpsHub # Delete the resource Write-Verbose -Message "Deleting resource: $($resource.Name)" Remove-AzResource -ResourceId $resource.ResourceId -Force -ErrorAction Stop - Write-Host "Deleted resource: $($resource.Name)" + Write-Information "Deleted resource: $($resource.Name)" } catch { diff --git a/src/powershell/Public/Start-FinOpsCostExport.ps1 b/src/powershell/Public/Start-FinOpsCostExport.ps1 index d3164302f..a3b42998b 100644 --- a/src/powershell/Public/Start-FinOpsCostExport.ps1 +++ b/src/powershell/Public/Start-FinOpsCostExport.ps1 @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. +# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. <# @@ -53,7 +53,7 @@ function Start-FinOpsCostExport { [OutputType([bool])] - [cmdletBinding()] + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] @@ -146,6 +146,12 @@ function Start-FinOpsCostExport { Write-Verbose "Exporting $($StartDate) - $($EndDate)" } + + if (-not $PSCmdlet.ShouldProcess($Name, 'Run cost export')) + { + return $false + } + do { # Report progress @@ -173,7 +179,7 @@ function Start-FinOpsCostExport $firstDay = $StartDate $lastDay = $EndDate } - + # Ensure end date is not in the future $today = (Get-Date).ToUniversalTime().Date if ($lastDay -ge $today) @@ -181,7 +187,7 @@ function Start-FinOpsCostExport Write-Verbose "Adjusting end date to yesterday as it cannot be in the future." $lastDay = $today.AddDays(-1) } - + $body = @{ timePeriod = @{ from = $firstDay.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'"); to = $lastDay.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'") } } Write-Verbose "Executing $($firstDay.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'")) to $($lastDay.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'")) export $runpath" } @@ -224,7 +230,7 @@ function Start-FinOpsCostExport { # If not retrying, then track the success $success = $success -and $response.Success - + # Only increment month if not throttled $monthToExport += 1 } diff --git a/src/powershell/Tests/Initialize-Tests.ps1 b/src/powershell/Tests/Initialize-Tests.ps1 index 2803380fe..51c3f5864 100644 --- a/src/powershell/Tests/Initialize-Tests.ps1 +++ b/src/powershell/Tests/Initialize-Tests.ps1 @@ -5,11 +5,11 @@ Remove-Module FinOpsToolkit -ErrorAction SilentlyContinue Import-Module -FullyQualifiedName "$PSScriptRoot/../FinOpsToolkit.psm1" BeforeAll { - [Diagnostics.CodeAnalysis.SuppressMessage("PSAvoidGlobalVars", "", Justification = "Used for testing only")] - param() - # Bring the Monitor functions in to simplify debugging . "$PSScriptRoot/../../scripts/Monitor.ps1" +} - $global:ftk_InitializeTests_Hubs_RequiredRPs = @( 'Microsoft.CostManagementExports', 'Microsoft.EventGrid' ) +function Get-FinOpsHubRequiredResourceProvider +{ + return @( 'Microsoft.CostManagementExports', 'Microsoft.EventGrid' ) } diff --git a/src/powershell/Tests/Integration/Hubs.Tests.ps1 b/src/powershell/Tests/Integration/Hubs.Tests.ps1 index 370b03859..602f1d14c 100644 --- a/src/powershell/Tests/Integration/Hubs.Tests.ps1 +++ b/src/powershell/Tests/Integration/Hubs.Tests.ps1 @@ -6,7 +6,7 @@ Describe 'Hubs' { BeforeDiscovery { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] - $requiredRPs = $global:ftk_InitializeTests_Hubs_RequiredRPs + $requiredRPs = Get-FinOpsHubRequiredResourceProvider # TODO: Automatically validate the last 3 versions only # TODO: Automatically validate the last 3 versions only @@ -17,7 +17,7 @@ Describe 'Hubs' { BeforeAll { # Must be duplicated because pre-discovery vars aren't accessible [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] - $requiredRPs = $global:ftk_InitializeTests_Hubs_RequiredRPs + $requiredRPs = Get-FinOpsHubRequiredResourceProvider } Context 'Register-FinOpsHubProviders' { diff --git a/src/powershell/Tests/Unit/Deploy-FinOpsHub.Tests.ps1 b/src/powershell/Tests/Unit/Deploy-FinOpsHub.Tests.ps1 index dce1982bd..6632f982f 100644 --- a/src/powershell/Tests/Unit/Deploy-FinOpsHub.Tests.ps1 +++ b/src/powershell/Tests/Unit/Deploy-FinOpsHub.Tests.ps1 @@ -8,7 +8,9 @@ InModuleScope 'FinOpsToolkit' { BeforeAll { function Get-AzResourceGroup {} function New-AzResourceGroup {} - function New-AzResourceGroupDeployment {} + function New-AzResourceGroupDeployment { + param($TemplateFile, $TemplateParameterObject, $ResourceGroupName) + } [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")] $hubName = 'ftk-test-Deploy-FinOpsHub' @@ -114,6 +116,7 @@ InModuleScope 'FinOpsToolkit' { Mock -CommandName 'Get-AzResourceGroup' -MockWith { return @{ ResourceGroupName = $rgName } } Mock -CommandName 'New-AzResourceGroup' Mock -CommandName 'Save-FinOpsHubTemplate' + Mock -CommandName 'Initialize-FinOpsHubDeployment' } It 'Should throw if template file is not found' { diff --git a/src/powershell/Tests/Unit/New-FinOpsCostExport.Tests.ps1 b/src/powershell/Tests/Unit/New-FinOpsCostExport.Tests.ps1 index 776b337d6..5a3c27812 100644 --- a/src/powershell/Tests/Unit/New-FinOpsCostExport.Tests.ps1 +++ b/src/powershell/Tests/Unit/New-FinOpsCostExport.Tests.ps1 @@ -270,7 +270,7 @@ InModuleScope 'FinOpsToolkit' { # Assert Assert-MockCalled -ModuleName FinOpsToolkit -CommandName 'Invoke-Rest' -Times 1 -ParameterFilter { - $Body.properties.deliveryInfo.destination.rootFolderPath -eq ($scopeWithColons -replace ':','-') + $Body.properties.deliveryInfo.destination.rootFolderPath -eq (($scopeWithColons -replace ':','-').Trim('/')) } } @@ -308,7 +308,7 @@ InModuleScope 'FinOpsToolkit' { # Assert Assert-MockCalled -ModuleName FinOpsToolkit -CommandName 'Invoke-Rest' -Times 1 -ParameterFilter { - $Body.properties.deliveryInfo.destination.rootFolderPath -eq $scopeWithoutColons + $Body.properties.deliveryInfo.destination.rootFolderPath -eq $scopeWithoutColons.Trim('/') } } }