From 0bc503e0d133d96658835dd78adb5020dd3def98 Mon Sep 17 00:00:00 2001 From: bganapa Date: Mon, 30 Nov 2020 11:47:03 -0800 Subject: [PATCH 1/3] Porting AzureRM.BootStrapper module version 0.5.0 to AzureStack repo The module code is same as that of in azure powershell AzureRM repo Build scripts added --- .../AzureRM.BootStrapper.csproj | 14 + .../AzureRM.BootStrapper.nuspec | 19 + .../Module/AzureRM.BootStrapper.Tests.ps1 | 1462 +++++++++++++++++ .../Module/AzureRM.BootStrapper.psd1 | 134 ++ .../Module/AzureRM.Bootstrapper.Format.ps1xml | 32 + .../AzureRM.Bootstrapper.ScenarioTests.ps1 | 828 ++++++++++ .../Module/AzureRM.Bootstrapper.psm1 | 1312 +++++++++++++++ .../Module/ProfileMap.json | 286 ++++ .../Module/Run-ScenarioTests.ps1 | 6 + .../Module/Run-UnitTests.ps1 | 6 + .../Module/about_version_profiles.help.txt | 122 ++ .../Module/help/AzureRM.BootStrapper.md | 43 + .../Module/help/AzureRM.Bootstrapper-help.xml | 996 +++++++++++ .../Module/help/Get-AzureRmModule.md | 79 + .../Module/help/Get-AzureRmProfile.md | 79 + .../Module/help/Install-AzureRmProfile.md | 125 ++ .../help/Remove-AzureRmDefaultProfile.md | 89 + .../Module/help/Set-AzureRmDefaultProfile.md | 138 ++ .../Module/help/Uninstall-AzureRmProfile.md | 107 ++ .../Module/help/Update-AzureRmProfile.md | 170 ++ .../Module/help/Use-AzureRmProfile.md | 148 ++ .../help/about_version_profiles.help.txt | 122 ++ .../Module/help/about_version_profiles.md | 153 ++ src/AzureRM.BootStrapper/build-module.ps1 | 91 + src/AzureRM.BootStrapper/dummy.json | 32 + src/AzureRM.BootStrapper/pack-module.ps1 | 16 + src/AzureRM.BootStrapper/readme.md | 17 + src/AzureRM.BootStrapper/test-module.ps1 | 37 + 28 files changed, 6663 insertions(+) create mode 100644 src/AzureRM.BootStrapper/AzureRM.BootStrapper.csproj create mode 100644 src/AzureRM.BootStrapper/AzureRM.BootStrapper.nuspec create mode 100644 src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.Tests.ps1 create mode 100644 src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.psd1 create mode 100644 src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.Format.ps1xml create mode 100644 src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.ScenarioTests.ps1 create mode 100644 src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.psm1 create mode 100644 src/AzureRM.BootStrapper/Module/ProfileMap.json create mode 100644 src/AzureRM.BootStrapper/Module/Run-ScenarioTests.ps1 create mode 100644 src/AzureRM.BootStrapper/Module/Run-UnitTests.ps1 create mode 100644 src/AzureRM.BootStrapper/Module/about_version_profiles.help.txt create mode 100644 src/AzureRM.BootStrapper/Module/help/AzureRM.BootStrapper.md create mode 100644 src/AzureRM.BootStrapper/Module/help/AzureRM.Bootstrapper-help.xml create mode 100644 src/AzureRM.BootStrapper/Module/help/Get-AzureRmModule.md create mode 100644 src/AzureRM.BootStrapper/Module/help/Get-AzureRmProfile.md create mode 100644 src/AzureRM.BootStrapper/Module/help/Install-AzureRmProfile.md create mode 100644 src/AzureRM.BootStrapper/Module/help/Remove-AzureRmDefaultProfile.md create mode 100644 src/AzureRM.BootStrapper/Module/help/Set-AzureRmDefaultProfile.md create mode 100644 src/AzureRM.BootStrapper/Module/help/Uninstall-AzureRmProfile.md create mode 100644 src/AzureRM.BootStrapper/Module/help/Update-AzureRmProfile.md create mode 100644 src/AzureRM.BootStrapper/Module/help/Use-AzureRmProfile.md create mode 100644 src/AzureRM.BootStrapper/Module/help/about_version_profiles.help.txt create mode 100644 src/AzureRM.BootStrapper/Module/help/about_version_profiles.md create mode 100644 src/AzureRM.BootStrapper/build-module.ps1 create mode 100644 src/AzureRM.BootStrapper/dummy.json create mode 100644 src/AzureRM.BootStrapper/pack-module.ps1 create mode 100644 src/AzureRM.BootStrapper/readme.md create mode 100644 src/AzureRM.BootStrapper/test-module.ps1 diff --git a/src/AzureRM.BootStrapper/AzureRM.BootStrapper.csproj b/src/AzureRM.BootStrapper/AzureRM.BootStrapper.csproj new file mode 100644 index 00000000..d9a52059 --- /dev/null +++ b/src/AzureRM.BootStrapper/AzureRM.BootStrapper.csproj @@ -0,0 +1,14 @@ + + + 0.5.0 + 7.1 + netstandard2.0 + Module + ./bin + $(OutputPath) + AzureRM.BootStrapper.nuspec + true + true + + + \ No newline at end of file diff --git a/src/AzureRM.BootStrapper/AzureRM.BootStrapper.nuspec b/src/AzureRM.BootStrapper/AzureRM.BootStrapper.nuspec new file mode 100644 index 00000000..849b9f71 --- /dev/null +++ b/src/AzureRM.BootStrapper/AzureRM.BootStrapper.nuspec @@ -0,0 +1,19 @@ + + + + AzureRM.BootStrapper + 0.5.0 + Microsoft Corporation + Microsoft Corporation + true + https://aka.ms/azps-license + https://github.com/Azure/azurestack-powershell + Microsoft Azure PowerShell: AzureRM.BootStrapper cmdlets + + Microsoft Corporation. All rights reserved. + AzureRM.BootStrapper PSModule AzureStack + + + + + \ No newline at end of file diff --git a/src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.Tests.ps1 b/src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.Tests.ps1 new file mode 100644 index 00000000..cb718019 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.Tests.ps1 @@ -0,0 +1,1462 @@ +#Requires -Modules AzureRM.BootStrapper +$global:testProfileMap = "{`"Profile1`": { `"Module1`": [`"1.0`", `"0.1`"], `"Module2`": [`"1.0`", `"0.2`"] }, `"Profile2`": { `"Module1`": [`"2.0`", `"1.0`"], `"Module2`": [`"2.0`"] }}" + +Describe "Get-ProfileCachePath" { + InModuleScope AzureRM.Bootstrapper { + Mock Test-Path -Verifiable { $false } + Mock New-Item -Verifiable {} + Context "Windows OS Admin" { + $IsWindows = $true + $Script:IsAdmin = $true + It "Should return ProgramData path" { + $result = Get-ProfileCachePath + $result | Should Match "(.*)ProfileCache$" + $result.Contains("ProgramData") | Should Be $true + Assert-VerifiableMock + } + } + + Context "Windows OS Non-Admin" { + $IsWindows = $true + $Script:IsAdmin = $false + It "Should return LOCALAPPDATA path" { + $result = Get-ProfileCachePath + $result | Should Match "(.*)ProfileCache$" + $result.Contains("AppData\Local") | Should Be $true + Assert-VerifiableMock + } + } + + Context "Linux OS Admin" { + $IsWindows = $false + $Script:IsCoreEdition = $true + It "Should return .config path" { + $result = Get-ProfileCachePath + $result | Should Match "(.*)ProfileCache$" + $result.Contains(".config") | Should Be $true + Assert-VerifiableMock + } + } + + # Cleanup + $Script:IsCoreEdition = ($PSVersionTable.PSEdition -eq 'Core') + $script:IsAdmin = $false + if ((-not $Script:IsCoreEdition) -or ($IsWindows)) + { + If (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) + { + $script:IsAdmin = $true + } + } + else { + # on Linux, tests run via sudo will generally report "root" for whoami + if ( (whoami) -match "root" ) + { + $script:IsAdmin = $true + } + } + } +} + +Describe "Get-LatestProfileMapPath" { + InModuleScope AzureRM.Bootstrapper { + Mock Get-ProfileCachePath -Verifiable { "foo\bar" } + + Context "ProfileCache is empty/no profilemaps available" { + Mock Get-ChildItem -Verifiable { $null } + It "Should return null" { + Get-LatestProfileMapPath | Should be $null + Assert-VerifiableMock + } + } + + context "Largest number did not exist" { + Mock Get-LargestNumber -Verifiable { $null } + Mock Get-ChildItem -Verifiable { "foo" } + It "Should return null" { + Get-LatestProfileMapPath | Should be $null + Assert-VerifiableMock + } + } + + Context "Two profile maps available at profile cache" { + $profilemap1 = New-Object -TypeName PSObject + $profilemap1 | Add-Member NoteProperty -Name "Name" -Value '123-pmap1.json' + $profilemap2 = New-Object -TypeName PSObject + $profilemap2 | Add-Member NoteProperty -Name "Name" -Value '42-pmap2.json' + Mock Get-ChildItem -Verifiable { @($profilemap1, $profilemap2)} + Mock Get-LargestNumber -Verifiable { 123 } + It "Should return Latest map" { + Get-LatestProfileMapPath | Should Be $profilemap1 + Assert-VerifiableMock + } + } + } +} + +Describe "Get-LargestNumber" { + InModuleScope AzureRM.BootStrapper { + Context "Profile cache is empty" { + Mock Get-ChildItem -Verifiable { } + It "Should return null" { + Get-LargestNumber | Should Be $null + } + } + + Context "ProfileMaps weren't numbered" { + $profilemap1 = New-Object -TypeName PSObject + $profilemap1 | Add-Member NoteProperty -Name "Name" -Value 'pmap1.json' + $profilemap2 = New-Object -TypeName PSObject + $profilemap2 | Add-Member NoteProperty -Name "Name" -Value 'pmap2.json' + Mock Get-ChildItem -Verifiable { @($profilemap1, $profilemap2) } + It "Should return null" { + Get-LargestNumber | Should Be $null + } + } + + Context "Two numbered Profiles were returned" { + $profilemap1 = New-Object -TypeName PSObject + $profilemap1 | Add-Member NoteProperty -Name "Name" -Value '123-pmap1.json' + $profilemap2 = New-Object -TypeName PSObject + $profilemap2 | Add-Member NoteProperty -Name "Name" -Value '456-pmap2.json' + Mock Get-ChildItem -Verifiable { @($profilemap1, $profilemap2) } + It "Should return largest number" { + Get-LargestNumber | Should Be 456 + } + } + } +} + +Describe "Get-AzureStorageBlob" { + InModuleScope AzureRM.Bootstrapper { + Context "Invoke-WebRequest is properly made" { + $response = New-Object -TypeName psobject + $response | Add-Member -MemberType NoteProperty -Name "StatusCode" -Value "200" + $response | Add-Member -MemberType NoteProperty -Name "Content" -Value "ProfileMap json" + Mock Invoke-CommandWithRetry -Verifiable { $response } + It "Returns proper response" { + $result = Get-AzureStorageBlob + $result.Content | Should Not Be $null + $result.StatusCode | Should Be "200" + Assert-VerifiableMock + } + } + + Context "Invoke-WebRequest threw exception at all retries" { + Mock Invoke-CommandWithRetry -Verifiable { throw } + It "Throws exception" { + { Get-AzureStorageBlob } | Should throw + Assert-VerifiableMock + } + } + } +} + +Describe "Get-AzureProfileMap" { + InModuleScope AzureRM.Bootstrapper { + $script:LatestProfileMapPath = New-Object -TypeName PSObject + $script:LatestProfileMapPath | Add-Member NoteProperty -Name "FullName" -Value "C:\mock\123-MockETag.json" + Mock Get-ProfileCachePath -Verifiable { return "MockPath\ProfileCache"} + $WebResponse = New-Object -TypeName PSObject + $Header = @{"Headers" = @{"ETag" = "MockETag"}} + $WebResponse | Add-Member $Header + Mock Get-AzureStorageBlob -Verifiable { return $WebResponse } + Mock Invoke-CommandWithRetry -Verifiable { ($testProfileMap | ConvertFrom-Json) } + + Context "ProfileCachePath Exists and Etags are equal" { + Mock Test-Path -Verifiable { $true } + It "Returns Correct ProfileMap" { + $result = Get-AzureProfileMap + $result.Profile1 | Should Not Be Empty + $result.Profile2 | Should Not Be Empty + Assert-VerifiableMock + } + } + + Context "ProfileCachePath Exists and ETags are different" { + Mock Out-File -Verifiable {} + $script:LatestProfileMapPath.FullName = "123-MockedDifferentETag.json" + Mock RetrieveProfileMap -Verifiable {$global:testProfileMap | ConvertFrom-Json} + Mock Get-LargestNumber -Verifiable {} + $ProfileMapPath = New-Object -TypeName PSObject + $ProfileMapPath | Add-Member NoteProperty 'FullName' -Value '124-MockedDifferentETag.json' + Mock Get-ChildItem -Verifiable { @($ProfileMapPath)} + Mock Test-Path -Verifiable { $true } + + It "Returns Correct ProfileMap and removes old profilemap" { + $result = Get-AzureProfileMap + $result.Profile1 | Should Not Be Empty + $result.Profile2 | Should Not Be Empty + Assert-VerifiableMock + } + } + + Context "Get-AzureStorageBlob throws exception" { + Mock Get-AzureStorageBlob { throw [System.Net.WebException] } + Mock Test-Path -Verifiable { $true } + It "Throws Web Exception" { + { Get-AzureProfileMap } | Should throw + } + } + } +} + +Describe "RetrieveProfileMap" { + InModuleScope AzureRM.Bootstrapper { + Context "WebResponse content has extra line breaks" { + $WebResponse = "{`n`"Profile1`":`t { `"Module1`": [`"1.0`"], `n`"Module2`": [`"1.0`"] }, `"Profile2`": `n{ `"Module1`": [`"2.0`", `"1.0`"],`n `r`"Module2`": `t[`"2.0`"] }}" + It "Should return proper profile map" { + (RetrieveProfileMap -WebResponse $WebResponse) -like ($global:testProfileMap | ConvertFrom-Json) | Should Be $true + } + } + + Context "WebResponse content has no extra line breaks" { + $WebResponse = $global:testProfileMap + It "Should return proper profile map" { + (RetrieveProfileMap -WebResponse $WebResponse) -like ($global:testProfileMap | ConvertFrom-Json) | Should Be $true + } + + } + } +} + +Describe Get-AzProfile { + InModuleScope AzureRM.Bootstrapper { + + Context "Forces update from Azure Endpoint" { + Mock Get-AzureProfileMap { ($testProfileMap | ConvertFrom-Json) } + It "Should get ProfileMap from Azure Endpoint" { + $result = Get-AzProfile -Update + $result.Profile1 | Should Not Be Empty + $result.Profile2 | Should Not Be Empty + } + It "Checks Mock calls to Get-AzureProfileMap" { + Assert-MockCalled Get-AzureProfileMap -Exactly 1 + } + } + + Context "Gets Azure ProfileMap from Cache" { + $script:LatestProfileMapPath = New-Object -TypeName PSObject + $script:LatestProfileMapPath | Add-Member NoteProperty -Name "FullName" -Value "C:\mock\MockETag.json" + Mock Invoke-CommandWithRetry -Verifiable { $global:testProfileMap | ConvertFrom-Json } + Mock Test-Path -Verifiable { $true } + It "Should get ProfileMap from Cache" { + $result = Get-AzProfile + $result.Profile1 | Should Not Be Empty + $result.Profile2 | Should Not Be Empty + Assert-VerifiableMock + } + } + + Context "ProfileMap is not available from cache" { + Mock Test-Path -Verifiable { $false } + Mock Invoke-CommandWithRetry -Verifiable { return $global:testProfileMap | ConvertFrom-Json} + It "Should get ProfileMap from Embedded source" { + $result = Get-AzProfile + $result.Profile1 | Should Not Be Empty + $result.Profile2 | Should Not Be Empty + Assert-VerifiableMock + } + } + + Context "ProfileMap is not available in cache or Embedded source" { + Mock Test-Path -Verifiable { $false } + Mock Invoke-CommandWithRetry -Verifiable {} + + It "Should throw FileNotFound Exception" { + { Get-AzProfile } | Should Throw + Assert-VerifiableMock + } + } + } +} + +Describe "Get-ProfilesInstalled" { + InModuleScope AzureRM.Bootstrapper { + Context "Valid ProfileMap and Invoke with IncompleteProfiles parameter" { + # Arrange + $VersionObj = New-Object -TypeName System.Version -ArgumentList "1.0" + $moduleObj = New-Object -TypeName PSObject + $moduleObj | Add-Member NoteProperty Version($VersionObj) + $Script:mockCalled = 0 + $mockTestPath = { + $Script:mockCalled++ + if ($Script:mockCalled -le 4) + { + return $moduleObj + } + else { + return $null + } + } + + Mock -CommandName Get-Module -MockWith $mockTestPath + + $IncompleteProfiles = @() + $expected = @{'Profile1'= @{'Module1' = @('1.0') ;'Module2'= @('1.0')}} + + # Act + $result = (Get-ProfilesInstalled -ProfileMap ($global:testProfileMap | ConvertFrom-Json) ([REF]$IncompleteProfiles)) + + # Assert + It "Should return profiles installed" { + $expected -like $result | Should Be $true + } + It "Should return Incomplete profiles" { + $incompleteprofiles[0] -eq 'Profile2' | Should Be $true + } + } + + Context "No profiles Installed and invoke without IncompleteProfiles parameter" { + Mock Get-Module -Verifiable {} + It "Should return empty" { + $result = (Get-ProfilesInstalled -ProfileMap ($global:testProfileMap | ConvertFrom-Json)) + $result.count | Should Be 0 + } + } + + Context "Null ProfileMap" { + It "Should throw exception" { + { Get-ProfilesInstalled -ProfileMap $null } | Should Throw + } + } + } +} + +Describe "Test-ProfilesInstalled" { + InModuleScope AzureRm.Bootstrapper { + Context "Profile associated with Module version is installed" { + $AllProfilesInstalled = @{'Module11.0'= @('Profile1', 'Profile2'); 'Module22.0'= @('Profile2')} + It "Should return ProfilesAssociated" { + $Result = (Test-ProfilesInstalled -version 1.0 -Module 'Module1' -Profile 'Profile1' -PMap ($global:testProfileMap | ConvertFrom-Json) -AllProfilesInstalled $AllProfilesInstalled) + $Result[0] | Should Be 'Profile1' + } + } + + Context "Profile associated with Module version is not installed" { + $AllProfilesInstalled = @{'Module11.0'= @('Profile1', 'Profile2')} + It "Should return empty array" { + $Result = (Test-ProfilesInstalled -version 1.0 -Module 'Module2' -Profile 'Profile2' -PMap ($global:testProfileMap | ConvertFrom-Json) -AllProfilesInstalled $AllProfilesInstalled) + $Result.Count | Should Be 0 + + } + } + } +} + +Describe "Uninstall-ModuleHelper" { + InModuleScope AzureRM.Bootstrapper { + Mock Remove-Module -Verifiable { } + Mock Uninstall-Module -Verifiable { } + Context "Modules are installed" { + # Arrange + $VersionObj = New-Object -TypeName System.Version -ArgumentList "1.0" + $moduleObj = New-Object -TypeName PSObject + $moduleObj | Add-Member NoteProperty Version($VersionObj) + $Script:mockCalled = 0 + $mockTestPath = { + $Script:mockCalled++ + if ($Script:mockCalled -eq 1) + { + return $moduleObj + } + else { + return $null + } + } + + Mock -CommandName Get-Module -MockWith $mockTestPath + + It "Should call Remove-Module and Uninstall-Module" { + Uninstall-ModuleHelper -Module 'Module1' -Version '1.0' -Profile 'Profile1' -RemovePreviousVersions + $Script:mockCalled | Should Be 2 + Assert-VerifiableMock + } + } + + Context "Modules are not installed" { + Mock Get-Module -Verifiable {} + It "Should not call Remove-Module or Uninstall-Module" { + Uninstall-ModuleHelper -Module 'Module1' -Version '1.0' -Profile 'Profile1' + Assert-MockCalled Remove-Module -Exactly 0 + Assert-MockCalled Uninstall-Module -Exactly 0 + } + } + + Context "Uninstall-Module threw error" { + # Arrange + $VersionObj = New-Object -TypeName System.Version -ArgumentList "1.0" + $moduleObj = New-Object -TypeName PSObject + $moduleObj | Add-Member NoteProperty -Name "Path" -Value "TestPath" + $moduleObj | Add-Member NoteProperty Version($VersionObj) + $Script:mockCalled = 0 + $mockTestPath = { + $Script:mockCalled++ + if ($Script:mockCalled -eq 1) + { + return $moduleObj + } + else { + return $null + } + } + + Mock -CommandName Get-Module -MockWith $mockTestPath + Mock Uninstall-Module -Verifiable { throw "No match was found for the specified search criteria and module names" } + It "Should write error 'custom directory' to error pipeline" { + Uninstall-ModuleHelper -Module 'Module1' -Version '1.0' -Profile 'Profile1' -RemovePreviousVersions -ErrorVariable ev -ea SilentlyContinue + ($null -ne ($ev -match "If you installed the module to a custom directory in your path")) | Should be $true + $Script:mockCalled | Should Be 1 + Assert-VerifiableMock + } + } + + Context "Uninstall-Module threw error MSI Install" { + # Arrange + $VersionObj = New-Object -TypeName System.Version -ArgumentList "1.0" + $moduleObj = New-Object -TypeName PSObject + $moduleObj | Add-Member NoteProperty -Name "Path" -Value "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\" + $moduleObj | Add-Member NoteProperty Version($VersionObj) + $Script:mockCalled = 0 + $mockTestPath = { + $Script:mockCalled++ + if ($Script:mockCalled -eq 1) + { + return $moduleObj + } + else { + return $null + } + } + + Mock -CommandName Get-Module -MockWith $mockTestPath + Mock Uninstall-Module -Verifiable { throw "No match was found for the specified search criteria and module names" } + It "Should write error 'msi' to error pipeline" { + Uninstall-ModuleHelper -Module 'Module1' -Version '1.0' -Profile 'Profile1' -RemovePreviousVersions -ErrorVariable ev -ea SilentlyContinue + ($ev -match "If you installed via an MSI") | Should not be $null + $Script:mockCalled | Should Be 1 + Assert-VerifiableMock + } + } + + Context "Uninstall-Module threw error In Use" { + # Arrange + $VersionObj = New-Object -TypeName System.Version -ArgumentList "1.0" + $moduleObj = New-Object -TypeName PSObject + $moduleObj | Add-Member NoteProperty -Name "Path" -Value "TestPath" + $moduleObj | Add-Member NoteProperty Version($VersionObj) + $Script:mockCalled = 0 + $mockTestPath = { + $Script:mockCalled++ + if ($Script:mockCalled -eq 1) + { + return $moduleObj + } + else { + return $null + } + } + + Mock -CommandName Get-Module -MockWith $mockTestPath + Mock Uninstall-Module -Verifiable { throw "The module is currently in use" } + It "Should write error 'in use' to error pipeline" { + Uninstall-ModuleHelper -Module 'Module1' -Version '1.0' -Profile 'Profile1' -RemovePreviousVersions -ErrorVariable ev -ea SilentlyContinue + ($ev -match "The module is currently in use") | Should not be $null + $Script:mockCalled | Should Be 1 + Assert-VerifiableMock + } + } + } +} + +Describe "Uninstall-ProfileHelper" { + InModuleScope AzureRM.Bootstrapper { + Mock Get-AllProfilesInstalled -Verifiable {} + Mock Invoke-UninstallModule -Verifiable {} + + Context "Profile associated with the module is installed" { + It "Should call Invoke-UninstallModule: With Force param" { + Uninstall-ProfileHelper -Profile 'Profile1' -PMap ($global:testProfileMap | ConvertFrom-Json) -Force + Assert-VerifiableMock + Assert-MockCalled Invoke-UninstallModule -Exactly 4 + } + } + + Context "Profile associated with the module is installed" { + It "Should call Invoke-UninstallModule: Without Force param" { + Uninstall-ProfileHelper -Profile 'Profile1' -PMap ($global:testProfileMap | ConvertFrom-Json) + Assert-VerifiableMock + Assert-MockCalled Invoke-UninstallModule -Exactly 4 + } + } + } +} + +Describe "Invoke-UninstallModule" { + InModuleScope AzureRM.Bootstrapper { + Context "Module not associated with any other profile" { + Mock Test-ProfilesInstalled -Verifiable { 'profile1'} + Mock Uninstall-ModuleHelper -Verifiable {} + It "Should Call Uninstall module helper" { + Invoke-UninstallModule -PMap ($global:testProfileMap | ConvertFrom-Json) -Profile 'profile1' -module 'module1' + Assert-VerifiableMock + } + } + + Context "Module associated with more than one profile" { + Mock Test-ProfilesInstalled -Verifiable { @('Profile1', 'Profile2')} + Mock Uninstall-ModuleHelper {} + It "Should not invoke Uninstall module helper" { + Invoke-UninstallModule -PMap ($global:testProfileMap | ConvertFrom-Json) -Profile 'profile1' -module 'module1' + Assert-MockCalled Uninstall-ModuleHelper -Exactly 0 + Assert-VerifiableMock + } + } + } +} + +Describe "Remove-PreviousVersion" { + InModuleScope AzureRM.Bootstrapper { + $AllProfilesInstalled = @{} + Context "Previous versions are installed" { + $VersionObj = New-Object -TypeName System.Version -ArgumentList "0.1" + $moduleObj = New-Object -TypeName PSObject + $moduleObj | Add-Member NoteProperty Version($VersionObj) + Mock Get-Module -Verifiable { $moduleObj} + Mock Import-Module -Verifiable {} + Mock Invoke-UninstallModule -Verifiable {} + It "Should call Invoke-UninstallModule" { + Remove-PreviousVersion -Profile 'Profile1' -LatestMap ($global:testProfileMap|ConvertFrom-Json) + Assert-VerifiableMock + } + + It "Invoke with Module parameter: Should call Invoke-UninstallModule" { + Remove-PreviousVersion -Profile 'Profile1' -Module 'Module1' -LatestMap ($global:testProfileMap|ConvertFrom-Json) + Assert-VerifiableMock + } + } + + Context "Previous versions are not installed" { + Mock Get-Module -Verifiable {} + Mock Import-Module -Verifiable {} + Mock Invoke-UninstallModule {} + It "Should not call Invoke-UninstallModule" { + Remove-PreviousVersion -Profile 'Profile1' -LatestMap ($global:testProfileMap|ConvertFrom-Json) + Assert-VerifiableMock + Assert-MockCalled Invoke-UninstallModule -Exactly 0 + } + } + + Context "No previous versions" { + Mock Get-Module -Verifiable {} + Mock Invoke-UninstallModule -Verifiable {} + Mock Import-Module -Verifiable {} + It "Should not call Invoke-UninstallModule" { + Remove-PreviousVersion -Profile 'Profile2' -module 'Module2' -LatestMap ($global:testProfileMap|ConvertFrom-Json) + Assert-MockCalled Get-Module -Exactly 0 + Assert-MockCalled Invoke-UninstallModule -Exactly 0 + } + } + + Context "Previous version is same as the latest version" { + Mock Get-Module -Verifiable {} + Mock Invoke-UninstallModule -Verifiable {} + Mock Import-Module -Verifiable {} + It "Should not call Invoke-UninstallModule" { + Remove-PreviousVersion -Profile 'Profile2' -module 'Module2' -LatestMap ($global:testProfileMap|ConvertFrom-Json) + Assert-MockCalled Get-Module -Exactly 0 + Assert-MockCalled Invoke-UninstallModule -Exactly 0 + Assert-MockCalled Import-Module -Times 1 + } + } + } +} + +Describe "Get-AllProfilesInstalled" { + InModuleScope AzureRM.Bootstrapper { + Mock Invoke-CommandWithRetry { $global:testProfileMap | ConvertFrom-Json } + Context "Profile Maps are available from cache" { + Mock Get-ProfilesInstalled -Verifiable { @{'Profile1'= @{'Module1'= '1.0'}}} + $expectedResult = @{"Module21.0"=@('Profile1'); "Module11.0"=@('Profile1')} + It "Should return Modules & Profiles Installed" { + (Get-AllProfilesInstalled) -like $expectedResult | Should Be $true + Assert-MockCalled Invoke-CommandWithRetry -Exactly 1 + Assert-MockCalled Get-ProfilesInstalled -exactly 1 + Assert-VerifiableMock + } + } + + Context "Profiles are not installed" { + Mock Get-ProfilesInstalled -Verifiable {} + + It "Should return empty" { + $AllProfilesInstalled = @() + $result = (Get-AllProfilesInstalled) + $result.Count | Should Be 0 + Assert-MockCalled Invoke-CommandWithRetry -Exactly 1 + Assert-MockCalled Get-ProfilesInstalled -exactly 1 + Assert-VerifiableMock + } + } + + Context "Cache is empty" { + $script:LatestProfileMapPath = $null + Mock Get-Item -Verifiable {} + Mock Get-ProfilesInstalled {} + It "Should return empty" { + $result = (Get-AllProfilesInstalled) + $result.Count | Should Be 0 + Assert-MockCalled Invoke-CommandWithRetry -Exactly 1 + Assert-MockCalled Get-ProfilesInstalled -exactly 1 + Assert-VerifiableMock + } + + # Cleanup + $script:LatestProfileMapPath = Get-LatestProfileMapPath + } + } +} + +Describe "Update-ProfileHelper" { + InModuleScope AzureRM.Bootstrapper { + Mock Invoke-CommandWithRetry -Verifiable { $global:testProfileMap } + $script:LatestProfileMapPath = New-Object -TypeName PSObject + $script:LatestProfileMapPath | Add-Member NoteProperty -Name "FullName" -Value "C:\mock\MockETag.json" + Mock Get-AllProfilesInstalled -Verifiable {} + Mock Remove-PreviousVersion -Verifiable {} + + Context "Previous Versions were present" { + It "Should invoke Remove-PreviousVersion" { + Update-ProfileHelper -profile 'Profile1' + Assert-VerifiableMock + } + + It "Invoke with -Module param: Should invoke Remove-PreviousVerison" { + Update-ProfileHelper -profile 'Profile1' -Module 'Module1' -RemovePreviousVersions + Assert-VerifiableMock + } + } + } +} + +Describe "Find-PotentialConflict" { + InModuleScope AzureRM.Bootstrapper { + Context "Modules are installed in other scope" { + $script:IsAdmin = $true + $moduleobj = New-Object -TypeName PSObject + $moduleobj | Add-Member NoteProperty -Name "Path" -Value $Env:HOMEPATH + Mock Get-Module -Verifiable { $moduleobj} + It "Should return false, because force is present" { + (Find-PotentialConflict -Module 'Module1' -Force) | Should Be $false + } + } + + Context "Modules are not installed in other scope" { + $script:IsAdmin = $false + $moduleobj = New-Object -TypeName PSObject + $moduleobj | Add-Member NoteProperty -Name "Path" -Value $Env:HOMEPATH + Mock Get-Module -Verifiable { $moduleobj} + It "Should return false, no conflict" { + (Find-PotentialConflict -Module 'Module1') | Should Be $false + } + } + + Context "Modules were not installed before" { + Mock Get-Module -Verifiable { $null } + It "Should return false, no conflict" { + Find-PotentialConflict -Module 'Module1' | Should Be $false + } + } + } +} + +Describe "Invoke-InstallModule" { + InModuleScope AzureRM.Bootstrapper { + Context "Install-Module has AllowClobber param" { + $cmd = New-Object -TypeName PSObject + $cmd | Add-Member -MemberType NoteProperty -Name "Parameters" -Value @{"AllowClobber" = $true } + Mock Get-Command -Verifiable { $cmd } + + <# It "Should invoke install-module with AllowClobber: No Scope" { + Mock Install-Module -Verifiable {} + Invoke-InstallModule -module "Module1" -version "1.0" + Assert-VerifiableMock + } + + It "Should invoke install-module with AllowClobber: CurrentUser Scope" { + Mock Install-Module -Verifiable -ParameterFilter { $Scope -eq "CurrentUser"} {} + Invoke-InstallModule -module "Module1" -version "1.0" -scope "CurrentUser" + Assert-VerifiableMock + } #> + } + + Context "Install-Module doesn not have AllowClobber" { + $cmd = New-Object -TypeName PSObject + $cmd | Add-Member -MemberType NoteProperty -Name "Parameters" -Value @{} + Mock Get-Command -Verifiable { $cmd } + It "Should invoke install-module with Force: No Scope" { + Mock Install-Module -Verifiable -ParameterFilter { '$Force'} {} + Invoke-InstallModule -module "Module1" -version "1.0" + Assert-VerifiableMock + } + + It "Should invoke install-module with Force: CurrentUser Scope" { + Mock Install-Module -Verifiable -ParameterFilter { '$Force' -and ($Scope -eq "CurrentUser")} {} + Invoke-InstallModule -module "Module1" -version "1.0" -scope "CurrentUser" + Assert-VerifiableMock + } + } + } +} + +Describe "Invoke-CommandWithRetry" { + InModuleScope AzureRM.Bootstrapper { + $scriptBlock = { + Get-ChildItem -ErrorAction Stop + } + + Context "Executes script block successfully at first attempt" { + Mock Get-ChildItem -Verifiable { "contents" } + It "Should return successfully" { + $result = Invoke-CommandWithRetry -scriptBlock $scriptBlock + $result | Should Be "contents" + Assert-VerifiableMock + } + } + + Context "Executes script successfully at one of the retries" { + $Script:mockCalled = 0 + $mockTestPath = { + $Script:mockCalled++ + if ($Script:mockCalled -eq 1) + { + throw + } + else { + return "contents" + } + } + + Mock -CommandName Get-ChildItem -MockWith $mockTestPath + Mock Start-Sleep -Verifiable {} + + It "Should return successfully" { + $result = Invoke-CommandWithRetry -scriptBlock $scriptBlock + $result | Should Be "contents" + Assert-MockCalled Get-ChildItem -Times 2 + Assert-VerifiableMock + } + } + + Context "Fails to execute script during all retries" { + Mock Get-ChildItem -Verifiable { throw } + Mock Start-Sleep -Verifiable {} + It "Throws exception" { + { Invoke-CommandWithRetry -scriptBlock $scriptBlock } | Should throw + } + } + } +} + +Describe "Select-Profile" { + InModuleScope AzureRM.Bootstrapper { + Mock Test-Path -Verifiable { $true } + Context "Scope AllUsers with Admin rights" { + $script:IsAdmin = $true + It "Should return AllUsersAllHosts profile" { + Select-Profile -scope "AllUsers" | Should Be $profile.AllUsersAllHosts + Assert-VerifiableMock + } + } + + Context "Scope CurrentUser" { + It "Should return CurrentUserAllHosts profile" { + Select-Profile -scope "CurrentUser" | Should Be $profile.CurrentUserAllHosts + Assert-VerifiableMock + } + } + + Context "Scope AllUsers no admin rights" { + $script:IsAdmin = $false + It "Should throw for admin rights" { + { Select-Profile -scope "AllUsers" } | Should throw + } + } + + Context "ProfilePath does not exist" { + $script:IsAdmin = $false + Mock Test-Path -Verifiable { $false } + Mock New-Item -Verifiable {} + It "Should create a new file for profile" { + Select-Profile -scope "CurrentUser" | Should Be $profile.CurrentUserAllHosts + Assert-VerifiableMock + } + } + } +} + +Describe "Get-LatestModuleVersion" { + InModuleScope AzureRM.Bootstrapper { + Context "Returns latest version in a version array" { + $versionarray = @("2.0", "1.5", "1.0") + It "Should return the latest version" { + $result = Get-LatestModuleVersion -versions $versionarray + $result | Should Be "2.0" + } + } + } +} + +Describe "Get-ModuleVersion" { + InModuleScope AzureRM.Bootstrapper { + Mock Get-AzProfile -Verifiable { $testProfileMap | ConvertFrom-Json } + Mock Get-LatestModuleVersion -Verifiable { "2.0" } + Context "Gets module version" { + $RollupModule = "Azure.Module1" + It "Should return script block" { + Get-ModuleVersion -armProfile "Profile1" -invocationLine "ipmo azure.module1" | Should Be "2.0" + Assert-VerifiableMock + } + } + } +} + +Describe "Get-ScriptBlock" { + InModuleScope AzureRM.Bootstrapper { + Context "Creates a script block" { + It "Should return script block" { + $result = Get-ScriptBlock -ProfilePath "Profilepath" + $result[1].contains("Import-Module:RequiredVersion") | Should Be $true + Assert-VerifiableMock + } + } + } +} + +Describe "Remove-ProfileSetting" { + InModuleScope AzureRM.Bootstrapper { + Mock Set-Content -Verifiable {} + + Context "Profile contents had bootstrapper scripts" { + $contents = @" +Temp Line 1 +##BEGIN AzureRM.Bootstrapper scripts +Temp Line 2 +Temp Line 3 +##END AzureRM.Bootstrapper scripts +Temp Line 4 +"@ + Mock Get-Content -Verifiable { $contents } + It "Should return lines 1 and 4" { + Remove-ProfileSetting -profilePath "testpath" + Assert-VerifiableMock + } + } + } +} + +Describe "Add-ScopeParam" { + InModuleScope AzureRM.Bootstrapper { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + + It "Should return Scope parameter object" { + (Add-ScopeParam $params) + $params.ContainsKey("Scope") | Should Be $true + } + } +} + +Describe "Add-ProfileParam" { + InModuleScope AzureRM.Bootstrapper { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Mock Get-AzProfile -Verifiable { ($global:testProfileMap | ConvertFrom-Json) } + + It "Should return Profile parameter object" { + (Add-ProfileParam $params) + $params.ContainsKey("Profile") | Should Be $true + Assert-VerifiableMock + } + } +} + +Describe "Add-ForceParam" { + InModuleScope AzureRM.Bootstrapper { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + + It "Should return Force parameter object" { + Add-ForceParam $params + $params.ContainsKey("Force") | Should Be $true + } + } +} + +Describe "Add-RemoveParam" { + InModuleScope AzureRM.Bootstrapper { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + + It "Should return RemovePreviousVersions parameter object" { + (Add-RemoveParam $params) + $params.ContainsKey("RemovePreviousVersions") | Should Be $true + } + } +} + +Describe "Add-SwitchParam" { + InModuleScope AzureRM.Bootstrapper { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + + It "Should return Switch parameter object" { + Add-SwitchParam $params "TestParam" + $params.ContainsKey("TestParam") | Should Be $true + } + } +} + +Describe "Add-ModuleParam" { + InModuleScope AzureRM.Bootstrapper { + + Context "ProfileMap has more than one profile" { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Mock Get-AzProfile -Verifiable { ($global:testProfileMap | ConvertFrom-Json) } + It "Should return Module parameter object" { + (Add-ModuleParam $params) + $params.ContainsKey("Module") | Should Be $true + Assert-VerifiableMock + } + } + + Context "ProfileMap has one profile" { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Mock Get-AzProfile -Verifiable { ("{`"Profile1`": { `"Module1`": [`"1.0`", `"0.1`"], `"Module2`": [`"1.0`", `"0.2`"] }}" ) | ConvertFrom-Json } + It "Should return Module parameter object" { + (Add-ModuleParam $params) + $params.ContainsKey("Module") | Should Be $true + Assert-VerifiableMock + } + } + } + +} + +Describe "Get-AzureRmModule" { + InModuleScope AzureRM.Bootstrapper { + Mock Get-AzProfile -Verifiable { ($global:testProfileMap | ConvertFrom-Json) } + + Context "Module is installed" { + Mock Get-Module -Verifiable { @( [PSCustomObject] @{ Name='Module1'; Version='1.0'; RepositorySourceLocation='foo\bar' }, [PSCustomObject] @{ Name='Module1'; Version='2.0'}) } + It "Should return installed version" { + Get-AzureRmModule -Profile 'Profile1' -Module 'Module1' | Should Be "1.0" + Assert-VerifiableMock + } + } + + Context "Module is not installed" { + Mock Get-Module -Verifiable {} + It "Should return null" { + Get-AzureRmModule -Profile 'Profile1' -Module 'Module1' | Should be $null + Assert-VerifiableMock + } + } + + Context "Module not in the list" { + Mock Get-Module -Verifiable { @( [PSCustomObject] @{ Name='Module1'; Version='1.0'; RepositorySourceLocation='foo\bar' }, [PSCustomObject] @{ Name='Module1'; Version='2.0'}) } + It "Should return null" { + Get-AzureRmModule -Profile 'Profile2' -Module 'Module2' | Should be $null + Assert-VerifiableMock + } + } + + Context "Invoke with invalid parameters" { + It "Should throw" { + { Get-AzureRmModule -Profile 'XYZ' -Module 'ABC' } | Should Throw + } + } + + Context "Invoke with null parameters" { + It "Should throw" { + { Get-AzureRmModule -Profile $null -Module $null } | Should Throw + } + } + + Context "ProfileMap has one profile" { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Mock Get-AzProfile -Verifiable { ("{`"Profile1`": { `"Module1`": [`"1.0`", `"0.1`"], `"Module2`": [`"1.0`", `"0.2`"] }}" ) | ConvertFrom-Json } + Mock Get-Module -Verifiable { @( [PSCustomObject] @{ Name='Module1'; Version='1.0'; RepositorySourceLocation='foo\bar' }, [PSCustomObject] @{ Name='Module1'; Version='2.0'}) } + It "Should return installed version" { + Get-AzureRmModule -Profile 'Profile1' -Module 'Module1' | Should Be "1.0" + Assert-VerifiableMock + } + } + } +} + +Describe "Get-AzureRmProfile" { + InModuleScope AzureRM.Bootstrapper { + Mock Get-AzProfile -Verifiable { ($global:testProfileMap | ConvertFrom-Json) } + + Context "With ListAvailable Switch" { + It "Should return available profiles" { + $Result = (Get-AzureRmProfile -ListAvailable) + $Result.Count | Should be 2 + $Result.ProfileName | Should Not Be $null + $Result.Module1 | Should Not Be $null + Assert-VerifiableMock + } + } + + Context "With ListAvailable and update Switches" { + It "Should return available profiles" { + $Result = (Get-AzureRmProfile -ListAvailable -Update) + $Result.Count | Should be 2 + $Result.ProfileName | Should Not Be $null + $Result.Module1 | Should Not Be $null + Assert-VerifiableMock + } + } + + Context "Without ListAvailable Switch" { + $IncompleteProfiles = @('Profile2') + Mock Get-ProfilesInstalled -Verifiable -ParameterFilter {[REF]$IncompleteProfiles} { @{'Profile1'= @{'Module1' = @('1.0') ;'Module2'= @('1.0')}} } + It "Returns installed Profile" { + $Result = (Get-AzureRmProfile) + $Result.ProfileName | Should Not Be $null + $Result.Module1 | Should Not Be $null + Assert-VerifiableMock + } + } + + Context "No profiles installed" { + Mock Get-ProfilesInstalled -Verifiable {} + It "Returns null" { + (Get-AzureRmProfile) | Should Be $null + Assert-VerifiableMock + } + } + } +} + +Describe "Use-AzureRmProfile" { + InModuleScope AzureRM.Bootstrapper { + $RollupModule = 'Module1' + Mock Get-AzProfile -Verifiable { ($global:testProfileMap | ConvertFrom-Json) } + Mock Install-Module { "Installing module..."} + Mock Import-Module -Verifiable { "Importing Module..."} + Mock Find-PotentialConflict {} + Context "Modules not installed" { + Mock Get-AzureRmModule -Verifiable {} -ParameterFilter {$Profile -eq "Profile1" -and $Module -eq "Module1"} + It "Should install modules" { + $Result = (Use-AzureRmProfile -Profile 'Profile1' -Force) + $Result.Length | Should Be 3 # Includes "Loading module" + $Result[1] | Should Be "Installing module..." + $Result[2] | Should Be "Importing Module..." + Assert-VerifiableMock + } + + It "Invoke with Module param: Should install modules" { + $Result = (Use-AzureRmProfile -Profile 'Profile1' -Module 'Module1' -Force) + $Result.Length | Should Be 3 + $Result[1] | Should Be "Installing module..." + $Result[2] | Should Be "Importing Module..." + Assert-VerifiableMock + } + } + + Context "Modules are installed" { + $RollupModule = "None" + Mock Get-AzureRmModule -Verifiable { "1.0" } -ParameterFilter {$Profile -eq "Profile1" -and $Module -eq "Module1"} + Mock Get-AzureRmModule -Verifiable { "1.0" } -ParameterFilter {$Profile -eq "Profile1" -and $Module -eq "Module2"} + Mock Import-Module { "Module1 1.0 Imported"} -ParameterFilter { $Name -eq "Module1" -and $RequiredVersion -eq "1.0"} + Mock Import-Module { "Module2 1.0 Imported"} -ParameterFilter { $Name -eq "Module2" -and $RequiredVersion -eq "1.0"} + It "Should skip installing modules, imports the right version module" { + $Result = (Use-AzureRmProfile -Profile 'Profile1' -Force) + $Result.length | Should Be 3 + $Result[1] | Should Be "Module1 1.0 Imported" + Assert-MockCalled Install-Module -Exactly 0 + Assert-MockCalled Import-Module -Exactly 2 + Assert-VerifiableMock + } + + It "Invoke with Module param: Should skip installing modules, imports the right version module" { + $Result = (Use-AzureRmProfile -Profile 'Profile1' -Module 'Module1', 'Module2' -Force) + $Result.length | Should Be 3 + $Result[1] | Should Be "Module1 1.0 Imported" + Assert-MockCalled Install-Module -Exactly 0 + Assert-VerifiableMock + + } + } + Context "Invoke with invalid profile" { + It "Should throw" { + { Use-AzureRmProfile -Profile 'WrongProfileName'} | Should Throw + } + } + + Context "Invoke with $null profile" { + It "Should throw" { + { Use-AzureRmProfile -Profile $null} | Should Throw + } + } + + Context "Invoke with Scope as CurrentUser" { + Mock Get-AzureRmModule -Verifiable {} -ParameterFilter {$Profile -eq "Profile1" -and $Module -eq "Module1"} + Mock Install-Module -Verifiable {} -ParameterFilter { $Scope -eq "CurrentUser"} + It "Should invoke Install-ModuleHelper with scope currentuser" { + (Use-AzureRmProfile -Profile 'Profile1' -Force -scope CurrentUser) + Assert-VerifiableMock + } + } + + Context "Invoke with Scope as AllUsers" { + Mock Get-AzureRmModule -Verifiable {} -ParameterFilter {$Profile -eq "Profile1" -and $Module -eq "Module1"} + Mock Install-Module -Verifiable {} -ParameterFilter { $Scope -eq "AllUsers"} + It "Should invoke Install-ModuleHelper with scope AllUsers" { + (Use-AzureRmProfile -Profile 'Profile1' -Force -scope AllUsers) + Assert-VerifiableMock + } + } + + Context "Invoke with invalide module name" { + It "Should throw" { + { Use-AzureRmProfile -Profile 'Profile1' -Module 'MockModule'} | Should Throw + } + } + + Context "Potential Conflict found" { + Mock Find-PotentialConflict -Verifiable { $true } + It "Should skip installing module" { + $Result = (Use-AzureRmProfile -Profile 'Profile1' -Force) + $Result.Contains("Installing module...") | Should Be $false + Assert-VerifiableMock + } + } + + Context "Other versions of the same module found imported" { + Mock Get-AzureRmModule -Verifiable { "1.0" } + $VersionObj = New-Object -TypeName System.Version -ArgumentList "2.0" + $moduleObj = New-Object -TypeName PSObject + $moduleObj | Add-Member NoteProperty -Name "Name" -Value "Module1" + $moduleObj | Add-Member NoteProperty Version($VersionObj) + Mock Get-Module -Verifiable { $moduleObj } + It "Should skip importing module" { + $result = Use-AzureRmProfile -Profile 'Profile1' -ErrorVariable useError -ErrorAction SilentlyContinue + $useError.exception.message.contains("A different profile version of module") | Should Be $true + } + } + + # User tries to execute Use-AzureRmProfile with different profiles & different modules + Context "A different profile's module was previously imported" { + Mock Get-AzureRmModule -Verifiable { "1.0" } + $VersionObj = New-Object -TypeName System.Version -ArgumentList "2.0" + $moduleObj = New-Object -TypeName PSObject + $moduleObj | Add-Member NoteProperty -Name "Name" -Value "Module1" + $moduleObj | Add-Member NoteProperty Version($VersionObj) + Mock Get-Module -Verifiable { $moduleObj } + It "Should skip importing module" { + $result = Use-AzureRmProfile -Profile 'Profile1' -Module 'Module1' -ErrorVariable useError -ErrorAction SilentlyContinue + $useError.exception.message.contains("A different profile version of module") | Should Be $true + } + } + + # User has module2 from profile1 imported; tries to execute Use-AzureRmProfile for profile1 with module1. Should import. + Context "A different module from same profile was previously imported" { + Mock Get-AzureRmModule -Verifiable { "1.0" } + $VersionObj = New-Object -TypeName System.Version -ArgumentList "1.0" + $moduleObj = New-Object -TypeName PSObject + $moduleObj | Add-Member NoteProperty -Name "Name" -Value "Module2" + $moduleObj | Add-Member NoteProperty Version($VersionObj) + Mock Get-Module -Verifiable { $moduleObj } + It "Should import module" { + $result = Use-AzureRmProfile -Profile 'Profile1' -Module 'Module1' + Assert-MockCalled Import-Module -Times 1 + } + } + } +} + +Describe "Install-AzureRmProfile" { + InModuleScope AzureRM.Bootstrapper { + Mock Get-AzProfile -Verifiable { ($global:testProfileMap | ConvertFrom-Json) } + Mock Get-AzureRmModule -Verifiable {} -ParameterFilter { $Profile -eq 'Profile1' -and $Module -eq 'Module1'} + Mock Get-AzureRmModule -Verifiable { "1.0"} -ParameterFilter { $Profile -eq 'Profile1' -and $Module -eq 'Module2'} + Mock Find-PotentialConflict -Verifiable { $false } + + Context "Invoke with valid profile name" { + Mock Invoke-InstallModule -Verifiable { "Installing module Module1... Version 1.0"} + It "Should install Module1" { + (Install-AzureRmProfile -Profile 'Profile1') | Should be "Installing module Module1... Version 1.0" + Assert-VerifiableMock + } + } + + Context "Invoke with invalid profile name" { + It "Should throw" { + { Install-AzureRmProfile -Profile 'WrongProfileName'} | Should Throw + } + } + + Context "Invoke with null profile name" { + It "Should throw" { + { Install-AzureRmProfile -Profile $null } | Should Throw + } + } + + Context "Invoke with Scope as CurrentUser" { + Mock Get-AzureRmModule -Verifiable {} -ParameterFilter {$Profile -eq "Profile1" -and $Module -eq "Module1"} + Mock Invoke-InstallModule -Verifiable {} -ParameterFilter { $Scope -eq "CurrentUser"} + It "Should invoke Install-ModuleHelper with scope currentuser" { + (Install-AzureRmProfile -Profile 'Profile1' -scope CurrentUser) + Assert-VerifiableMock + } + } + + Context "Invoke with Scope as AllUsers" { + Mock Get-AzureRmModule -Verifiable {} -ParameterFilter {$Profile -eq "Profile1" -and $Module -eq "Module1"} + Mock Invoke-InstallModule -Verifiable {} -ParameterFilter { $Scope -eq "AllUsers"} + It "Should invoke Install-ModuleHelper with scope AllUsers" { + (Install-AzureRmProfile -Profile 'Profile1' -scope AllUsers) + Assert-VerifiableMock + } + } + + Context "Potential Conflict found" { + Mock Find-PotentialConflict -Verifiable { $true } + Mock Invoke-InstallModule {} + It "Should skip installing module" { + Install-AzureRmProfile -Profile 'Profile1' + Assert-MockCalled Invoke-InstallModule -Exactly 0 + Assert-VerifiableMock + } + } + + } +} + +Describe "Uninstall-AzureRmProfile" { + InModuleScope AzureRM.Bootstrapper { + Mock Get-AzProfile -Verifiable { ($global:testProfileMap | ConvertFrom-Json) } + Mock Uninstall-ProfileHelper -Verifiable {} + Context "Valid profile name" { + It "Should invoke Uninstall-ProfileHelper" { + Uninstall-AzureRmProfile -Profile 'Profile1' -Force + Assert-VerifiableMock + } + } + + Context "Invoke with invalid profile name" { + It "Should throw" { + { Uninstall-AzureRmProfile -Profile 'WrongProfileName' } | Should Throw + } + } + + Context "Invoke with null profile name" { + It "Should throw" { + { Uninstall-AzureRmProfile -Profile $null } | Should Throw + } + } + } +} + +Describe "Update-AzureRmProfile" { + InModuleScope AzureRM.Bootstrapper { + # Arrange + Mock Get-AzProfile -Verifiable { ($global:testProfileMap | ConvertFrom-Json) } + Mock Get-AzProfile -Verifiable -ParameterFilter { $Update.IsPresent } { ($global:testProfileMap | ConvertFrom-Json) } + + Context "Proper profile with '-RemovePreviousVersions' and '-Force' params" { + Mock Use-AzureRmProfile -Verifiable {} -ParameterFilter { ($Force.IsPresent)} + Mock Update-ProfileHelper -Verifiable {} + + It "Imports profile modules and invokes Update-ProfileHelper" { + Update-AzureRmProfile -Profile 'Profile2' -RemovePreviousVersions -Force + Assert-VerifiableMock + } + + It "Invoke with Module param: Imports profile modules and invokes Update-ProfileHelper" { + Update-AzureRmProfile -Profile 'Profile2' -module 'Module1' -RemovePreviousVersions -Force + Assert-VerifiableMock + } + } + + Context "Invoke with invalid profile name" { + It "Should throw" { + { Update-AzureRmProfile -Profile 'WrongProfileName'} | Should Throw + } + } + + Context "Invoke with null profile name" { + It "Should throw" { + { Update-AzureRmProfile -Profile $null } | Should Throw + } + } + + Context "Invoke with Scope as CurrentUser" { + Mock Use-AzureRmProfile -Verifiable {} -ParameterFilter { ($Force.IsPresent) -and {$Scope -like 'CurrentUser'}} + Mock Update-ProfileHelper -Verifiable {} + It "Should invoke Use-AzureRmProfile with scope currentuser" { + (Update-AzureRmProfile -Profile 'Profile1' -scope CurrentUser -Force -r) + Assert-VerifiableMock + } + } + + Context "Invoke with Scope as AllUsers" { + Mock Use-AzureRmProfile -Verifiable {} -ParameterFilter { ($Force.IsPresent) -and {$Scope -like 'CurrentUser'}} + Mock Update-ProfileHelper -Verifiable {} + It "Should invoke Use-AzureRmProfile with scope AllUsers" { + (Update-AzureRmProfile -Profile 'Profile1' -scope AllUsers -Force -r) + Assert-VerifiableMock + } + } + + Context "Invoke with invalid module name" { + It "Should throw" { + { Update-AzureRmProfile -Profile 'Profile1' -module 'MockModule' } | Should Throw + } + } + + # Cleanup + if (Test-Path '.\MockPath') + { + Remove-Item -Path '.\MockPath' -Force -Recurse + } + } +} + +Describe "Set-BootstrapRepo" { + InModuleScope AzureRM.Bootstrapper { + Context "Repo name is given" { + # Arrange + $currentBootstrapRepo = $script:BootStrapRepo + It "Should set given repo as BootstrapRepo" { + Set-BootstrapRepo -Repo "MockName" + $script:BootStrapRepo | Should Be "MockName" + } + + # Cleanup + $script:BootStrapRepo = $currentBootstrapRepo + } + } +} + +Describe "Set-AzureRmDefaultProfile" { + InModuleScope AzureRM.Bootstrapper { + $sb = { + if ($MyInvocation.Line.Contains("Module1")) { "1.0"} + } + Mock Get-ScriptBlock -Verifiable { $sb } + Mock Invoke-CommandWithRetry -Verifiable {} + Mock Select-Profile -verifiable {} + Mock Remove-ProfileSetting -Verifiable {} + Mock Get-AzProfile -Verifiable { ($global:testProfileMap | ConvertFrom-Json) } + + Context "New default profile value is given" { + It "Setting default profile succeeds" { + $Global:PSDefaultParameterValues.Remove("*-AzureRmProfile:Profile") + Set-AzureRmDefaultProfile -Profile "Profile1" -Force + $Global:PSDefaultParameterValues["*-AzureRmProfile:Profile"] | Should Be "Profile1" + Assert-VerifiableMock + } + } + + Context "User wants to update default profile value" { + It "Should update default profile value" { + $Global:PSDefaultParameterValues.Remove("*-AzureRmProfile:Profile") + Set-AzureRmDefaultProfile -Profile "Profile2" -Force + $Global:PSDefaultParameterValues["*-AzureRmProfile:Profile"] | Should Be "Profile2" + Assert-VerifiableMock + } + } + + Context "Removing old default profile vaule throws" { + Mock Invoke-CommandWithRetry -Verifiable { throw } + It "Should throw for updating default profile" { + $Global:PSDefaultParameterValues.Remove("*-AzureRmProfile:Profile") + { Set-AzureRmDefaultProfile -Profile "Profile1" -Force } | Should throw + Assert-VerifiableMock + } + } + + Context "Set Default Profile with scope as AllUsers in admin shell" { + $script:IsAdmin = $true + Mock Select-Profile -Verifiable { "AllUsersProfile"} + It "Should succeed setting AllUsers Profile" { + $Global:PSDefaultParameterValues.Remove("*-AzureRmProfile:Profile") + Set-AzureRmDefaultProfile -Profile "Profile1" -Scope "AllUsers" -Force + $Global:PSDefaultParameterValues["*-AzureRmProfile:Profile"] | Should Be "Profile1" + Assert-VerifiableMock + } + } + + Context "Set Default Profile with scope as AllUsers in non-admin shell" { + $script:IsAdmin = $false + Mock Select-Profile -Verifiable { throw } + It "Should throw for AllUsers Profile" { + $Global:PSDefaultParameterValues.Remove("*-AzureRmProfile:Profile") + { Set-AzureRmDefaultProfile -Profile "Profile2" -Scope "AllUsers" -Force } | Should throw + Assert-VerifiableMock + } + } + + Context "Set a Default Profile twice" { + It "Should not edit profile content twice" { + $Global:PSDefaultParameterValues.Remove("*-AzureRmProfile:Profile") + Set-AzureRmDefaultProfile -Profile "Profile1" -Force + Set-AzureRmDefaultProfile -Profile "Profile1" -Force + Assert-MockCalled Invoke-CommandWithRetry -Exactly 1 + Assert-VerifiableMock + } + } + } +} + +Describe "Remove-AzureRmDefaultProfile" { + InModuleScope AzureRM.Bootstrapper { + Mock Remove-Module -Verifiable {} + Mock Remove-ProfileSetting -Verifiable {} + Mock Test-Path -Verifiable { $true } + Mock Get-Module -Verifiable { "AzureRm" } + + Context "Default profile presents in the profile file & default variable" { + It "Should successfully remove default profile from shell & profile file" { + Remove-AzureRmDefaultProfile -Force + $Global:PSDefaultParameterValues["*-AzureRmProfile:Profile"] | Should Be $null + Assert-VerifiableMock + } + } + + Context "Default profile is not set previously or was removed" { + It "Should return null for default profile" { + Remove-AzureRmDefaultProfile -Force + $Global:PSDefaultParameterValues["*-AzureRmProfile:Profile"] | Should Be $null + Assert-VerifiableMock + } + } + + Context "Profile files do not exist" { + Mock Test-Path -Verifiable { $false } + It "Should not invoke remove script" { + Remove-AzureRmDefaultProfile -Force + Assert-MockCalled Remove-ProfileSetting -Exactly 0 + } + } + + Context "Remove default profile in admin mode" { + Mock Remove-Module -verifiable {} + $Script:IsAdmin = $true + It "Should remove setting in AllUsersAllHosts and CurrentUserAllHosts profiles" { + Remove-AzureRmDefaultProfile -Force + # For Admin, two profile paths are tested + Assert-MockCalled Test-Path -Exactly 2 + } + } + + Context "Remove default profile in non-admin mode" { + Mock Remove-Module -verifiable {} + $Script:IsAdmin = $false + Mock Invoke-CommandWithRetry -Verifiable {} + It "Should remove setting in CurrentUserAllHosts profile" { + Remove-AzureRmDefaultProfile -Force + Assert-MockCalled Test-Path -Exactly 1 + } + } + } +} \ No newline at end of file diff --git a/src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.psd1 b/src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.psd1 new file mode 100644 index 00000000..b7f7bd26 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.psd1 @@ -0,0 +1,134 @@ +# +# Module manifest for module 'PSGet_AzureRM.BootStrapper' +# +# Generated by: Microsoft Corporation +# +# Generated on: 7/6/2017 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'AzureRM.Bootstrapper.psm1' + +# Version number of this module. +ModuleVersion = '0.5.0' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = '30d8a5cf-3ee5-49ce-b9b0-a4d000d65161' + +# Author of this module +Author = 'Microsoft Corporation' + +# Company or vendor of this module +CompanyName = 'Microsoft Corporation' + +# Copyright statement for this module +Copyright = '(c) 2017 Microsoft Corporation. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'Manage Modules for an Azure Version Profile. This allows selecting the Azure cmdlets that are compatible with an AzureStack instance, an Azure sovereign cloud, or across Azure instances.' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '5.0' + +# Name of the Windows PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the Windows PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +DotNetFrameworkVersion = '4.5' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +CLRVersion = '4.0' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +FormatsToProcess = 'AzureRM.Bootstrapper.Format.ps1xml' + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'Set-BootstrapRepo', 'Update-AzureRmProfile', + 'Uninstall-AzureRmProfile', 'Install-AzureRmProfile', + 'Use-AzureRmProfile', 'Get-AzureRmProfile', 'Get-AzureRmModule', + 'Set-AzureRmDefaultProfile', 'Remove-AzureRmDefaultProfile', + 'Get-ModuleVersion' + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = 'Update-AzureRmProfile', 'Uninstall-AzureRmProfile', + 'Install-AzureRmProfile', 'Use-AzureRmProfile', 'Get-AzureRmProfile', + 'Get-AzureRmModule', 'Set-AzureRmDefaultProfile', + 'Remove-AzureRmDefaultProfile' + +# Variables to export from this module +# VariablesToExport = @() + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = 'Azure','AzureRM','AzureStack','Profile','ResourceManager' + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + ReleaseNotes = '* 0.5.0: Updated for the azure stack profile 2019-03-01-hybrid + * Bug fix to add UseBasicParsing for Invoke-WebRequest' + + # External dependent modules of this module + # ExternalModuleDependencies = '' + + } # End of PSData hashtable + + } # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + diff --git a/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.Format.ps1xml b/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.Format.ps1xml new file mode 100644 index 00000000..996423a6 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.Format.ps1xml @@ -0,0 +1,32 @@ + + + + + ProfileMapDataView + + ProfileMapData + + + + + + + ProfileName + + + + + $RequiredModules = "AzureRM", "AzureRM.Compute", "AzureRM.Netowrk", "AzureRM.Storage", "AzureRM.Sql" + foreach ($name in $RequiredModules) + { + ($_.psobject.properties | % { if ($_.name -eq $name) { $name + " : " + ( $_.value | ft -auto | out-string ) } } ) + } + + + + + + + + + \ No newline at end of file diff --git a/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.ScenarioTests.ps1 b/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.ScenarioTests.ps1 new file mode 100644 index 00000000..39afef08 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.ScenarioTests.ps1 @@ -0,0 +1,828 @@ +Import-Module -Name AzureRM.Bootstrapper +$RollupModule = 'AzureRM' +InModuleScope AzureRM.Bootstrapper { + $ProfileMap = (Get-AzProfile) + $ProfileCachePath = Get-ProfileCachePath + + # Helper function to uninstall all profiles + function Remove-InstalledProfile { + $installedProfiles = Get-ProfilesInstalled -ProfileMap $ProfileMap + if ($installedProfiles.Keys -ne $null) + { + foreach ($profile in $installedProfiles.Keys) + { + Write-Host "Removing profile $profile" + Uninstall-AzureRmProfile -Profile $profile -Force -ErrorAction SilentlyContinue + } + + $profiles = (Get-ProfilesInstalled -ProfileMap $ProfileMap) + if ($profiles.Count -ne 0) + { + Throw "Uninstallation was not successful: Profile(s) $(@($profiles.Keys) -join ',') were not uninstalled correctly." + } + } + } + + Describe "A machine with no profile installed can install profile" { + + # Using Install-AzureRmProfile + Context "New Profile Install - Latest" { + # Arrange + # Uninstall previously installed profiles + Remove-InstalledProfile + + # Launch the test in a new powershell session + # Create a new PS session + $session = New-PSSession + + # Act + # Install latest version + Invoke-Command -Session $session -ScriptBlock { Install-AzureRmProfile -Profile 'Latest' -Force } + + # Assert + It "Should return Latest Profile" { + $result = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + $result[0].Contains('Latest') | Should Be $true + } + + # Clean up + Remove-PSSession -Session $session + } + + # Using Use-AzureRmProfile + Context "New Profile Install - 2016-04-consistent" { + # Arrange + # Uninstall previously installed profiles + Remove-InstalledProfile + + # Create a new PS session + $session = New-PSSession + + # Act + # Install profile '2016-04-consistent' + Invoke-Command -Session $session -ScriptBlock { Use-AzureRmProfile -Profile '2016-04-consistent' -Force } + + # Assert + It "Should return 2016-04-consistent" { + $result = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + $result[0].Contains('2016-04-consistent') | Should Be $true + } + + # Clean up + Remove-PSSession -Session $session + } + } + + Describe "Add: A Machine with a Profile installed can install latest profile" { + InModuleScope AzureRM.Bootstrapper { + + Context "Profile 2016-04-consistent already installed" { + # Arrange + # Create a new PS session + $session = New-PSSession + + # Ensure 2016-09-consistent is installed + $profilesInstalled = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + $profilesInstalled[0].Contains('2016-04-consistent') | Should Be $true + + # Act + # Install profile 'Latest' + Invoke-Command -Session $session -ScriptBlock { Use-AzureRmProfile -Profile 'Latest' -Force } + $result = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + + # Assert + It "Should return 2016-09-consistent & Latest" { + ($result -like "*latest*") -ne $null | Should Be $true + ($result -like "*2016-04-consistent*") -ne $null | Should Be $true + } + + # Clean up + Remove-PSSession -Session $session + } + } + } + + Describe "Attempting to use already installed profile will import the modules to the current session" { + InModuleScope AzureRM.Bootstrapper { + Context "Profile Latest is installed" { + # Should import Latest profile to current session + # Arrange + # Create a new PS session + $session = New-PSSession + + # Ensure profile Latest is installed + $profilesInstalled = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + ($profilesInstalled -like "*latest*") -ne $null | Should Be $true + + # Act + Invoke-Command -Session $session -ScriptBlock { Use-AzureRmProfile -Profile 'Latest' -Force } + + # Get the version of the Latest profile + $ProfileMap = Get-AzProfile + $latestVersion = $ProfileMap.'Latest'.$RollupModule + + # Assert + It "Should return AzureRm module Latest version" { + # Get-module script block + $getModule = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + + $modules = Invoke-Command -Session $session -ScriptBlock $getModule -ArgumentList $RollupModule + + $modules.Name | Should Be $RollupModule + $modules.version | Should Be $latestVersion + } + + # Cleanup + Invoke-Command -Session $session -ScriptBlock { Uninstall-AzureRmProfile -Profile 'Latest' -Force -ea SilentlyContinue } + Remove-PSSession -Session $session + } + } + } + + Describe "User can update their machine to a latest profile" { + InModuleScope AzureRM.Bootstrapper { + # Using Use-AzureRmProfile + Context "Profile 2016-09-consistent is installed: Use-AzureRmProfile" { + # Should refresh profile map from Azure end point and update modules. + # Arrange + # Create a new PS session + $session = New-PSSession + + # Check if '2016-04-consistent' is installed + $profilesInstalled = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + ($profilesInstalled -like "*2016-04-consistent*") -ne $null | Should Be $true + + # Remove latest profile map from cache for testing if it updates from online. + $latestMap = Get-LatestProfileMapPath + if (($latestMap -ne $null) -and (Test-Path $latestMap.FullName)) + { + Remove-Item -Path $latestMap.FullName -Force + } + + # Act + Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile -Update } + Invoke-Command -Session $session -ScriptBlock { Use-AzureRmProfile -Profile 'Latest' -Force } + + # Assert + It "Should return 2016-04-consistent & Latest" { + $result = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + ($result -like "*latest*") -ne $null | Should Be $true + ($result -like "*2016-04-consistent*") -ne $null | Should Be $true + } + + It "Latest version of modules are imported" { + # Get the version of the Latest profile + $ProfileMap = Get-AzProfile + $latestVersion = $ProfileMap.'Latest'.$RollupModule + + # Get-module script block + $getModule = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + + $modules = Invoke-Command -Session $session -ScriptBlock $getModule -ArgumentList $RollupModule + + # Are latest modules imported? + $modules.Name | Should Be $RollupModule + $modules.version | Should Be $latestVersion + } + + It "Last Write Time should be less than 5 minutes" { + # Get LastWriteTime for ProfileMap + $lastWriteTime = (Get-Item -Path (Get-LatestProfileMapPath).FullName).LastWriteTime + (((Get-Date) - $lastWriteTime).TotalMinutes -lt 5) | Should Be $true + } + + # Cleanup + Remove-PSSession -Session $session + } + + # Using Update-AzureRmProfile; Previous Versions do not exist + Context "Profile 2016-04-consistent is installed: Update-AzureRmProfile" { + # Arrange + # Remove existing profiles + Remove-InstalledProfile + + # Create a new PS session + $session = New-PSSession + + # Install profile 2016-04-consistent + Install-AzureRmProfile -Profile '2016-04-consistent' -Force + + # Ensure profile 2016-04-consistent is installed + $profilesInstalled = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + ($profilesInstalled -like "*2016-04-consistent*") -ne $null | Should Be $true + + # Act + # Update to profile 'Latest' + Invoke-Command -Session $session -ScriptBlock { Update-AzureRmProfile -Profile 'Latest' -Force -RemovePreviousVersions } + + # Assert + # Returns 2016-04-consistent & Latest + It "Should Return 2016-04-consistent & Latest" { + $result = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + ($result -like "*latest*") -ne $null | Should Be $true + ($result -like "*2016-04-consistent*") -ne $null | Should Be $true + } + + It "Latest version of modules are imported" { + # Get the version of the Latest profile + $ProfileMap = Get-AzProfile + $latestVersion = $ProfileMap.'Latest'.$RollupModule + + # Get-module script block + $getModule = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + + $modules = Invoke-Command -Session $session -ScriptBlock $getModule -ArgumentList $RollupModule + + # Are latest modules imported? + $modules.Name | Should Be $RollupModule + $modules.version | Should Be $latestVersion + } + + Remove-PSSession -Session $session + } + + # Using Update-AzureRmProfile; Previous Versions exist + Context "Profile 2016-04-consistent is installed: Update-AzureRmProfile with PreviousVerisons" { + # Arrange + # Remove existing profiles + Remove-InstalledProfile + + # Create a new PS session + $session = New-PSSession + + # Install profile 2016-04-consistent + Install-AzureRmProfile -Profile '2016-04-consistent' -Force + + # Ensure profile 2016-04-consistent is installed + $profilesInstalled = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + ($profilesInstalled -like "*2016-04-consistent*") -ne $null | Should Be $true + + # Remove latest profile map from cache for testing if it updates from online. + $latestMap = Get-LatestProfileMapPath + if (($latestMap -ne $null) -and (Test-Path $latestMap.FullName)) + { + Remove-Item -Path $latestMap.FullName -Force + } + + # Add a version of old profilemap with older versions of 'latest' profile to cache + $testProfileMap = "{`"Latest`": { `"AzureRM`": [`"3.3.0`"], `"Azure.Storage`": [`"2.4.0`"] }}" + $testProfileMap | Out-File -FilePath "$ProfileCachePath\TestMap.json" -Force + + # Install the modules from that profilemap + $testProfileMap = ($testProfileMap | ConvertFrom-Json) + + foreach ($Module in ($testProfileMap.'Latest' | Get-Member -MemberType NoteProperty).Name) + { + $oldVersion = $testProfileMap.'Latest'.$Module + Install-Module $Module -RequiredVersion $oldVersion[0] -ErrorAction Stop -AllowClobber + } + + # Act + # Invoke Update-AzureRmProfile 'latest' with -RemovePreviousVersions + Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile -update } + Invoke-Command -Session $session -ScriptBlock { Update-AzureRmProfile -Profile 'Latest' -Force -RemovePreviousVersions } + + # Assert + # Check if new versions of 'latest' are installed + $latestVersion = $ProfileMap.'Latest'.$RollupModule + + It "Should return latest module versions" { + # Get-module script block + $getModule = { + Param($RollupModule) + Get-AzureRmModule -Profile 'Latest' -Module $RollupModule + } + + $version = Invoke-Command -Session $session -ScriptBlock $getModule -ArgumentList $RollupModule + $version | Should Be $latestVersion + } + + # Check if old versions of 'latest' are uninstalled + It "Should return null for old versions" { + # Get-module script block + $getModule = { + Param($RollupModule) + Get-Module -Name $RollupModule -ListAvailable + } + + $modules = Invoke-Command -Session $session -ScriptBlock $getModule -ArgumentList $RollupModule + foreach ($module in $modules) + { + $module.Version -eq $oldVersion | Should Be $false + } + } + + # Check if the old profilemap was removed + It "Should return false for old profile map in cache" { + (Test-Path "$ProfileCachePath\TestMap.json") | Should Be $false + } + } + } + } + + Describe "User can uninstall a profile" { + InModuleScope AzureRM.Bootstrapper { + Context "Latest profile is installed" { + # Should uninstall latest profile + # Arrange + # Create a new PS session + $session = New-PSSession + + # Check if 'Latest' is installed + $profilesInstalled = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + ($profilesInstalled -like "*latest*") -ne $null | Should Be $true + + # Get the version of the Latest profile + $ProfileMap = Get-AzProfile + $latestVersion = $ProfileMap.'Latest'.$RollupModule + + # Act + Invoke-Command -Session $session -ScriptBlock { Uninstall-AzureRmProfile -Profile 'Latest' -Force } + + # Assert + It "Profile Latest is uninstalled" { + $result = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + if($result -ne $null) + { + $result.Contains('Latest') | Should Be $false + } + else { + $true + } + } + + It "Available Modules should not contain uninstalled modules" { + $getModule = { + Param($RollupModule) + Get-Module -Name $RollupModule -ListAvailable + } + $results = Invoke-Command -Session $session -ScriptBlock $getModule -ArgumentList $RollupModule + + # Result won't be null because profile 2016-04-consistent is installed. + foreach ($result in $results) + { + $result.Version -eq $latestVersion | Should Be $false + } + + } + + # Cleanup + Remove-PSSession -Session $session + } + } + } + + Describe "Install Two named profiles and selecting each" { + InModuleScope AzureRM.Bootstrapper { + # Get the version of the respective profile + $ProfileMap = Get-AzProfile + $Version1 = $ProfileMap.'Latest'.$RollupModule + $Version2 = $ProfileMap.'2016-04-consistent'.$RollupModule + + Context "Install Two Profiles" { + # Arrange + # Remove all profiles + Remove-InstalledProfile + + # Create a new PS session + $session = New-PSSession + + # Act + # Install Profile: 2016-08 + Invoke-Command -Session $session -ScriptBlock { Install-AzureRmProfile -Profile 'Latest' -Force } + + # Install Profile: 2016-04 + Invoke-Command -Session $session -ScriptBlock { Install-AzureRmProfile -Profile '2016-04-consistent' -Force } + + # Assert + It "Should return Profiles Latest & 2016-04-consistent" { + $profilesInstalled = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + ($profilesInstalled -like "*2016-04-consistent*") -ne $null | Should Be $true + ($profilesInstalled -like "*latest*") -ne $null | Should Be $true + } + + # Clean up + Remove-PSSession -Session $session + } + + Context "Select diff profiles" { + # Arrange + # Create two new PS sessions + $session1 = New-PSSession + $session2 = New-PSSession + + # Act + # Use-AzureRmProfile will import the respective versions of modules in the session + Invoke-Command -Session $session1 -ScriptBlock { Use-AzureRmProfile -Profile 'Latest' -Force } + Invoke-Command -Session $session2 -ScriptBlock { Use-AzureRmProfile -Profile '2016-04-consistent' -Force } + + $getModule = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + + $result = Invoke-Command -Session $session1 -ScriptBlock { Get-AzureRmProfile } + $module1 = Invoke-Command -Session $session1 -ScriptBlock $getModule -ArgumentList $RollupModule + $module2 = Invoke-Command -Session $session2 -ScriptBlock $getModule -ArgumentList $RollupModule + + # Assert + It "Should return Latest & 2016-04-consistent" { + ($result -like "*latest*") -ne $null | Should Be $true + ($result -like "*2016-04-consistent*") -ne $null | Should Be $true + } + + It "Respective versions of modules are imported" { + # Are respective modules imported? + $module1.Name | Should Be $RollupModule + $module1.version | Should Be $Version1 + + $module2.Name | Should Be $RollupModule + $module2.version | Should Be $Version2 + } + + # "Uninstall All Profiles" + Remove-InstalledProfile + + It "Should return null" { + Get-AzureRmProfile | Should Be $null + } + + It "Modules should return null" { + $getModuleList = { + Param($RollupModule) + Get-Module -ListAvailable -Name $RollupModule + } + + $result1 = Invoke-Command -Session $session1 -ScriptBlock $getModuleList -ArgumentList $RollupModule + foreach ($result in $result1) + { + $result.Version -eq $Version1 | Should Be $false + } + $result2 = Invoke-Command -Session $session2 -ScriptBlock $getModuleList -ArgumentList $RollupModule + foreach ($result in $result2) + { + $result.Version -eq $Version2 | Should Be $false + } + } + + # Cleanup + Remove-PSSession -Session $session1 + Remove-PSSession -Session $session2 + } + } + } + + Describe "Invalid Cases" { + Context "Install wrong profile name" { + # Install profile 'abcTest' + It "Throws Invalid argument error" { + { Install-AzureRmProfile -Profile 'abcTest' } | Should Throw + } + } + + Context "Install null profile name" { + # Install profile 'null' + It "Throws Invalid argument error" { + { Install-AzureRmProfile -Profile $null } | Should Throw + } + } + + Context "Install already installed profile" { + # Arrange + # Create a new PS session + $session = New-PSSession + + # Ensure profile 2016-09 is installed + Install-AzureRmProfile -Profile '2016-04-consistent' -Force + $installedProfile = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + ($installedProfile -like "*2016-04-consistent*") -ne $null | Should Be $true + + # Act + # Install profile '2016-04-consistent' + $result = Invoke-Command -Session $session -ScriptBlock { Install-AzureRmProfile -Profile '2016-04-consistent' -Force } + + # Get modules imported into the session + $getModuleList = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + $modules = Invoke-Command -Session $session -ScriptBlock $getModuleList -ArgumentList $RollupModule + + It "Doesn't install/import the profile" { + $result | Should Be $null + $modules | Should Be $null + } + + # Cleanup + Remove-PSSession -Session $session + } + + Context "Uninstall not installed profile" { + # Arrange + # Create a new PS session + $session = New-PSSession + + # Ensure profile latest is not installed + $installedProfile = Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile } + ($installedProfile -like "*latest*") | Should Be $null + + # Act + # Uninstall profile 'latest' + $result = Invoke-Command -Session $session -ScriptBlock { Uninstall-AzureRmProfile -Profile 'latest' -Force} + + It "Doesn't uninstall/throw" { + $result | Should Be $null + } + + # Cleanup + Remove-PSSession -Session $session + } + + Context "Use-AzureRmProfile with wrong profile name" { + # Install profile 'abcTest' + It "Throws Invalid argument error" { + { Use-AzureRmProfile -Profile 'abcTest' } | Should Throw + } + } + } + + Describe "Failure Recovery: Attempt to install profile recovers from error" { + InModuleScope AzureRM.Bootstrapper { + Context "Azure ProfileMap endpoint threw exception" { + # Arrange + # Mock Get-ProfileMap returns error + Mock Get-AzureProfileMap -Verifiable { throw [System.Net.WebException] } + + # Mock Install-Modules returns error + Mock Install-Module -Verifiable { throw } + Mock Get-AzureRmModule -Verifiable {} + + # Act & Assert + It "Should not download/install the latest profile" { + { Get-AzureRmProfile -Update } | Should Throw + { Install-AzureRmProfile -Profile 'Latest' -Force } | Should Throw + } + + It "Last Write time should not be less than 3 mins" { + # Get LastWriteTime for ProfileMap + $lastWriteTime = (Get-Item -Path (Get-LatestProfileMapPath).FullName).LastWriteTime + (((Get-Date) - $lastWriteTime).TotalMinutes -gt 3) | Should Be $true + Assert-VerifiableMock + } + } + + Context "Retry install after ProfileMap update" { + # Arrange + # Create a new PS session + $session = New-PSSession + + # Remove ProfileMap.json to test if it is updated from online + Remove-Item -Path (Get-LatestProfileMapPath).FullName -Force + + # Act + # Update ProfileMap + Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile -Update } + + # Install profile 'Latest' + Invoke-Command -Session $session -ScriptBlock { Use-AzureRmProfile -Profile 'Latest' -Force } + + # Assert + It "Installs & Imports Latest profile to the session" { + $getModuleList = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + $modules = Invoke-Command -Session $session -ScriptBlock $getModuleList -ArgumentList $RollupModule + + # Get the version of the Latest profile + $ProfileMap = Get-AzProfile + $latestVersion = $ProfileMap.'Latest'.$RollupModule + + # Are latest modules imported? + $modules.Name | Should Be $RollupModule + $modules.version | Should Be $latestVersion + } + + It "Last Write time should be less than 5 mins" { + # Get LastWriteTime for ProfileMap + $lastWriteTime = (Get-Item -Path (Get-LatestProfileMapPath).FullName).LastWriteTime + (((Get-Date) - $lastWriteTime).TotalMinutes -lt 5) | Should Be $true + } + + # Cleanup + Remove-PSSession -Session $session + } + } + } + + Describe "Install profiles using Scope" { + InModuleScope AzureRM.Bootstrapper { + + Context "Using Install-AzureRmProfile: Scope 'CurrentUser'" { + # Arrange + # Create a new PS session + $session = New-PSSession + + # Remove installed profiles + Remove-InstalledProfile + + # Act + # Install profile Latest scope as current user + Invoke-Command -Session $session -ScriptBlock { Install-AzureRmProfile -Profile 'Latest' -scope 'CurrentUser' -Force } + + # Assert + It "Installs & Imports Latest profile to the session" { + # Get the version of the Latest profile + $ProfileMap = Get-AzProfile + $latestVersion = $ProfileMap.'Latest'.$RollupModule + + $getModuleList = { + Param($RollupModule, $latestVersion) + Get-Module -Name $RollupModule -ListAvailable | Where-Object {$_.Version -like $latestVersion} + } + $modules = Invoke-Command -Session $session -ScriptBlock $getModuleList -ArgumentList @($RollupModule, $latestVersion) + + # Are latest modules imported? + $modules.Name | Should Be $RollupModule + $modules.version | Should Be $latestVersion + } + } + + Context "Using Use-AzureRmProfile: Scope 'AllUsers'" { + # Arrange + # Create a new PS session + $session = New-PSSession + + # Remove installed profiles + Remove-InstalledProfile + + # Act + # Install profile 2016-04-consistent scope as all users + Invoke-Command -Session $session -ScriptBlock { Use-AzureRmProfile -Profile '2016-04-consistent' -scope 'AllUsers' -Force } + + # Assert + It "Installs & Imports 2016-04-consistent profile to the session" { + $getModuleList = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + $modules = Invoke-Command -Session $session -ScriptBlock $getModuleList -ArgumentList $RollupModule + + # Get the version of the 2016-04-consistent profile + $ProfileMap = Get-AzProfile + $version = $ProfileMap.'2016-04-consistent'.$RollupModule + + # Are appropriate modules imported? + $modules.Name | Should Be $RollupModule + $modules.version | Should Be $version + } + } + + Context "Using Update-AzureRmProfile: Scope 'CurrentUser' " { + # Arrange + # Create a new PS session + $session = New-PSSession + + # Remove installed profiles + Remove-InstalledProfile + + # Act + # Install profile 2016-04-consistent scope as current user + Invoke-Command -Session $session -ScriptBlock { Update-AzureRmProfile -Profile '2016-04-consistent' -scope 'CurrentUser' -Force -r } + + # Assert + It "Installs & Imports 2016-04-consistent profile to the session" { + $getModuleList = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + $modules = Invoke-Command -Session $session -ScriptBlock $getModuleList -ArgumentList $RollupModule + + # Get the version of the 2016-04-consistent profile + $ProfileMap = Get-AzProfile + $version = $ProfileMap.'2016-04-consistent'.$RollupModule + + # Are correct modules imported? + $modules.Name | Should Be $RollupModule + $modules.version | Should Be $version + } + } + } + } + + Describe "Load/Import AzureRmProfile modules" { + InModuleScope AzureRM.Bootstrapper { + Context "Using Use-AzureRmProfile: Modules are not installed" { + # Arrange + # Create a new PS session + $session = New-PSSession + + # Remove installed profiles + Remove-InstalledProfile + + # Act + # Use module from Latest profile scope as current user + Invoke-Command -Session $session -ScriptBlock { Use-AzureRmProfile -Profile '2016-04-consistent' -Module 'AzureRM.Storage' -Force -scope 'CurrentUser'} + + # Assert + $RollupModule = 'AzureRM.Storage' + It "Installs & Imports 2016-04-consistent profile's module to the session" { + $getModuleList = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + $modules = Invoke-Command -Session $session -ScriptBlock $getModuleList -ArgumentList $RollupModule + + # Get the version of the 2016-04-consistent profile + $ProfileMap = Get-AzProfile + $version = $ProfileMap.'2016-04-consistent'.$RollupModule + + # Are 2016-04-consistent modules imported? + $modules.Name | Should Be $RollupModule + $modules.version | Should Be $version + } + } + + Context "Using Update-AzureRmProfile: Previous version of modules are installed" { + # Arrange + # Create a new PS session + $session = New-PSSession + + # Remove installed profiles + Remove-InstalledProfile + + # Remove latest profile map from cache for testing if it updates from online. + $latestMap = Get-LatestProfileMapPath + if (($latestMap -ne $null) -and (Test-Path $latestMap.FullName)) + { + Remove-Item -Path $latestMap.FullName -Force + } + + # Add a version of old profilemap with older versions of 'latest' profile to cache + $testProfileMap = "{`"Latest`": {`"Azure.Storage`": [`"2.4.0`"], `"AzureRM.Storage`": [`"2.4.0`"] }}" + $testProfileMap | Out-File -FilePath "$ProfileCachePath\TestMap.json" -Force + + # Install the modules from that profilemap + $testProfileMap = ($testProfileMap | ConvertFrom-Json) + + foreach ($Module in ($testProfileMap.'Latest' | Get-Member -MemberType NoteProperty).Name) + { + $oldVersion = $testProfileMap.'Latest'.$Module + Install-Module $Module -RequiredVersion $oldVersion[0] -ErrorAction Stop -AllowClobber + } + + # Act + # Update profile Latest with -RemovePreviousVersions + Invoke-Command -Session $session -ScriptBlock { Get-AzureRmProfile -Update } + Invoke-Command -Session $session -ScriptBlock { Update-AzureRmProfile -Profile 'Latest' -Module 'AzureRM.Storage', 'Azure.Storage' -Force -r } + + # Assert + It "Installs & Imports latest profile's module ('AzureRM.Storage') to the session" { + $getModuleList = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + $module1 = Invoke-Command -Session $session -ScriptBlock $getModuleList -ArgumentList 'AzureRM.Storage' + + # Get the version of the latest profile + $ProfileMap = Get-AzProfile + $version1 = $ProfileMap.'Latest'.'AzureRM.Storage' + + # Are latest modules imported? + $module1.Name | Should Be 'AzureRM.Storage' + $module1.version | Should Be $version1 + } + + It "Installs & Imports latest profile's module ('Azure.Storage') to the session" { + $getModuleList = { + Param($RollupModule) + Get-Module -Name $RollupModule + } + $module2 = Invoke-Command -Session $session -ScriptBlock $getModuleList -ArgumentList 'Azure.Storage' + + # Get the version of the latest profile + $ProfileMap = Get-AzProfile + $version2 = $ProfileMap.'Latest'.'Azure.Storage' + + # Are latest modules imported? + $module2.Name | Should Be 'Azure.Storage' + $module2.version | Should Be $version2 + } + + # Check if the old profilemap was removed + It "Should return false for old profile map in cache" { + (Test-Path "$ProfileCachePath\TestMap.json") | Should Be $false + } + } + } + } +} \ No newline at end of file diff --git a/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.psm1 b/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.psm1 new file mode 100644 index 00000000..23610d6a --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.psm1 @@ -0,0 +1,1312 @@ +$RollUpModule = "AzureRM" +$PSProfileMapEndpoint = "https://azureprofile.azureedge.net/powershell/profilemap.json" +$script:BootStrapRepo = "PSGallery" + +# Is it Powershell Core edition? +$Script:IsCoreEdition = ($PSVersionTable.PSEdition -eq 'Core') + +# Check if current user is Admin to decide on cache path +$script:IsAdmin = $false +if ((-not $Script:IsCoreEdition) -or ($IsWindows)) +{ + $script:ProgramFilesPSPath = $env:ProgramFiles + If (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) + { + $script:IsAdmin = $true + } +} +else { + $script:ProgramFilesPSPath = $PSHOME + # on Linux, tests run via sudo will generally report "root" for whoami + if ( (whoami) -match "root" ) + { + $script:IsAdmin = $true + } +} + +# Get profile cache path +function Get-ProfileCachePath +{ + if ((-not $Script:IsCoreEdition) -or ($IsWindows)) + { + $ProfileCache = Join-Path -path $env:LOCALAPPDATA -childpath "Microsoft\AzurePowerShell\ProfileCache" + if ($script:IsAdmin) + { + $ProfileCache = Join-Path -path $env:ProgramData -ChildPath "Microsoft\AzurePowerShell\ProfileCache" + } + } + else { + $ProfileCache = "$HOME/.config/Microsoft/AzurePowerShell/ProfileCache" + } + + # If profile cache directory does not exist, create one. + if(-Not (Test-Path $ProfileCache)) + { + New-Item -ItemType Directory -Force -Path $ProfileCache | Out-Null + } + + return $ProfileCache +} + +# Function to find the latest profile map from cache +function Get-LatestProfileMapPath +{ + $ProfileCache = Get-ProfileCachePath + $ProfileMapPaths = Get-ChildItem $ProfileCache + if ($null -eq $ProfileMapPaths) + { + return + } + + $LargestNumber = Get-LargestNumber -ProfileCache $ProfileCache + if ($null -eq $LargestNumber) + { + return + } + + $LatestMapPath = $ProfileMapPaths | Where-Object { $_.Name.Startswith($LargestNumber.ToString() + '-') } + return $LatestMapPath +} + +# Function to get the largest number in profile cache profile map names: This helps to find the latest map +function Get-LargestNumber +{ + param($ProfileCache) + + $ProfileMapPaths = Get-ChildItem $ProfileCache + $LargestNumber = $ProfileMapPaths | ForEach-Object { if($_.Name -match "\d+-") { $matches[0] -replace '-' } } | Measure-Object -Maximum + if ($null -ne $LargestNumber) + { + return $LargestNumber.Maximum + } +} + +# Find the latest ProfileMap +$script:LatestProfileMapPath = Get-LatestProfileMapPath + +# Make Web-Call +function Get-AzureStorageBlob +{ + $ScriptBlock = { + Invoke-WebRequest -uri $PSProfileMapEndpoint -UseBasicParsing -TimeoutSec 120 -ErrorVariable RestError + } + + $WebResponse = Invoke-CommandWithRetry -ScriptBlock $ScriptBlock + return $WebResponse +} + +# Get-ProfileMap from Azure Endpoint +function Get-AzureProfileMap +{ + Write-Verbose "Updating profiles" + $ProfileCache = Get-ProfileCachePath + + # Get online profile data using Web request + $WebResponse = Get-AzureStorageBlob + + # Get ETag value for OnlineProfileMap + $OnlineProfileMapETag = $WebResponse.Headers["ETag"] + + # If profilemap json exists, compare online Etag and cached Etag; if not different, don't replace cache. + if (($null -ne $script:LatestProfileMapPath) -and ($script:LatestProfileMapPath -match "(\d+)-(.*.json)")) + { + [string]$ProfileMapETag = [System.IO.Path]::GetFileNameWithoutExtension($Matches[2]) + if (($ProfileMapETag -eq $OnlineProfileMapETag) -and (Test-Path $script:LatestProfileMapPath.FullName)) + { + $scriptBlock = { + Get-Content -Raw -Path $script:LatestProfileMapPath.FullName -ErrorAction stop | ConvertFrom-Json + } + $ProfileMap = Invoke-CommandWithRetry -ScriptBlock $scriptBlock + + if ($null -ne $ProfileMap) + { + return $ProfileMap + } + } + } + + # If profilemap json doesn't exist, or if online ETag and cached ETag are different, cache online profile map + $LargestNoFromCache = Get-LargestNumber -ProfileCache $ProfileCache + if ($null -eq $LargestNoFromCache) + { + $LargestNoFromCache = 0 + } + + $ChildPathName = ($LargestNoFromCache+1).ToString() + '-' + ($OnlineProfileMapETag) + ".json" + $CacheFilePath = (Join-Path $ProfileCache -ChildPath $ChildPathName) + $OnlineProfileMap = RetrieveProfileMap -WebResponse $WebResponse + $OnlineProfileMap | ConvertTo-Json -Compress | Out-File -FilePath $CacheFilePath + + # Store old profile map's path before Updating + $oldProfileMap = $script:LatestProfileMapPath + + # Update $script:LatestProfileMapPath + $script:LatestProfileMapPath = Get-ChildItem $ProfileCache | Where-Object { $_.FullName.equals($CacheFilePath)} + + # Remove old profile map if it exists + if (($null -ne $oldProfileMap) -and (Test-Path $oldProfileMap.FullName)) + { + $ScriptBlock = { + Remove-Item -Path $oldProfileMap.FullName -Force -ErrorAction Stop + } + Invoke-CommandWithRetry -ScriptBlock $ScriptBlock + } + + return $OnlineProfileMap +} + +# Helper to retrieve profile map from http response +function RetrieveProfileMap +{ + param($WebResponse) + $OnlineProfileMap = $WebResponse | ConvertFrom-Json + return $OnlineProfileMap +} + +# Get ProfileMap from Cache, online or embedded source +function Get-AzProfile +{ + [CmdletBinding()] + param([Switch]$Update) + + $Update = $PSBoundParameters.Update + # If Update is present, download ProfileMap from online source + if ($Update.IsPresent) + { + return (Get-AzureProfileMap) + } + + # Check the cache + if(($null -ne $script:LatestProfileMapPath) -and (Test-Path $script:LatestProfileMapPath.FullName)) + { + $scriptBlock = { + Get-Content -Raw -Path $script:LatestProfileMapPath.FullName -ErrorAction stop | ConvertFrom-Json + } + $ProfileMap = Invoke-CommandWithRetry -ScriptBlock $scriptBlock + if ($null -ne $ProfileMap) + { + return $ProfileMap + } + } + + # If cache doesn't exist, Check embedded source + $defaults = [System.IO.Path]::GetDirectoryName($PSCommandPath) + $scriptBlock = { + Get-Content -Raw -Path (Join-Path -Path $defaults -ChildPath "ProfileMap.json") -ErrorAction stop | ConvertFrom-Json + } + $ProfileMap = Invoke-CommandWithRetry -ScriptBlock $scriptBlock + if($null -eq $ProfileMap) + { + # Cache & Embedded source empty; Return error and stop + throw [System.IO.FileNotFoundException] "Profile meta data does not exist. Use 'Get-AzureRmProfile -Update' to download from online source." + } + + return $ProfileMap +} + +# Lists the profiles that are installed on the machine +function Get-ProfilesInstalled +{ + param([parameter(Mandatory = $true)] [PSCustomObject] $ProfileMap, [REF]$IncompleteProfiles) + $result = @{} + $AllProfiles = ($ProfileMap | Get-Member -MemberType NoteProperty).Name + foreach ($key in $AllProfiles) + { + Write-Verbose "Checking if profile $key is installed" + foreach ($module in ($ProfileMap.$key | Get-Member -MemberType NoteProperty).Name) + { + $ModulesList = (Get-Module -Name $Module -ListAvailable) + $versionList = $ProfileMap.$key.$module + foreach ($version in $versionList) + { + if ($null -ne ($ModulesList | Where-Object { $_.Version -eq $version})) + { + if ($result.ContainsKey($key)) + { + if ($result[$key].Containskey($module)) + { + $result[$key].$module += $version + } + else + { + $result[$key] += @{$module = @($version)} + } + } + else + { + $result.Add($key, @{$module = @($version)}) + } + } + } + } + + # If not all the modules from a profile are installed, add it to $IncompleteProfiles + if(($result.$key.Count -gt 0) -and ($result.$key.Count -ne ($ProfileMap.$key | Get-Member -MemberType NoteProperty).Count)) + { + if ($result.$key.Contains($RollUpModule)) + { + continue + } + + $result.Remove($key) + if ($null -ne $IncompleteProfiles) + { + $IncompleteProfiles.Value += $key + } + } + } + return $result +} + +# Get profiles installed associated with the module version +function Test-ProfilesInstalled +{ + param([System.Version]$version, [String]$Module, [String]$Profile, [PSObject]$PMap, [hashtable]$AllProfilesInstalled) + + # Profiles associated with the particular module version - installed? + $profilesAssociated = @() + foreach ($profileInAllProfiles in $AllProfilesInstalled[$Module + $version]) + { + $profilesAssociated += $profileInAllProfiles + } + return $profilesAssociated +} + +# Function to uninstall module +function Uninstall-ModuleHelper +{ + [CmdletBinding(SupportsShouldProcess = $true)] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidShouldContinueWithoutForce", "")] + param([String]$Profile, $Module, [System.Version]$version, [Switch]$RemovePreviousVersions) + + $Remove = $PSBoundParameters.RemovePreviousVersions + Do + { + $moduleInstalled = Get-Module -Name $Module -ListAvailable | Where-Object { $_.Version -eq $version} + if ($PSCmdlet.ShouldProcess("$module version $version", "Remove module")) + { + if (($null -ne $moduleInstalled) -and ($Remove.IsPresent -or $PSCmdlet.ShouldContinue("Uninstall module $Module version $version", "Uninstall Modules for profile $Profile"))) + { + Write-Verbose "Removing module from session" + Remove-Module -Name $module -Force -ErrorAction "SilentlyContinue" + try + { + Write-Verbose "Uninstalling module $module version $version" + Uninstall-Module -Name $module -RequiredVersion $version -Force -ErrorAction Stop + } + catch + { + if ($_.Exception.Message -match "No match was found") + { + # Check for msi installation (Install folder: C:\ProgramFiles(x86)\Microsoft SDKs\Azure\PowerShell) Only in windows + if ((-not $Script:IsCoreEdition) -or ($IsWindows)) + { + $sdkPath1 = (join-path ${env:ProgramFiles(x86)} -childpath "\Microsoft SDKs\Azure\PowerShell\") + $sdkPath2 = (join-path $script:ProgramFilesPSPath -childpath "\Microsoft SDKs\Azure\PowerShell\") + if (($null -ne $moduleInstalled.Path) -and (($moduleInstalled.Path.Contains($sdkPath1) -or $moduleInstalled.Path.Contains($sdkPath2)))) + { + Write-Error "Unable to uninstall module $module because it was installed in a different scope than expected. If you installed via an MSI, please uninstall the MSI before proceeding." -Category InvalidOperation + break + } + } + Write-Error "Unable to uninstall module $module because it was installed in a different scope than expected. If you installed the module to a custom directory in your path, please remove the module manually, by using Uninstall-Module, or removing the module directory." -Category InvalidOperation + } + else { + Write-Error $_.Exception.Message + } + break + } + } + else { + break + } + } + else { + break + } + } + While($null -ne $moduleInstalled); +} + +# Help function to uninstall a profile +function Uninstall-ProfileHelper +{ + [CmdletBinding()] + param([PSObject]$PMap, [String]$Profile, [Switch]$Force) + $modules = ($PMap.$Profile | Get-Member -MemberType NoteProperty).Name + + # Get-Profiles installed across all hashes. This is to avoid uninstalling modules that are part of other installed profiles + $AllProfilesInstalled = Get-AllProfilesInstalled + + foreach ($module in $modules) + { + $versionList = $PMap.$Profile.$module + foreach ($version in $versionList) + { + if ($Force.IsPresent) + { + Invoke-UninstallModule -PMap $PMap -Profile $Profile -Module $module -version $version -AllProfilesInstalled $AllProfilesInstalled -RemovePreviousVersions + } + else { + Invoke-UninstallModule -PMap $PMap -Profile $Profile -Module $module -version $version -AllProfilesInstalled $AllProfilesInstalled + } + } + } +} + +# Checks if the module is part of other installed profiles. Calls Uninstall-ModuleHelper if not. +function Invoke-UninstallModule +{ + [CmdletBinding()] + param([PSObject]$PMap, [String]$Profile, $Module, [System.Version]$version, [hashtable]$AllProfilesInstalled, [Switch]$RemovePreviousVersions) + + # Check if the profiles associated with the module version are installed. + Write-Verbose "Checking module dependency to any other profile installed" + $profilesAssociated = Test-ProfilesInstalled -version $version -Module $Module -Profile $Profile -PMap $PMap -AllProfilesInstalled $AllProfilesInstalled + + # If more than one profile is installed for the same version of the module, do not uninstall + if ($profilesAssociated.Count -gt 1) + { + return + } + + $PSBoundParameters.Remove('AllProfilesInstalled') | Out-Null + $PSBoundParameters.Remove('PMap') | Out-Null + + Uninstall-ModuleHelper @PSBoundParameters +} + +# Helps to uninstall previous versions of modules in the profile +function Remove-PreviousVersion +{ + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] + param([PSObject]$LatestMap, [hashtable]$AllProfilesInstalled, [String]$Profile, [Array]$Module, [Switch]$RemovePreviousVersions) + + $Remove = $PSBoundParameters.RemovePreviousVersions + $Modules = $PSBoundParameters.Module + + Write-Verbose "Checking if previous versions of modules are installed" + + if ($null -eq $Modules) + { + $Modules = ($LatestMap.$Profile | Get-Member -MemberType NoteProperty).Name + } + + foreach ($module in $Modules) + { + # Skip the latest version; first element will be the latest version + $versionList = $LatestMap.$Profile.$module + $versionList = $versionList | Where-Object { $_ -ne $versionList[0] } + foreach ($version in $versionList) + { + # Is that module version installed? If not skip; + if ($null -eq (Get-Module -Name $Module -ListAvailable | Where-Object { $_.Version -eq $version} )) + { + continue + } + + Write-Verbose "Previous versions of modules were found. Trying to uninstall..." + if ($Remove.IsPresent) + { + Invoke-UninstallModule -PMap $LatestMap -Profile $Profile -Module $module -version $version -AllProfilesInstalled $AllProfilesInstalled -RemovePreviousVersions + } + else { + Invoke-UninstallModule -PMap $LatestMap -Profile $Profile -Module $module -version $version -AllProfilesInstalled $AllProfilesInstalled + } + } + + # Uninstall removes module from session; import latest version again + $versions = $LatestMap.$Profile.$module + $version = Get-LatestModuleVersion -versions $versions + Import-Module $Module -RequiredVersion $version -Global + } +} + +# Gets profiles installed as @{Module+Version = @(profile)} for checking module dependency during uninstall +function Get-AllProfilesInstalled +{ + $AllProfilesInstalled = @{} + # If Cache is empty, use embedded source + if ($null -eq $script:LatestProfileMapPath) + { + $ModulePath = [System.IO.Path]::GetDirectoryName($PSCommandPath) + $script:LatestProfileMapPath = Get-Item -Path (Join-Path -Path $ModulePath -ChildPath "ProfileMap.json") + } + + $scriptBlock = { + Get-Content -Raw -Path $script:LatestProfileMapPath.FullName -ErrorAction stop | ConvertFrom-Json + } + $ProfileMap = Invoke-CommandWithRetry -ScriptBlock $scriptBlock + $profilesInstalled = (Get-ProfilesInstalled -ProfileMap $ProfileMap) + foreach ($Profile in $profilesInstalled.Keys) + { + foreach ($module in ($profilesinstalled.$profile.Keys)) + { + $versionList = $profilesinstalled.$Profile.$Module + foreach ($version in $versionList) + { + if ($AllProfilesInstalled.ContainsKey(($Module + $version))) + { + if ($Profile -notin $AllProfilesInstalled[($Module + $version)]) + { + $AllProfilesInstalled[($Module + $version)] += $Profile + } + } + else { + $AllProfilesInstalled.Add(($Module + $version), @($Profile)) + } + } + } + } + return $AllProfilesInstalled +} + +# Helps to remove-previous versions of the update-profile and clean up cache +function Update-ProfileHelper +{ + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] + param([String]$Profile, [Array]$Module, [Switch]$RemovePreviousVersions) + + Write-Verbose "Attempting to clean up previous versions" + + # Cache was updated before calling this function, so latestprofilemap will not be null. + $scriptBlock = { + Get-Content -Raw -Path $script:LatestProfileMapPath.FullName -ErrorAction stop | ConvertFrom-Json + } + $LatestProfileMap = Invoke-CommandWithRetry -ScriptBlock $scriptBlock + + $AllProfilesInstalled = Get-AllProfilesInstalled + Remove-PreviousVersion -LatestMap $LatestProfileMap -AllProfilesInstalled $AllProfilesInstalled @PSBoundParameters +} + +# If cmdlets were installed at a different scope, warn users of the potential conflict +function Find-PotentialConflict +{ + [CmdletBinding(SupportsShouldProcess = $true)] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")] + param([string]$Module, [switch]$Force) + + Write-Verbose "Checking if there is a potential conflict for module installation" + $availableModules = Get-Module $Module -ListAvailable + $IsPotentialConflict = $false + + Write-Information "Modules installed: $availableModules" + + if ($null -eq $availableModules) + { + return $false + } + + # If Admin, check CurrentUser Module folder path and vice versa + if ($script:IsAdmin) + { + $availableModules | ForEach-Object { if (($null -ne $_.Path) -and $_.Path.Contains($HOME)) { $IsPotentialConflict = $true } } + } + else { + $availableModules | ForEach-Object { if (($null -ne $_.Path) -and $_.Path.Contains($script:ProgramFilesPSPath)) { $IsPotentialConflict = $true } } + } + + # If potential conflict found, confirm with user for continuing with module installation if 'force' was not used + if ($IsPotentialConflict) + { + if (($Force.IsPresent) -or ($PSCmdlet.ShouldContinue(` + "The Cmdlets from module $Module are already present on this device. Proceeding with the installation might cause conflicts. Would you like to continue?", "Detected $Module cmdlets"))) + { + return $false + } + else + { + return $true + } + } + + # False if no conflict was found + return $false +} + +# Helper function to invoke install-module +function Invoke-InstallModule +{ + param($module, $version, $scope) + $installCmd = Get-Command Install-Module + if($installCmd.Parameters.ContainsKey('AllowClobber')) + { + if (-not $scope) + { + Install-Module $Module -RequiredVersion $version -AllowClobber -Repository $script:BootStrapRepo + } + else { + Install-Module $Module -RequiredVersion $version -Scope $scope -AllowClobber -Repository $script:BootStrapRepo + } + } + else { + if (-not $scope) + { + Install-Module $Module -RequiredVersion $version -Force -Repository $script:BootStrapRepo + } + else { + Install-Module $Module -RequiredVersion $version -Scope $scope -Force -Repository $script:BootStrapRepo + } + } +} + +# Invoke any script block with a retry logic +function Invoke-CommandWithRetry +{ + [CmdletBinding()] + [OutputType([PSObject])] + Param + ( + [Parameter(Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + Position=0)] + [System.Management.Automation.ScriptBlock] + $ScriptBlock, + + [Parameter(Position=1)] + [ValidateNotNullOrEmpty()] + [int]$MaxRetries=3, + + [Parameter(Position=2)] + [ValidateNotNullOrEmpty()] + [int]$RetryDelay=3 + ) + + Begin + { + $currentRetry = 1 + $Success = $False + } + + Process + { + do { + try + { + $result = . $ScriptBlock + $success = $true + return $result + } + catch + { + $currentRetry = $currentRetry + 1 + if ($currentRetry -gt $MaxRetries) { + $PSCmdlet.ThrowTerminatingError($PSitem) + } + else { + Write-verbose -Message "Waiting $RetryDelay second(s) before attempting again" + Start-Sleep -seconds $RetryDelay + } + } + } while(-not $Success) + } +} + +# Select profile according to scope & create if it doesn't exist +function Select-Profile +{ + param([string]$scope) + if($scope -eq "AllUsers" -and (-not $script:IsAdmin)) + { + Write-Error "Administrator rights are required to use AllUsers scope. Log on to the computer with an account that has Administrator rights, and then try again, or retry the operation by adding `"-Scope CurrentUser`" to your command. You can also try running the Windows PowerShell session with elevated rights (Run as Administrator). " -Category InvalidArgument -ErrorAction Stop + } + + if($scope -eq "AllUsers") + { + $profilePath = $profile.AllUsersAllHosts + } + else { + $profilePath = $profile.CurrentUserAllHosts + } + if (-not (Test-Path $ProfilePath)) + { + new-item -path $ProfilePath -itemtype file -force | Out-Null + } + return $profilePath +} + +# Get the latest version of a module in a profile +function Get-LatestModuleVersion +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")] + param ([array]$versions) + + $versionEnum = $versions.GetEnumerator() + $toss = $versionEnum.MoveNext() + $version = $versionEnum.Current + return $version +} + +# Gets module version to be set in default parameter in $profile +function Get-ModuleVersion +{ + param ([string] $armProfile, [string] $invocationLine) + + if (-not $invocationLine.ToLower().Contains("azure")) + { + return + } + + $ProfileMap = (Get-AzProfile) + $Modules = ($ProfileMap.$armProfile | Get-Member -MemberType NoteProperty).Name + + # Check for AzureRm first + if ($invocationLine.ToLower().Contains($RollUpModule.ToLower()) -and (-not $invocationLine.ToLower().Contains("$($RollUpModule.ToLower())."))) + { + $versions = $ProfileMap.$armProfile.$RollUpModule + $version = Get-LatestModuleVersion -versions $versions + return $version + } + + foreach ($module in $Modules) + { + if ($module -eq $RollUpModule) + { + continue + } + + if ($invocationLine.ToLower().Contains($module.ToLower())) + { + $versions = $ProfileMap.$armProfile.$module + $version = Get-LatestModuleVersion -versions $versions + return $version + } + } +} + +# Create a script block with function to be called to get requiredversions for use with default parameters +function Get-ScriptBlock +{ + param ($ProfilePath) + + $profileContent = @() + + # Write Get-ModuleVersion function to $profile path + $functionScript = @" +function Get-ModVersion +{ + param (`$armProfile, `$invocationLine) + if (-not `$invocationLine.ToLower().Contains("azure")) + { + return + } + try + { + `$BootstrapModule = Get-Module -Name "AzureRM.Bootstrapper" -ListAvailable + if (`$null -ne `$BootstrapModule) + { + Import-Module -Name "AzureRM.Bootstrapper" -RequiredVersion `$BootstrapModule.Version[0] + `$version = Get-ModuleVersion -armProfile `$armProfile -invocationLine `$invocationLine + return `$version + } + } + catch + { + return + } +} `r`n +"@ + + $profileContent += $functionScript + + $defaultScript = @" +`$PSDefaultParameterValues["Import-Module:RequiredVersion"]={ Get-ModVersion -armProfile `$PSDefaultParameterValues["*-AzureRmProfile:Profile"] -invocationLine `$MyInvocation.Line } +########## END AzureRM.Bootstrapper scripts +"@ + $profileContent += $defaultScript + return $profileContent +} + +function Remove-ProfileSetting +{ + [CmdletBinding(SupportsShouldProcess=$true)] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")] + param ([string] $profilePath) + + $RemoveSettingScriptBlock = { + $reqLines = @() + Get-Content -Path $profilePath -ErrorAction Stop | + Foreach-Object { + if($_.contains("BEGIN AzureRM.Bootstrapper scripts") -or $donotread) + { + $donotread = $true; + } + else + { + $reqLines += $_ + } + if ($_.contains("END AzureRM.Bootstrapper scripts")) + { + $donotread = $false + } + } + + if ($PSCmdlet.ShouldProcess($reqLines, "Updating `$profile conents")) + { + Set-Content -path $profilePath -Value $reqLines -ErrorAction Stop + } + } + + Invoke-CommandWithRetry -ScriptBlock $RemoveSettingScriptBlock +} + +# Add Scope parameter to the cmdlet +function Add-ScopeParam +{ + param([System.Management.Automation.RuntimeDefinedParameterDictionary]$params, [string]$set = "__AllParameterSets") + $Keys = @('CurrentUser', 'AllUsers') + $scopeValid = New-Object -Type System.Management.Automation.ValidateSetAttribute($Keys) + $scopeAttribute = New-Object -Type System.Management.Automation.ParameterAttribute + $scopeAttribute.ParameterSetName = + $scopeAttribute.Mandatory = $false + $scopeAttribute.Position = 2 + $scopeCollection = New-object -Type System.Collections.ObjectModel.Collection[System.Attribute] + $scopeCollection.Add($scopeValid) + $scopeCollection.Add($scopeAttribute) + $scopeParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Scope", [string], $scopeCollection) + $params.Add("Scope", $scopeParam) +} + +# Add the profile parameter to the cmdlet +function Add-ProfileParam +{ + param([System.Management.Automation.RuntimeDefinedParameterDictionary]$params, [string]$set = "__AllParameterSets") + $ProfileMap = (Get-AzProfile) + $AllProfiles = ($ProfileMap | Get-Member -MemberType NoteProperty).Name + $profileAttribute = New-Object -Type System.Management.Automation.ParameterAttribute + $profileAttribute.ParameterSetName = $set + $profileAttribute.Mandatory = $true + $profileAttribute.Position = 0 + $profileAttribute.ValueFromPipeline = $true + $validateProfileAttribute = New-Object -Type System.Management.Automation.ValidateSetAttribute($AllProfiles) + $profileCollection = New-object -Type System.Collections.ObjectModel.Collection[System.Attribute] + $profileCollection.Add($profileAttribute) + $profileCollection.Add($validateProfileAttribute) + $profileParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Profile", [string], $profileCollection) + $params.Add("Profile", $profileParam) +} + +function Add-ForceParam +{ + param([System.Management.Automation.RuntimeDefinedParameterDictionary]$params, [string]$set = "__AllParameterSets") + Add-SwitchParam $params "Force" $set +} + +function Add-RemoveParam +{ + param([System.Management.Automation.RuntimeDefinedParameterDictionary]$params, [string]$set = "__AllParameterSets") + $name = "RemovePreviousVersions" + $newAttribute = New-Object -Type System.Management.Automation.ParameterAttribute + $newAttribute.ParameterSetName = $set + $newAttribute.Mandatory = $false + $newCollection = New-object -Type System.Collections.ObjectModel.Collection[System.Attribute] + $newCollection.Add($newAttribute) + $newParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter($name, [switch], $newCollection) + $params.Add($name, [Alias("r")]$newParam) +} + +function Add-SwitchParam +{ + param([System.Management.Automation.RuntimeDefinedParameterDictionary]$params, [string]$name, [string] $set = "__AllParameterSets") + $newAttribute = New-Object -Type System.Management.Automation.ParameterAttribute + $newAttribute.ParameterSetName = $set + $newAttribute.Mandatory = $false + $newCollection = New-object -Type System.Collections.ObjectModel.Collection[System.Attribute] + $newCollection.Add($newAttribute) + $newParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter($name, [switch], $newCollection) + $params.Add($name, $newParam) +} + +function Add-ModuleParam +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")] + param([System.Management.Automation.RuntimeDefinedParameterDictionary]$params, [string]$name, [string] $set = "__AllParameterSets") + $ProfileMap = (Get-AzProfile) + $Profiles = ($ProfileMap | Get-Member -MemberType NoteProperty).Name + if ($Profiles.Count -gt 1) + { + $enum = $Profiles.GetEnumerator() + $toss = $enum.MoveNext() + $Current = $enum.Current + $Keys = ($($ProfileMap.$Current) | Get-Member -MemberType NoteProperty).Name + } + else { + $Keys = ($($ProfileMap.$Profiles[0]) | Get-Member -MemberType NoteProperty).Name + } + $moduleValid = New-Object -Type System.Management.Automation.ValidateSetAttribute($Keys) + $AllowNullAttribute = New-Object -Type System.Management.Automation.AllowNullAttribute + $AllowEmptyStringAttribute = New-Object System.Management.Automation.AllowEmptyStringAttribute + $moduleAttribute = New-Object -Type System.Management.Automation.ParameterAttribute + $moduleAttribute.ParameterSetName = + $moduleAttribute.Mandatory = $false + $moduleAttribute.Position = 1 + $moduleCollection = New-object -Type System.Collections.ObjectModel.Collection[System.Attribute] + $moduleCollection.Add($moduleValid) + $moduleCollection.Add($moduleAttribute) + $moduleCollection.Add($AllowNullAttribute) + $moduleCollection.Add($AllowEmptyStringAttribute) + $moduleParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Module", [array], $moduleCollection) + $params.Add("Module", $moduleParam) +} + +<# +.ExternalHelp help\AzureRM.Bootstrapper-help.xml +#> +function Get-AzureRmModule +{ + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")] + param() + DynamicParam + { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Add-ProfileParam $params + $ProfileMap = (Get-AzProfile) + $Profiles = ($ProfileMap | Get-Member -MemberType NoteProperty).Name + if ($Profiles.Count -gt 1) + { + $enum = $Profiles.GetEnumerator() + $toss = $enum.MoveNext() + $Current = $enum.Current + $Keys = ($($ProfileMap.$Current) | Get-Member -MemberType NoteProperty).Name + } + else { + $Keys = ($($ProfileMap.$Profiles[0]) | Get-Member -MemberType NoteProperty).Name + } + $moduleValid = New-Object -Type System.Management.Automation.ValidateSetAttribute($Keys) + $moduleAttribute = New-Object -Type System.Management.Automation.ParameterAttribute + $moduleAttribute.ParameterSetName = + $moduleAttribute.Mandatory = $true + $moduleAttribute.Position = 1 + $moduleCollection = New-object -Type System.Collections.ObjectModel.Collection[System.Attribute] + $moduleCollection.Add($moduleValid) + $moduleCollection.Add($moduleAttribute) + $moduleParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Module", [string], $moduleCollection) + $params.Add("Module", $moduleParam) + return $params + } + + PROCESS + { + $ProfileMap = (Get-AzProfile) + $Profile = $PSBoundParameters.Profile + $Module = $PSBoundParameters.Module + $versionList = $ProfileMap.$Profile.$Module + Write-Verbose "Getting the version of $module from $profile" + $moduleList = Get-Module -Name $Module -ListAvailable | Where-Object {$null -ne $_.RepositorySourceLocation} + foreach ($version in $versionList) + { + foreach ($module in $moduleList) + { + if ($version -eq $module.Version) + { + return $version + } + } + } + return $null + } +} + +<# +.ExternalHelp help\AzureRM.Bootstrapper-help.xml +#> +function Get-AzureRmProfile +{ + [CmdletBinding(DefaultParameterSetName="ListAvailableParameterSet")] + param() + DynamicParam + { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Add-SwitchParam $params "ListAvailable" "ListAvailableParameterSet" + Add-SwitchParam $params "Update" + return $params + } + PROCESS + { + # ListAvailable helps to display all profiles available from the gallery + [switch]$ListAvailable = $PSBoundParameters.ListAvailable + $PSBoundParameters.Remove('ListAvailable') | Out-Null + $ProfileMap = (Get-AzProfile @PSBoundParameters) + if ($ListAvailable.IsPresent) + { + Write-Verbose "Getting all the profiles available for install" + foreach ($profile in ($ProfileMap | get-member -MemberType NoteProperty).Name) + { + $profileObj = $ProfileMap.$profile + $profileObj | Add-Member -MemberType NoteProperty -Name "ProfileName" -Value $profile + $profileObj | Add-Member -TypeName ProfileMapData + $profileObj + } + return + } + else + { + # Just display profiles installed on the machine + Write-Verbose "Getting profiles installed on the machine and available for import" + $IncompleteProfiles = @() + $profilesInstalled = Get-ProfilesInstalled -ProfileMap $ProfileMap ([REF]$IncompleteProfiles) + foreach ($key in $profilesInstalled.Keys) + { + $profileObj = New-Object -TypeName psobject -property $profilesinstalled.$key + $profileObj.PSObject.TypeNames.Insert(0,'ProfileMapData') + $profileObj | Add-Member -MemberType NoteProperty -Name "ProfileName" -Value $key + $profileObj + } + if ($IncompleteProfiles.Count -gt 0) + { + Write-Warning "Some modules from profile(s) $(@($IncompleteProfiles) -join ', ') were not installed. Use Install-AzureRmProfile to install missing modules." + } + return + } + } +} + +<# +.ExternalHelp help\AzureRM.Bootstrapper-help.xml +#> +function Use-AzureRmProfile +{ + [CmdletBinding(SupportsShouldProcess=$true)] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidShouldContinueWithoutForce", "")] + param() + DynamicParam + { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Add-ProfileParam $params + Add-ForceParam $params + Add-ScopeParam $params + Add-ModuleParam $params + return $params + } + PROCESS + { + $Force = $PSBoundParameters.Force + $ProfileMap = (Get-AzProfile) + $Profile = $PSBoundParameters.Profile + $Scope = $PSBoundParameters.Scope + $Modules = $PSBoundParameters.Module + + # If user hasn't provided modules, use the module names from profile + if ($null -eq $Modules) + { + $Modules = ($ProfileMap.$Profile | Get-Member -MemberType NoteProperty).Name + } + + # If AzureRM $RollUpModule is present in that profile, it will install all the dependent modules; no need to specify other modules + if ($Modules.Contains($RollUpModule)) + { + $Modules = @($RollUpModule) + } + + $PSBoundParameters.Remove('Profile') | Out-Null + $PSBoundParameters.Remove('Scope') | Out-Null + $PSBoundParameters.Remove('Module') | Out-Null + + # Variable to track progress + $ModuleCount = 0 + Write-Output "Loading Profile $Profile" + foreach ($Module in $Modules) + { + $ModuleCount = $ModuleCount + 1 + $version = Get-AzureRmModule -Profile $Profile -Module $Module + if (($null -eq $version) -and $PSCmdlet.ShouldProcess($module, "Installing module for profile $profile in the current scope")) + { + Write-Verbose "$module was not found on the machine. Trying to install..." + if (($Force.IsPresent -or $PSCmdlet.ShouldContinue("Install Module $module for Profile $Profile from the gallery?", "Installing Modules for Profile $Profile"))) + { + if (Find-PotentialConflict -Module $Module @PSBoundParameters) + { + continue + } + $versions = $ProfileMap.$Profile.$Module + $version = Get-LatestModuleVersion -versions $versions + Write-Progress -Activity "Installing Module $Module version: $version" -Status "Progress:" -PercentComplete ($ModuleCount/($Modules.Length)*100) + Write-Verbose "Installing module $module" + Invoke-InstallModule -module $Module -version $version -scope $scope + } + } + + # If a different profile's Azure Module was imported, block user + $importedModules = Get-Module "Azure*" + foreach ($importedModule in $importedModules) + { + $importedVersions = $ProfileMap.$Profile.$($importedModule.Name) + if ($null -ne $importedVersions) + { + # We need the latest version in that profile to be imported. If old version was imported, block user and ask to import in a new session + $importedVersion = Get-LatestModuleVersion -versions $importedVersions + if ([system.version]$importedVersion -ne $importedModule.Version) + { + Write-Error "A different profile version of module $importedModule is imported in this session. Start a new PowerShell session and retry the operation." -Category InvalidOperation + return + } + } + } + + if ($PSCmdlet.ShouldProcess($module, "Importing module for profile $profile in the current scope")) + { + Write-Verbose "Importing module $module" + Import-Module -Name $Module -RequiredVersion $version -Global + } + } + } +} + +<# +.ExternalHelp help\AzureRM.Bootstrapper-help.xml +#> +function Install-AzureRmProfile +{ + [CmdletBinding(SupportsShouldProcess=$true)] + param() + DynamicParam + { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Add-ProfileParam $params + Add-ScopeParam $params + Add-ForceParam $params + return $params + } + + PROCESS { + $ProfileMap = (Get-AzProfile) + $Profile = $PSBoundParameters.Profile + $Scope = $PSBoundParameters.Scope + $Modules = ($ProfileMap.$Profile | Get-Member -MemberType NoteProperty).Name + + # If AzureRM $RollUpModule is present in $profile, it will install all the dependent modules; no need to specify other modules + if ($Modules.Contains($RollUpModule)) + { + $Modules = @($RollUpModule) + } + + $PSBoundParameters.Remove('Profile') | Out-Null + $PSBoundParameters.Remove('Scope') | Out-Null + + $ModuleCount = 0 + foreach ($Module in $Modules) + { + $ModuleCount = $ModuleCount + 1 + if (Find-PotentialConflict -Module $Module @PSBoundParameters) + { + continue + } + + $version = Get-AzureRmModule -Profile $Profile -Module $Module + if ($null -eq $version) + { + $versions = $ProfileMap.$Profile.$Module + $version = Get-LatestModuleVersion -versions $versions + if ($PSCmdlet.ShouldProcess($Module, "Installing Module $Module version: $version")) + { + Write-Progress -Activity "Installing Module $Module version: $version" -Status "Progress:" -PercentComplete ($ModuleCount/($Modules.Length)*100) + Write-Verbose "Installing module $module" + Invoke-InstallModule -module $Module -version $version -scope $scope + } + } + } + } +} + +<# +.ExternalHelp help\AzureRM.Bootstrapper-help.xml +#> +function Uninstall-AzureRmProfile +{ + [CmdletBinding(SupportsShouldProcess = $true)] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidShouldContinueWithoutForce", "")] + param() + DynamicParam + { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Add-ProfileParam $params + Add-ForceParam $params + return $params + } + + PROCESS { + $ProfileMap = (Get-AzProfile) + $Profile = $PSBoundParameters.Profile + $Force = $PSBoundParameters.Force + + if ($PSCmdlet.ShouldProcess("$Profile", "Uninstall Profile")) + { + if (($Force.IsPresent -or $PSCmdlet.ShouldContinue("Uninstall Profile $Profile", "Removing Modules for profile $Profile"))) + { + Write-Verbose "Trying to uninstall profile $profile" + Uninstall-ProfileHelper -PMap $ProfileMap @PSBoundParameters + } + } + } +} + +<# +.ExternalHelp help\AzureRM.Bootstrapper-help.xml +#> +function Update-AzureRmProfile +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")] + [CmdletBinding(SupportsShouldProcess = $true)] + param() + DynamicParam + { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Add-ProfileParam $params + Add-ForceParam $params + Add-RemoveParam $params + Add-ModuleParam $params + Add-ScopeParam $params + return $params + } + + PROCESS { + # Update Profile cache, if not up-to-date + $ProfileMap = (Get-AzProfile -Update) + $profile = $PSBoundParameters.Profile + $Remove = $PSBoundParameters.RemovePreviousVersions + + $PSBoundParameters.Remove('RemovePreviousVersions') | Out-Null + + # Install & import the required version + Use-AzureRmProfile @PSBoundParameters + + $PSBoundParameters.Remove('Force') | Out-Null + $PSBoundParameters.Remove('Scope') | Out-Null + + # Remove previous versions of the profile? + if ($Remove.IsPresent -and $PSCmdlet.ShouldProcess($profile, "Remove previous versions of profile")) + { + # Remove-PreviousVersions and clean up cache + Update-ProfileHelper @PSBoundParameters -RemovePreviousVersions + } + } +} + +function Set-BootstrapRepo +{ + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] + param([string]$Repo) + $script:BootStrapRepo = $Repo +} + +<# +.ExternalHelp help\AzureRM.Bootstrapper-help.xml +#> +function Set-AzureRmDefaultProfile +{ + [CmdletBinding(SupportsShouldProcess = $true)] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidShouldContinueWithoutForce", "")] + param() + DynamicParam + { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Add-ProfileParam $params + Add-ForceParam $params + Add-ScopeParam $params + return $params + } + PROCESS { + $armProfile = $PSBoundParameters.Profile + $Scope = $PSBoundParameters.Scope + $Force = $PSBoundParameters.Force + + $defaultProfile = $Global:PSDefaultParameterValues["*-AzureRmProfile:Profile"] + if ($defaultProfile -ne $armProfile) + { + if ($PSCmdlet.ShouldProcess("$armProfile", "Set Default Profile")) + { + if (($Force.IsPresent -or $PSCmdlet.ShouldContinue("Are you sure you would like to set $armProfile as Default Profile?", "Setting $armProfile as Default profile"))) + { + # Check Profile existence and choose proper profile + $profilePath = Select-Profile -Scope $Scope + + # Set DefaultProfile for this session + $Global:PSDefaultParameterValues["*-AzureRmProfile:Profile"]="$armProfile" + $Global:PSDefaultParameterValues["Import-Module:RequiredVersion"]={ Get-ModuleVersion -armProfile $Global:PSDefaultParameterValues["*-AzureRmProfile:Profile"] -invocationLine $MyInvocation.Line } + + # Edit the profile content + $profileContent = @" +########## BEGIN AzureRM.Bootstrapper scripts +`$PSDefaultParameterValues["*-AzureRmProfile:Profile"]="$armProfile" `r`n +"@ + + # Get Script to be added to the $profile path + $profileContent += Get-ScriptBlock -ProfilePath $profilePath + + Write-Verbose "Updating default profile value to $armProfile" + Write-Debug "Removing previous setting if exists" + Remove-ProfileSetting -profilePath $profilePath + + Write-Debug "Adding new default profile value as $armProfile" + $AddContentScriptBlock = { + Add-Content -Value $profileContent -Path $profilePath -ErrorAction Stop + } + Invoke-CommandWithRetry -ScriptBlock $AddContentScriptBlock + } + } + } + } +} + +<# +.ExternalHelp help\AzureRM.Bootstrapper-help.xml +#> +function Remove-AzureRmDefaultProfile +{ + [CmdletBinding(SupportsShouldProcess = $true)] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidShouldContinueWithoutForce", "")] + param() + DynamicParam + { + $params = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary + Add-ForceParam $params + return $params + } + PROCESS { + $Force = $PSBoundParameters.Force + + if ($PSCmdlet.ShouldProcess("ARM Default Profile", "Remove Default Profile")) + { + if (($Force.IsPresent -or $PSCmdlet.ShouldContinue("Are you sure you would like to remove Default Profile?", "Remove Default profile"))) + { + Write-Verbose "Removing default profile value" + $Global:PSDefaultParameterValues.Remove("*-AzureRmProfile:Profile") + $Global:PSDefaultParameterValues.Remove("Import-Module:RequiredVersion") + + # Remove AzureRm modules except bootstrapper module + $importedModules = Get-Module "Azure*" + foreach ($importedModule in $importedModules) + { + if ($importedModule.Name -eq "AzureRM.Bootstrapper") + { + continue + } + Remove-Module -Name $importedModule -Force -ErrorAction "SilentlyContinue" + } + + # Remove content from $profile + $profiles = @() + if ($script:IsAdmin) + { + $profiles += $profile.AllUsersAllHosts + } + + $profiles += $profile.CurrentUserAllHosts + + foreach ($profilePath in $profiles) + { + if (-not (Test-Path -path $profilePath)) + { + continue + } + + Remove-ProfileSetting -profilePath $profilePath + } + } + } + } +} \ No newline at end of file diff --git a/src/AzureRM.BootStrapper/Module/ProfileMap.json b/src/AzureRM.BootStrapper/Module/ProfileMap.json new file mode 100644 index 00000000..f8573b89 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/ProfileMap.json @@ -0,0 +1,286 @@ +{ + "latest": { + "AzureRM": [ + "6.13.1" + ], + "AzureRM.Profile": [ + "5.8.3" + ], + "Azure.Storage": [ + "4.5.0" + ], + "AzureRM.AnalysisServices": [ + "0.6.14" + ], + "Azure.AnalysisServices": [ + "0.5.4" + ], + "AzureRM.ApiManagement": [ + "6.1.7" + ], + "AzureRM.ApplicationInsights": [ + "0.1.8" + ], + "AzureRM.Automation": [ + "6.1.1" + ], + "AzureRM.Backup": [ + "4.0.11" + ], + "AzureRM.Batch": [ + "4.1.5" + ], + "AzureRM.Billing": [ + "0.14.6" + ], + "AzureRM.Cdn": [ + "5.0.6" + ], + "AzureRM.CognitiveServices": [ + "0.9.12" + ], + "AzureRM.Compute": [ + "5.9.1" + ], + "AzureRM.Consumption": [ + "0.3.7" + ], + "AzureRM.ContainerInstance": [ + "0.2.12" + ], + "AzureRM.ContainerRegistry": [ + "1.0.10" + ], + "AzureRM.DataFactories": [ + "5.0.3" + ], + "AzureRM.DataFactoryV2": [ + "0.5.11" + ], + "AzureRM.DataLakeAnalytics": [ + "5.1.4" + ], + "AzureRM.DataLakeStore": [ + "6.2.1" + ], + "AzureRM.DevTestLabs": [ + "4.0.9" + ], + "AzureRM.Dns": [ + "5.1.0" + ], + "AzureRM.EventGrid": [ + "0.3.7" + ], + "AzureRM.EventHub": [ + "0.7.0" + ], + "AzureRM.HDInsight": [ + "4.1.8" + ], + "AzureRM.Insights": [ + "5.1.5" + ], + "AzureRM.IoTHub": [ + "3.1.8" + ], + "AzureRM.KeyVault": [ + "5.2.1" + ], + "AzureRM.LogicApp": [ + "4.1.4" + ], + "AzureRM.MachineLearning": [ + "0.18.5" + ], + "AzureRM.MachineLearningCompute": [ + "0.4.8" + ], + "AzureRM.MarketplaceOrdering": [ + "0.4.8" + ], + "AzureRM.Media": [ + "0.10.4" + ], + "AzureRM.Network": [ + "6.11.1" + ], + "AzureRM.NotificationHubs": [ + "5.0.3" + ], + "AzureRM.OperationalInsights": [ + "5.0.6" + ], + "AzureRM.PolicyInsights": [ + "1.1.0" + ], + "AzureRM.PowerBIEmbedded": [ + "4.1.10" + ], + "AzureRM.RecoveryServices": [ + "4.1.9" + ], + "AzureRM.RecoveryServices.Backup": [ + "4.5.2" + ], + "AzureRM.RecoveryServices.SiteRecovery": [ + "0.2.12" + ], + "AzureRM.RedisCache": [ + "5.1.0" + ], + "AzureRM.Relay": [ + "0.3.12" + ], + "AzureRM.Resources": [ + "6.7.3" + ], + "AzureRM.Scheduler": [ + "0.16.10" + ], + "AzureRM.ServiceBus": [ + "0.6.13" + ], + "AzureRM.ServiceFabric": [ + "0.3.15" + ], + "AzureRM.Sql": [ + "4.12.1" + ], + "AzureRM.Storage": [ + "5.2.0" + ], + "AzureRM.StreamAnalytics": [ + "4.0.10" + ], + "AzureRM.Tags": [ + "4.0.5" + ], + "AzureRM.TrafficManager": [ + "4.1.3" + ], + "AzureRM.UsageAggregates": [ + "4.0.5" + ], + "AzureRM.Websites": [ + "5.2.0" + ] + }, + "2017-03-09-profile": { + "AzureRM": [ + "1.2.11" + ], + "AzureRM.Profile": [ + "3.4.1" + ], + "Azure.Storage": [ + "1.0.5.4" + ], + "AzureRM.Compute": [ + "1.2.3.4" + ], + "AzureRM.Dns": [ + "3.4.1" + ], + "AzureRM.KeyVault": [ + "3.4.1" + ], + "AzureRM.Network": [ + "1.0.5.4" + ], + "AzureRM.Resources": [ + "4.4.1" + ], + "AzureRM.Storage": [ + "1.0.5.4" + ], + "AzureRM.Tags": [ + "3.4.1" + ], + "AzureRM.UsageAggregates": [ + "3.4.1" + ] + }, + "2018-03-01-hybrid": { + "AzureRM": [ + "2.3.0" + ], + "AzureRM.Profile": [ + "5.5.2" + ], + "Azure.Storage": [ + "4.1.1" + ], + "AzureRM.Compute": [ + "4.0.2" + ], + "AzureRM.Dns": [ + "3.5.1" + ], + "AzureRM.Insights": [ + "5.0.0" + ], + "AzureRM.KeyVault": [ + "4.2.0" + ], + "AzureRM.Network": [ + "5.0.1" + ], + "AzureRM.Resources": [ + "6.0.2" + ], + "AzureRM.Storage": [ + "1.1.0.1" + ], + "AzureRM.Tags": [ + "4.0.2" + ], + "AzureRM.UsageAggregates": [ + "4.0.3" + ], + "AzureRM.Websites": [ + "5.0.1" + ] + }, + "2019-03-01-hybrid": { + "AzureRM": [ + "2.5.0" + ], + "AzureRM.Profile": [ + "5.8.3" + ], + "Azure.Storage": [ + "4.5.0" + ], + "AzureRM.Compute": [ + "4.6.1" + ], + "AzureRM.Dns": [ + "3.5.1" + ], + "AzureRM.Insights": [ + "5.1.5" + ], + "AzureRM.KeyVault": [ + "4.2.0" + ], + "AzureRM.Network": [ + "5.0.1" + ], + "AzureRM.Resources": [ + "6.4.3" + ], + "AzureRM.Storage": [ + "5.0.4" + ], + "AzureRM.Tags": [ + "4.0.2" + ], + "AzureRM.UsageAggregates": [ + "4.0.3" + ], + "AzureRM.Websites": [ + "5.0.1" + ] + } +} \ No newline at end of file diff --git a/src/AzureRM.BootStrapper/Module/Run-ScenarioTests.ps1 b/src/AzureRM.BootStrapper/Module/Run-ScenarioTests.ps1 new file mode 100644 index 00000000..d5132dd6 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/Run-ScenarioTests.ps1 @@ -0,0 +1,6 @@ +#Requires -Modules AzureRM.Bootstrapper, Pester + +$defaults = [System.IO.Path]::GetDirectoryName($PSCommandPath) +Set-Location $defaults + +. .\AzureRM.Bootstrapper.ScenarioTests.ps1 \ No newline at end of file diff --git a/src/AzureRM.BootStrapper/Module/Run-UnitTests.ps1 b/src/AzureRM.BootStrapper/Module/Run-UnitTests.ps1 new file mode 100644 index 00000000..9d14d774 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/Run-UnitTests.ps1 @@ -0,0 +1,6 @@ +#Requires -Modules AzureRM.Bootstrapper, Pester + +$defaults = [System.IO.Path]::GetDirectoryName($PSCommandPath) +Set-Location $defaults + +Invoke-Pester -EnableExit diff --git a/src/AzureRM.BootStrapper/Module/about_version_profiles.help.txt b/src/AzureRM.BootStrapper/Module/about_version_profiles.help.txt new file mode 100644 index 00000000..0ff547fe --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/about_version_profiles.help.txt @@ -0,0 +1,122 @@ +TOPIC + about_version_profiles + +SHORT DESCRIPTION + Version profiles provide a mechanism for managing powershell cmdlets that + target specific versions of azure services supported in different instances + of Azure. + +LONG DESCRIPTION + Different concrete instances of Azure (AzureCloud, AzureChinaCloud, + AzureGermanCloud, AzureUSGovernmentCloud, AzureStack) may have different + versions of Azure services installed, with different capabilities. Azure + Version Profiles provide a mechanism for managing these version differences. + + Each Azure instance has a discoverable set of supported version profiles. + + A user can select a version profile supported by the instances of Azure they + target, and this version profile corresponds to versions of the Azure + PowerShell modules. Users can then select these Azure PowerShell module + versions and be confident that their scripts will work when targeting those + Azure instances. + The AzureRM.Bootstrapper module provides cmdlets to discover, acquire, and + use modules that are appropriate for the azure version profile you are targeting. + You can also use Tags in the AzureRM modules to discover profile information + for each module version. + + Tags for a Profile use the form VersionProfile:2019-03-01-hybrid + The AzureRM bootstrapper module uses the PowerShell Gallery to install and + load needed modules when you want to target a specific version profile. + +EXAMPLES +Finding appropriate version profiles + Use the Get-AzureRmProfile cmdlet to discover available profile versions, + and profile versions supported by an Azure instance. + + Get-AzureRmProfile -ListAvailable + + lists all available version profiles. + + Use-AzureRmProfile -Profile 2019-03-01-hybrid + + Installs and loads cmdlets for one of the listed profiles. + +Targeting all Azure Instances + Get-AzureRmProfile + + Lists the profiles that are currently installed on the machine. + + Use-AzureRmProfile -Profile 2019-03-01-hybrid + + Installs and loads cmdlets compatible with one of the listed profiles. + +Targeting the Latest Stable Features + Use-AzureRmProfile -Profile Latest + + Installs and loads the latest published cmdlets for Azure PowerShell. + +Acquiring and Loading All Azure modules using the BootStrapper + Use-AzureRmProfile -Profile '2019-03-01-hybrid' -Force + + Checks if modules compatible with the '2019-03-01-hybrid' profile are + installed in the current scope, downloads and installs the modules if + necessary, and then loads the modules in the current session. You must + open a new PowerShell session to target a different version profile. Using + the 'Force' parameter installs the necessary modules without prompting. + +Acquiring and Loading Selected Azure modules using the Bootstrapper + Use-AzureRmProfile -Profile '2019-03-01-hybrid' -Module AzureRM.Compute + + Checks if an AzureRM.Compute module compatible with the + '2019-03-01-hybrid' profile is installed in the current scope, downloads + and installs the module if necessary, and then loads the module in the + current session. You must open a new PowerShell session to target a + different module. + +Switching Between Version Profiles + To switch between version profiles on a machine, in a new PowerShell window, + execute the following cmdlet: + + Use-AzureRmProfile -Profile '2019-03-01-hybrid' + + This loads the modules associated with the '2019-03-01-hybrid' profile in + the current session. You must open a new PowerShell session to target a + different version profile. + + +Updating and Removing Profiles + To update a profile to the latest versions in that profile and import + updated modules to the current session, execute the following cmdlet: + + Update-AzureRmProfile -Profile 'latest' + + This checks if the latest versions of Azure PowerShell modules are + installed, if not prompts the user if they should be installed and imports + them into the current session. This should always be executed in a new + PowerShell session. + If you would like to update to the latest modules in a Profile and remove + previously installed versions of the modules, use: + + Update-AzureRmProfile -Profile 'latest' -RemovePreviousVersions + +Setting and Removing Default Profiles + To set or update a profile as a default to be used with all Azure PowerShell + modules, execute the following cmdlet: + + Set-AzureRmDefaultProfile -Profile '2019-03-01-hybrid' + + The default profile selection is persisted across shells and sessions. + After default profile is set using the above cmdlet, the 'Import-Module' + when used with AzureRm modules will automatically load Azure PowerShell + modules compatible with the given profile. You may also use API version + profile cmdlets without the '-profile' parameter. + + Import-Module AzureRM.Compute + Use-AzureRmProfile + Uninstall-AzureRmProfile + + To remove a default profile from all sessions and shells, execute the + following cmdlet: + + Remove-AzureRmDefaultProfile + diff --git a/src/AzureRM.BootStrapper/Module/help/AzureRM.BootStrapper.md b/src/AzureRM.BootStrapper/Module/help/AzureRM.BootStrapper.md new file mode 100644 index 00000000..bb87691a --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/AzureRM.BootStrapper.md @@ -0,0 +1,43 @@ +--- +Module Name: AzureRM.BootStrapper +Module Guid: 30d8a5cf-3ee5-49ce-b9b0-a4d000d65161 +Download Help Link: {{Please enter FwLink manually}} +Help Version: {{Please enter version of help manually (X.X.X.X) format}} +Locale: en-US +--- + +# AzureRM.BootStrapper Module +## Description +{{Manually Enter Description Here}} + +## AzureRM.BootStrapper Cmdlets +### [Get-AzureRmModule](Get-AzureRmModule.md) +{{Manually Enter Get-AzureRmModule Description Here}} + +### [Get-AzureRmProfile](Get-AzureRmProfile.md) +{{Manually Enter Get-AzureRmProfile Description Here}} + +### [Get-ModuleVersion](Get-ModuleVersion.md) +{{Manually Enter Get-ModuleVersion Description Here}} + +### [Install-AzureRmProfile](Install-AzureRmProfile.md) +{{Manually Enter Install-AzureRmProfile Description Here}} + +### [Remove-AzureRmDefaultProfile](Remove-AzureRmDefaultProfile.md) +{{Manually Enter Remove-AzureRmDefaultProfile Description Here}} + +### [Set-AzureRmDefaultProfile](Set-AzureRmDefaultProfile.md) +{{Manually Enter Set-AzureRmDefaultProfile Description Here}} + +### [Set-BootstrapRepo](Set-BootstrapRepo.md) +{{Manually Enter Set-BootstrapRepo Description Here}} + +### [Uninstall-AzureRmProfile](Uninstall-AzureRmProfile.md) +{{Manually Enter Uninstall-AzureRmProfile Description Here}} + +### [Update-AzureRmProfile](Update-AzureRmProfile.md) +{{Manually Enter Update-AzureRmProfile Description Here}} + +### [Use-AzureRmProfile](Use-AzureRmProfile.md) +{{Manually Enter Use-AzureRmProfile Description Here}} + diff --git a/src/AzureRM.BootStrapper/Module/help/AzureRM.Bootstrapper-help.xml b/src/AzureRM.BootStrapper/Module/help/AzureRM.Bootstrapper-help.xml new file mode 100644 index 00000000..65062b90 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/AzureRM.Bootstrapper-help.xml @@ -0,0 +1,996 @@ + + + + +Get-AzureRmModule +Get +AzureRmModule +Returns the versions of an AzureRM module that support a given profile. + + + +Returns the versions of an AzureRM module that support a given profile. + + +Get-AzureRmModule +Profile +The profile version to check for the given module. + + +2017-03-09-profile +<others> + +String +String + +None + +Module +The AzureRM module to retrieve the version for. + + +String +String + +None + + + +Module +The AzureRM module to retrieve the version for. + + +String +String + +None + +Profile +The profile version to check for the given module. + + +String +String + +None + + +None + + + + + + +System.String + + + + + + + + + + +Example 1 +PS C:\> Get-AzureRmModule -Profile 2017-03-09-profile -Module AzureRM.Storage + +1.0.4.4 +The version of the AzureRM.Storage module that supports profile 2017-03-09-profile is version 1.0.4.3. + + + + + + + +Get-AzureRmProfile +Get +AzureRmProfile +List the supported AzureRM profiles. + + + +Lists the supported AzureRM profiles. If no parameters are given, returns the profile version supported by modules on the current machine. If -ListAvailable is specified, lists all profiles that could be installed on the machine. + + +Get-AzureRmProfile +ListAvailable +If specified, list all available profiles, not just the profiles currently installed. + + +SwitchParameter + +False + +Update +If specified, updates Profiles available by querying Azure Endpoint + + +SwitchParameter + +False + + + +ListAvailable +If specified, list all available profiles, not just the profiles currently installed. + + +SwitchParameter +SwitchParameter + +False + +Update +If specified, updates Profiles available by querying Azure Endpoint + + +SwitchParameter +SwitchParameter + +False + + +None + + + + + + +System.String + + + + + + + + + + +Example 2 +PS C:\> Get-AzureRmProfile -ListAvailable + +2015-06 +2015-09 +List all ARM profiles available to be installed. + + + + + + + +Install-AzureRmProfile +Install +AzureRmProfile +Install all the latest modules associated with a particular AzureRM Profile on the machine. + + + +Install all the latest modules associated with a particular AzureRM Profile on the machine. Modules for a particular profile can be loaded in a new PowerShell session using 'Use-AzureRmProfile'. + + +Install-AzureRmProfile +Profile +The profile version to install. You can get a list of available profile versions using Get-AzureRmProfile -ListAvailable + + +2017-03-09-profile +<others> + +String +String + +None + +Force +Automatically install modules for the given profile if they are not already installed. + + +SwitchParameter + +False + +Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. The CurrentUser scope lets modules be installed in a location that is available only to the current user. + + +CurrentUser +AllUsers + +String +String + +None + +Confirm +Prompts you for confirmation before running the cmdlet. + + +SwitchParameter + +False + +WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + + +SwitchParameter + +False + + + +Force +Automatically install modules for the given profile if they are not already installed. + + +SwitchParameter +SwitchParameter + +False + +Profile +The profile version to install. You can get a list of available profile versions using Get-AzureRmProfile -ListAvailable + + +String +String + +None + +Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. The CurrentUser scope lets modules be installed in a location that is available only to the current user. + + +String +String + +None + +Confirm +Prompts you for confirmation before running the cmdlet. + + +SwitchParameter +SwitchParameter + +False + +WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + + +SwitchParameter +SwitchParameter + +False + + +None + + + + + + +None + + + + + + + + + + +Example 1 +PS C:\> Install-AzureRmProfile -Profile '2017-03-09-profile' +Install all the modules associated with profile '2017-03-09-profile' + + + + + + + +Remove-AzureRmDefaultProfile +Remove +AzureRmDefaultProfile +Removes the default profile setting. + + + +Removes the default profile setting that was set using 'Set-AzureRmDefaultProfile' cmdlet. + + +Remove-AzureRmDefaultProfile +Force +Removes the default profile setting without prompting for confirmation. + + +SwitchParameter + +False + +Confirm +Prompts you for confirmation before running the cmdlet. + + +SwitchParameter + +False + +WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + + +SwitchParameter + +False + + + +Force +Removes the default profile setting without prompting for confirmation. + + +SwitchParameter +SwitchParameter + +False + +Confirm +Prompts you for confirmation before running the cmdlet. + + +SwitchParameter +SwitchParameter + +False + +WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + + +SwitchParameter +SwitchParameter + +False + + +None + + + + + + +None + + + + + + + + + + +Example 1 +PS C:\> Remove-AzureRmDefaultProfile + + + + + + + + +Set-AzureRmDefaultProfile +Set +AzureRmDefaultProfile +Sets the given profile as a default profile to be used with all API version profile cmdlets. + + + +Sets the given profile as a default profile to be used with all API version profile cmdlets. Default profile selection is persisted across sessions and shells. + + +Set-AzureRmDefaultProfile +Profile +The profile version to set as default. You can get a list of available profile versions using Get-AzureRmProfile -ListAvailable + + +2017-03-09-profile +latest +<others> + +String +String + +None + +Force +Set the given profile as default without prompting for confirmation. + + +SwitchParameter + +False + +Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. The CurrentUser scope lets modules be installed in a location that is available only to the current user. + + +CurrentUser +AllUsers + +String +String + +None + +Confirm +Prompts you for confirmation before running the cmdlet. + + +SwitchParameter + +False + +WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + + +SwitchParameter + +False + + + +Force +Set the given profile as default without prompting for confirmation. + + +SwitchParameter +SwitchParameter + +False + +Profile +The profile version to set as default. You can get a list of available profile versions using Get-AzureRmProfile -ListAvailable + + +String +String + +None + +Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. The CurrentUser scope lets modules be installed in a location that is available only to the current user. + + +String +String + +None + +Confirm +Prompts you for confirmation before running the cmdlet. + + +SwitchParameter +SwitchParameter + +False + +WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + + +SwitchParameter +SwitchParameter + +False + + +None + + + + + + +None + + + + + + + + + + +Example 1 - Using Default Version Profile to Automatically Load Module Versions +PS C:\> Set-AzureRmDefaultProfile -Profile '2017-03-09-profile' +PS C:\> Import-Module AzureRM.Compute +Sets profile '2017-03-09-profile' as the default profile. When importing AzureRM modules like AzureRM.Compute, you will automatically import a version of the module compatible with the default profile setting, unless you explicitly specify a RequiredVersion. + + + +Example 2 - Using Default Version Profile to Set Default Profile for BootStrapper cmdlets +PS C:\> Set-AzureRmDefaultProfile -Profile '2017-03-09-profile' +PS c:\> Install-AzureRmProfile +Sets the default profile as '2017-03-09-profile'. After this, BootStrapper cmdlets will automatically use the default profile if no profile is set. In this case, 'Install-AzureRmProfile' will install profile '2017-03-09-profile', since this profile was set as the default. + + + + + + + +Uninstall-AzureRmProfile +Uninstall +AzureRmProfile +Uninstall all modules associated with the given profile version. + + + +Uninstall all modules associated with the given profile version. Note that this may uninstall modules associated with multiple profiles. + + +Uninstall-AzureRmProfile +Profile +The profile version to uninstall. + + +2016-09 +2017-03-09-profile +<others> + +String +String + +None + +Force +Automatically remove all given modules without propmpting. + + +SwitchParameter + +False + +Confirm +Request confirmation for any change made by the cmdlet + + +SwitchParameter + +False + +WhatIf +Print the changes that would be made in executing the cmdlets, but do not make any changes. + + +SwitchParameter + +False + + + +Force +Automatically remove all given modules without propmpting. + + +SwitchParameter +SwitchParameter + +False + +Profile +The profile version to uninstall. + + +String +String + +None + +Confirm +Request confirmation for any change made by the cmdlet + + +SwitchParameter +SwitchParameter + +False + +WhatIf +Print the changes that would be made in executing the cmdlets, but do not make any changes. + + +SwitchParameter +SwitchParameter + +False + + +None + + + + + + +None + + + + + + + + + + +Example 1 +PS C:\> Uninstall-AzureRmProfile '2017-03-09-profile' +Uninstall all modules associated with the '2017-03-09-profile' profile on the machine + + + + + + + +Update-AzureRmProfile +Update +AzureRmProfile +Update modules to the latest versions consitent with the given profile and import updated modules to the current session. This should always be executed in a new PowerShell session. + + + +Update modules to the latest versions consitent with the given profile and import updated modules to the current session. This should always be executed in a new PowerShell session. + + +Update-AzureRmProfile +Profile +The profile version to load in the current PowerShell session. + + +2017-03-09-profile +Latest +<others> + +String +String + +None + +Module +The module name to be updated. + + +Array +Array + +None + +Force +Automatically install modules for the given profile if they are not already installed. + + +SwitchParameter + +False + +RemovePreviousVersions +Automatically remove old versions of the modules currently installed. + + +SwitchParameter + +False + +Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. The CurrentUser scope lets modules be installed in a location that is available only to the current user. + + +CurrentUser +AllUsers + +String +String + +None + +Confirm +Request confrimation for any change made by the cmdlet + + +SwitchParameter + +False + +WhatIf +Print the changes that would be made in executing the cmdlets, but do not make any changes. + + +SwitchParameter + +False + + + +Force +Automatically install modules for the given profile if they are not already installed. + + +SwitchParameter +SwitchParameter + +False + +Module +The module name to be updated. + + +Array +Array + +None + +Profile +The profile version to load in the current PowerShell session. + + +String +String + +None + +RemovePreviousVersions +Automatically remove old versions of the modules currently installed. + + +SwitchParameter +SwitchParameter + +False + +Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. The CurrentUser scope lets modules be installed in a location that is available only to the current user. + + +String +String + +None + +Confirm +Request confrimation for any change made by the cmdlet + + +SwitchParameter +SwitchParameter + +False + +WhatIf +Print the changes that would be made in executing the cmdlets, but do not make any changes. + + +SwitchParameter +SwitchParameter + +False + + +None + + + + + + +None + + + + + + + + + + +Example 1 +PS C:\> Update-AzureRmProfile -Profile '2017-03-09-profile' +Update the modules associated with profile '2017-03-09-profile' to their latest versions and load in the current session. This should be executed after opening a new PowerShell session. + + + +Example 2 +PS C:\> Update-AzureRmProfile -Profile 'Latest' -RemovePreviousVersions -Force +Update the modules associated with profile version 'Latest' and load the modules in the current session. It downloads and installs the required modules and removes old versions of the modules without prompting the user. This should be executed after opening a new PowerShell session. + + + +Example 3 +PS C:\> Update-AzureRmProfile -Profile 'Latest' -Module 'AzureRM', 'Azure.Storage' -Scope 'CurrentUser' +Update the modules 'AzureRM', 'Azure.Storage' with profile version 'Latest' and load the modules in the current session. It downloads and installs the required modules in the CurrentUser scope. This should be executed after opening a new PowerShell session. + + + + + + + +Use-AzureRmProfile +Use +AzureRmProfile +Load the modules associated with a particular profile in the current PowerShell session. This should always be executed in a new PowerShell session. + + + +Load the modules associated with a particular profile in the current PowerShell session. This should always be executed in a new PowerShell session. + + +Use-AzureRmProfile +Profile +The profile version to load in the current PowerShell session. + + +2017-03-09-profile +2017-03-09-profile +<others> + +String +String + +None + +Module +The module name to be used. + + +Array +Array + +None + +Force +Automatically install modules for the given profile if they are not already installed. + + +SwitchParameter + +False + +Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. The CurrentUser scope lets modules be installed in a location that is available only to the current user. + + +CurrentUser +AllUsers + +String +String + +None + +Confirm +Request confrimation for any change made by the cmdlet + + +SwitchParameter + +False + +WhatIf +Print the changes that would be made in executing the cmdlets, but do not make any changes. + + +SwitchParameter + +False + + + +Force +Automatically install modules for the given profile if they are not already installed. + + +SwitchParameter +SwitchParameter + +False + +Module +The module name to be used. + + +Array +Array + +None + +Profile +The profile version to load in the current PowerShell session. + + +String +String + +None + +Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. The CurrentUser scope lets modules be installed in a location that is available only to the current user. + + +String +String + +None + +Confirm +Request confrimation for any change made by the cmdlet + + +SwitchParameter +SwitchParameter + +False + +WhatIf +Print the changes that would be made in executing the cmdlets, but do not make any changes. + + +SwitchParameter +SwitchParameter + +False + + +None + + + + + + +None + + + + + + + + + + +Example 1 +PS C:\> Use-AzureRmProfile -Profile '2017-03-09-profile' +Load the modules associated with profile version '2017-03-09-profile' in the current session. This should be executed after opening a new PowerShell session. + + + +Example 2 +PS C:\> Use-AzureRmProfile -Profile 'Latest' -Module 'AzureRM' -Scope 'CurrentUser' -Force +Load the module 'AzureRM' associated with profile version 'Latest' in the current session. It downloads and installs from online gallery in the 'CurrentUser' scope if not already installed. This should be executed after opening a new PowerShell session. + + + + + + + diff --git a/src/AzureRM.BootStrapper/Module/help/Get-AzureRmModule.md b/src/AzureRM.BootStrapper/Module/help/Get-AzureRmModule.md new file mode 100644 index 00000000..35edc239 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/Get-AzureRmModule.md @@ -0,0 +1,79 @@ +--- +external help file: AzureRM.Bootstrapper-help.xml +online version: +schema: 2.0.0 +--- + +# Get-AzureRmModule + +## SYNOPSIS +Returns the versions of an AzureRM module that support a given profile. + +## SYNTAX + +``` +Get-AzureRmModule [-Profile] [-Module] [] +``` + +## DESCRIPTION +Returns the versions of an AzureRM module that support a given profile. + +## EXAMPLES + +### Example 1 +``` +PS C:\> Get-AzureRmModule -Profile 2017-03-09-profile -Module AzureRM.Storage + +1.0.4.4 +``` + +The version of the AzureRM.Storage module that supports profile 2017-03-09-profile is version 1.0.4.3. + +## PARAMETERS + +### -Module +The AzureRM module to retrieve the version for. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Profile +The profile version to check for the given module. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: 2017-03-09-profile, + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.String + +## NOTES + +## RELATED LINKS + diff --git a/src/AzureRM.BootStrapper/Module/help/Get-AzureRmProfile.md b/src/AzureRM.BootStrapper/Module/help/Get-AzureRmProfile.md new file mode 100644 index 00000000..93fde680 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/Get-AzureRmProfile.md @@ -0,0 +1,79 @@ +--- +external help file: AzureRM.Bootstrapper-help.xml +online version: +schema: 2.0.0 +--- + +# Get-AzureRmProfile + +## SYNOPSIS +List the supported AzureRM profiles. + +## SYNTAX + +``` +Get-AzureRmProfile [-ListAvailable] [-Update] [] +``` + +## DESCRIPTION +Lists the supported AzureRM profiles. If no parameters are given, returns the profile version supported by modules on the current machine. If *-ListAvailable* is specified, lists all profiles that could be installed on the machine. + +## EXAMPLES + +### Example 2 +``` +PS C:\> Get-AzureRmProfile -ListAvailable + +2015-06 +2015-09 +``` + +List all ARM profiles available to be installed. + +## PARAMETERS + +### -ListAvailable +If specified, list all available profiles, not just the profiles currently installed. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Update +If specified, updates Profiles available by querying Azure Endpoint + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.String + +## NOTES + +## RELATED LINKS + diff --git a/src/AzureRM.BootStrapper/Module/help/Install-AzureRmProfile.md b/src/AzureRM.BootStrapper/Module/help/Install-AzureRmProfile.md new file mode 100644 index 00000000..6c3907dd --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/Install-AzureRmProfile.md @@ -0,0 +1,125 @@ +--- +external help file: AzureRM.Bootstrapper-help.xml +online version: +schema: 2.0.0 +--- + +# Install-AzureRmProfile + +## SYNOPSIS +Install all the latest modules associated with a particular AzureRM Profile on the machine. + +## SYNTAX + +``` +Install-AzureRmProfile [-WhatIf] [-Confirm] [-Profile] [-Scope ] [-Force] [] +``` + +## DESCRIPTION +Install all the latest modules associated with a particular AzureRM Profile on the machine. Modules for a particular profile can be loaded in a new PowerShell session using 'Use-AzureRmProfile'. + +## EXAMPLES + +### Example 1 +``` +PS C:\> Install-AzureRmProfile -Profile '2017-03-09-profile' +``` + +Install all the modules associated with profile '2017-03-09-profile' + +## PARAMETERS + +### -Force +Automatically install modules for the given profile if they are not already installed. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Profile +The profile version to install. You can get a list of available profile versions using *Get-AzureRmProfile -ListAvailable* + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: 2017-03-09-profile, + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. +The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. +The CurrentUser scope lets modules be installed in a location that is available only to the current user. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: CurrentUser, AllUsers + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### None + +## NOTES + +## RELATED LINKS + diff --git a/src/AzureRM.BootStrapper/Module/help/Remove-AzureRmDefaultProfile.md b/src/AzureRM.BootStrapper/Module/help/Remove-AzureRmDefaultProfile.md new file mode 100644 index 00000000..dc369fd7 --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/Remove-AzureRmDefaultProfile.md @@ -0,0 +1,89 @@ +--- +external help file: AzureRM.Bootstrapper-help.xml +online version: +schema: 2.0.0 +--- + +# Remove-AzureRmDefaultProfile + +## SYNOPSIS +Removes the default profile setting. + +## SYNTAX + +``` +Remove-AzureRmDefaultProfile [-WhatIf] [-Confirm] [-Force] [] +``` + +## DESCRIPTION +Removes the default profile setting that was set using 'Set-AzureRmDefaultProfile' cmdlet. + +## EXAMPLES + +### Example 1 +``` +PS C:\> Remove-AzureRmDefaultProfile +``` + +## PARAMETERS + +### -Force +Removes the default profile setting without prompting for confirmation. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### None + +## NOTES + +## RELATED LINKS + diff --git a/src/AzureRM.BootStrapper/Module/help/Set-AzureRmDefaultProfile.md b/src/AzureRM.BootStrapper/Module/help/Set-AzureRmDefaultProfile.md new file mode 100644 index 00000000..2bdf1e2b --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/Set-AzureRmDefaultProfile.md @@ -0,0 +1,138 @@ +--- +external help file: AzureRM.Bootstrapper-help.xml +online version: +schema: 2.0.0 +--- + +# Set-AzureRmDefaultProfile + +## SYNOPSIS +Sets the given profile as a default profile to be used with all API version profile cmdlets. + +## SYNTAX + +``` +Set-AzureRmDefaultProfile [-WhatIf] [-Confirm] [-Profile] [-Force] [-Scope ] + [] +``` + +## DESCRIPTION +Sets the given profile as a default profile to be used with all API version profile cmdlets. Default profile selection is persisted across sessions and shells. + +## EXAMPLES + +### Example 1 - Using Default Version Profile to Automatically Load Module Versions +``` +PS C:\> Set-AzureRmDefaultProfile -Profile '2017-03-09-profile' +PS C:\> Import-Module AzureRM.Compute +``` + +Sets profile '2017-03-09-profile' as the default profile. +When importing AzureRM modules like AzureRM.Compute, you will automatically import a version of the module compatible with the default profile setting, +unless you explicitly specify a RequiredVersion. + +### Example 2 - Using Default Version Profile to Set Default Profile for BootStrapper cmdlets +``` +PS C:\> Set-AzureRmDefaultProfile -Profile '2017-03-09-profile' +PS c:\> Install-AzureRmProfile +``` + +Sets the default profile as '2017-03-09-profile'. After this, BootStrapper cmdlets will automatically use the default profile if no profile is set. +In this case, 'Install-AzureRmProfile' will install profile '2017-03-09-profile', since this profile was set as the default. + +## PARAMETERS + +### -Force +Set the given profile as default without prompting for confirmation. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Profile +The profile version to set as default. You can get a list of available profile versions using *Get-AzureRmProfile -ListAvailable* + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: 2017-03-09-profile, latest, + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. +The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. +The CurrentUser scope lets modules be installed in a location that is available only to the current user. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: CurrentUser, AllUsers + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### None + +## NOTES + +## RELATED LINKS + diff --git a/src/AzureRM.BootStrapper/Module/help/Uninstall-AzureRmProfile.md b/src/AzureRM.BootStrapper/Module/help/Uninstall-AzureRmProfile.md new file mode 100644 index 00000000..a0db122d --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/Uninstall-AzureRmProfile.md @@ -0,0 +1,107 @@ +--- +external help file: AzureRM.Bootstrapper-help.xml +online version: +schema: 2.0.0 +--- + +# Uninstall-AzureRmProfile + +## SYNOPSIS +Uninstall all modules associated with the given profile version. + +## SYNTAX + +``` +Uninstall-AzureRmProfile [-WhatIf] [-Confirm] [-Profile] [-Force] [] +``` + +## DESCRIPTION +Uninstall all modules associated with the given profile version. Note that this may uninstall modules associated with multiple profiles. + +## EXAMPLES + +### Example 1 +``` +PS C:\> Uninstall-AzureRmProfile '2017-03-09-profile' +``` + +Uninstall all modules associated with the '2017-03-09-profile' profile on the machine + +## PARAMETERS + +### -Force +Automatically remove all given modules without propmpting. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Profile +The profile version to uninstall. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: 2016-09, 2017-03-09-profile, + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Confirm +Request confirmation for any change made by the cmdlet + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Print the changes that would be made in executing the cmdlets, but do not make any changes. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### None + +## NOTES + +## RELATED LINKS + diff --git a/src/AzureRM.BootStrapper/Module/help/Update-AzureRmProfile.md b/src/AzureRM.BootStrapper/Module/help/Update-AzureRmProfile.md new file mode 100644 index 00000000..09316aae --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/Update-AzureRmProfile.md @@ -0,0 +1,170 @@ +--- +external help file: AzureRM.Bootstrapper-help.xml +online version: +schema: 2.0.0 +--- + +# Update-AzureRmProfile + +## SYNOPSIS +Update modules to the latest versions consitent with the given profile and import updated modules to the current session. This should always be executed in a new PowerShell session. + +## SYNTAX + +``` +Update-AzureRmProfile [-WhatIf] [-Confirm] [-Profile] [-Force] [-RemovePreviousVersions] + [[-Module] ] [-Scope ] [] +``` + +## DESCRIPTION +Update modules to the latest versions consitent with the given profile and import updated modules to the current session. This should always be executed in a new PowerShell session. + +## EXAMPLES + +### Example 1 +``` +PS C:\> Update-AzureRmProfile -Profile '2017-03-09-profile' +``` + +Update the modules associated with profile '2017-03-09-profile' to their latest versions and load in the current session. This should be executed after opening a new PowerShell session. + +### Example 2 +``` +PS C:\> Update-AzureRmProfile -Profile 'Latest' -RemovePreviousVersions -Force +``` + +Update the modules associated with profile version 'Latest' and load the modules in the current session. It downloads and installs the required modules and removes old versions of the modules without prompting the user. This should be executed after opening a new PowerShell session. + +### Example 3 +``` +PS C:\> Update-AzureRmProfile -Profile 'Latest' -Module 'AzureRM', 'Azure.Storage' -Scope 'CurrentUser' +``` + +Update the modules 'AzureRM', 'Azure.Storage' with profile version 'Latest' and load the modules in the current session. It downloads and installs the required modules in the CurrentUser scope. This should be executed after opening a new PowerShell session. + +## PARAMETERS + +### -Force +Automatically install modules for the given profile if they are not already installed. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Module +The module name to be updated. + +```yaml +Type: Array +Parameter Sets: (All) +Aliases: + +Required: False +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Profile +The profile version to load in the current PowerShell session. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: 2017-03-09-profile, Latest, + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -RemovePreviousVersions +Automatically remove old versions of the modules currently installed. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. +The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. +The CurrentUser scope lets modules be installed in a location that is available only to the current user. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: CurrentUser, AllUsers + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Request confrimation for any change made by the cmdlet + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Print the changes that would be made in executing the cmdlets, but do not make any changes. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### None + +## NOTES + +## RELATED LINKS + diff --git a/src/AzureRM.BootStrapper/Module/help/Use-AzureRmProfile.md b/src/AzureRM.BootStrapper/Module/help/Use-AzureRmProfile.md new file mode 100644 index 00000000..72fa092b --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/Use-AzureRmProfile.md @@ -0,0 +1,148 @@ +--- +external help file: AzureRM.Bootstrapper-help.xml +online version: +schema: 2.0.0 +--- + +# Use-AzureRmProfile + +## SYNOPSIS +Load the modules associated with a particular profile in the current PowerShell session. This should always be executed in a new PowerShell session. + +## SYNTAX + +``` +Use-AzureRmProfile [-WhatIf] [-Confirm] [-Profile] [-Force] [-Scope ] [[-Module] ] + [] +``` + +## DESCRIPTION +Load the modules associated with a particular profile in the current PowerShell session. This should always be executed in a new PowerShell session. + +## EXAMPLES + +### Example 1 +``` +PS C:\> Use-AzureRmProfile -Profile '2017-03-09-profile' +``` + +Load the modules associated with profile version '2017-03-09-profile' in the current session. This should be executed after opening a new PowerShell session. + +### Example 2 +``` +PS C:\> Use-AzureRmProfile -Profile 'Latest' -Module 'AzureRM' -Scope 'CurrentUser' -Force +``` + +Load the module 'AzureRM' associated with profile version 'Latest' in the current session. It downloads and installs from online gallery in the 'CurrentUser' scope if not already installed. This should be executed after opening a new PowerShell session. + +## PARAMETERS + +### -Force +Automatically install modules for the given profile if they are not already installed. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Module +The module name to be used. + +```yaml +Type: Array +Parameter Sets: (All) +Aliases: + +Required: False +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Profile +The profile version to load in the current PowerShell session. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: 2017-03-09-profile, 2017-03-09-profile, + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Scope +Specifies the installation scope of the modules. The acceptable values for this parameter are: AllUsers and CurrentUser. +The AllUsers scope lets modules be installed in a location that is accessible to all users of the computer. +The CurrentUser scope lets modules be installed in a location that is available only to the current user. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: CurrentUser, AllUsers + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Request confrimation for any change made by the cmdlet + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Print the changes that would be made in executing the cmdlets, but do not make any changes. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### None + +## NOTES + +## RELATED LINKS + diff --git a/src/AzureRM.BootStrapper/Module/help/about_version_profiles.help.txt b/src/AzureRM.BootStrapper/Module/help/about_version_profiles.help.txt new file mode 100644 index 00000000..0ff547fe --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/about_version_profiles.help.txt @@ -0,0 +1,122 @@ +TOPIC + about_version_profiles + +SHORT DESCRIPTION + Version profiles provide a mechanism for managing powershell cmdlets that + target specific versions of azure services supported in different instances + of Azure. + +LONG DESCRIPTION + Different concrete instances of Azure (AzureCloud, AzureChinaCloud, + AzureGermanCloud, AzureUSGovernmentCloud, AzureStack) may have different + versions of Azure services installed, with different capabilities. Azure + Version Profiles provide a mechanism for managing these version differences. + + Each Azure instance has a discoverable set of supported version profiles. + + A user can select a version profile supported by the instances of Azure they + target, and this version profile corresponds to versions of the Azure + PowerShell modules. Users can then select these Azure PowerShell module + versions and be confident that their scripts will work when targeting those + Azure instances. + The AzureRM.Bootstrapper module provides cmdlets to discover, acquire, and + use modules that are appropriate for the azure version profile you are targeting. + You can also use Tags in the AzureRM modules to discover profile information + for each module version. + + Tags for a Profile use the form VersionProfile:2019-03-01-hybrid + The AzureRM bootstrapper module uses the PowerShell Gallery to install and + load needed modules when you want to target a specific version profile. + +EXAMPLES +Finding appropriate version profiles + Use the Get-AzureRmProfile cmdlet to discover available profile versions, + and profile versions supported by an Azure instance. + + Get-AzureRmProfile -ListAvailable + + lists all available version profiles. + + Use-AzureRmProfile -Profile 2019-03-01-hybrid + + Installs and loads cmdlets for one of the listed profiles. + +Targeting all Azure Instances + Get-AzureRmProfile + + Lists the profiles that are currently installed on the machine. + + Use-AzureRmProfile -Profile 2019-03-01-hybrid + + Installs and loads cmdlets compatible with one of the listed profiles. + +Targeting the Latest Stable Features + Use-AzureRmProfile -Profile Latest + + Installs and loads the latest published cmdlets for Azure PowerShell. + +Acquiring and Loading All Azure modules using the BootStrapper + Use-AzureRmProfile -Profile '2019-03-01-hybrid' -Force + + Checks if modules compatible with the '2019-03-01-hybrid' profile are + installed in the current scope, downloads and installs the modules if + necessary, and then loads the modules in the current session. You must + open a new PowerShell session to target a different version profile. Using + the 'Force' parameter installs the necessary modules without prompting. + +Acquiring and Loading Selected Azure modules using the Bootstrapper + Use-AzureRmProfile -Profile '2019-03-01-hybrid' -Module AzureRM.Compute + + Checks if an AzureRM.Compute module compatible with the + '2019-03-01-hybrid' profile is installed in the current scope, downloads + and installs the module if necessary, and then loads the module in the + current session. You must open a new PowerShell session to target a + different module. + +Switching Between Version Profiles + To switch between version profiles on a machine, in a new PowerShell window, + execute the following cmdlet: + + Use-AzureRmProfile -Profile '2019-03-01-hybrid' + + This loads the modules associated with the '2019-03-01-hybrid' profile in + the current session. You must open a new PowerShell session to target a + different version profile. + + +Updating and Removing Profiles + To update a profile to the latest versions in that profile and import + updated modules to the current session, execute the following cmdlet: + + Update-AzureRmProfile -Profile 'latest' + + This checks if the latest versions of Azure PowerShell modules are + installed, if not prompts the user if they should be installed and imports + them into the current session. This should always be executed in a new + PowerShell session. + If you would like to update to the latest modules in a Profile and remove + previously installed versions of the modules, use: + + Update-AzureRmProfile -Profile 'latest' -RemovePreviousVersions + +Setting and Removing Default Profiles + To set or update a profile as a default to be used with all Azure PowerShell + modules, execute the following cmdlet: + + Set-AzureRmDefaultProfile -Profile '2019-03-01-hybrid' + + The default profile selection is persisted across shells and sessions. + After default profile is set using the above cmdlet, the 'Import-Module' + when used with AzureRm modules will automatically load Azure PowerShell + modules compatible with the given profile. You may also use API version + profile cmdlets without the '-profile' parameter. + + Import-Module AzureRM.Compute + Use-AzureRmProfile + Uninstall-AzureRmProfile + + To remove a default profile from all sessions and shells, execute the + following cmdlet: + + Remove-AzureRmDefaultProfile + diff --git a/src/AzureRM.BootStrapper/Module/help/about_version_profiles.md b/src/AzureRM.BootStrapper/Module/help/about_version_profiles.md new file mode 100644 index 00000000..c753e1ec --- /dev/null +++ b/src/AzureRM.BootStrapper/Module/help/about_version_profiles.md @@ -0,0 +1,153 @@ +# Version Profiles +## about_version_profiles + +# SHORT DESCRIPTION +Version profiles provide a mechanism for managing powershell cmdlets that target +specific versions of azure services supported in different instances of Azure. + +# LONG DESCRIPTION +Different concrete instances of Azure (AzureCloud, AzureChinaCloud, +AzureGermanCloud, AzureUSGovernmentCloud, AzureStack) may have different +versions of Azure services installed, with different capabilities. Azure +Version Profiles provide a mechanism for managing these version differences. +Each Azure instance has a discoverable set of supported version profiles. +A user can select a version profile supported by the instances of Azure they +target, and this version profile corresponds to versions of the Azure PowerShell +modules. Users can then select these Azure PowerShell module versions and be +confident that their scripts will work when targeting those Azure instances. + +The AzureRM.Bootstrapper module provides cmdlets to discover, acquire, and use +modules that are appropriate for the azure version profile you are targeting. + +You can also use Tags in the AzureRM modules to discover profile information +for each module version. + +Tags for a Profile use the form VersionProfile:2019-03-01-hybrid + +The AzureRM bootstrapper module uses the PowerShell Gallery to install and +load needed modules when you want to target a specific version profile. + +# EXAMPLES + +## Finding appropriate version profiles + +Use the Get-AzureRmProfile cmdlet to discover available profile versions, +and profile versions supported by an Azure instance. + +``` +Get-AzureRmProfile -ListAvailable +``` + +lists all available version profiles. + +``` +Use-AzureRmProfile -Profile 2019-03-01-hybrid +``` + +Installs and loads cmdlets for one of the listed profiles. + +## Targeting all Azure Instances + +``` +Get-AzureRmProfile +``` + +Lists the profiles that are currently installed on the machine. + +``` +Use-AzureRmProfile -Profile 2019-03-01-hybrid +``` +Installs and loads cmdlets compatible with one of the listed profiles. + +## Targeting the Latest Stable Features + +``` +Use-AzureRmProfile -Profile Latest +``` +Installs and loads the latest published cmdlets for Azure PowerShell. + +## Acquiring and Loading All Azure modules using the BootStrapper + +``` +Use-AzureRmProfile -Profile '2019-03-01-hybrid' -Force +``` + +Checks if modules compatible with the '2019-03-01-hybrid' profile are +installed in the current scope, downloads and installs the modules if necessary, +and then loads the modules in the current session. You must open a new +PowerShell session to target a different version profile. Using the +'Force' parameter installs the necessary modules without prompting. + +## Acquiring and Loading Selected Azure modules using the Bootstrapper + +``` +Use-AzureRmProfile -Profile '2019-03-01-hybrid' -Module AzureRM.Compute +``` + +Checks if an AzureRM.Compute module compatible with the +'2019-03-01-hybrid' profile is installed in the current scope, downloads +and installs the module if necessary, and then loads the module in the +current session. You must open a new PowerShell session to target a different +module. + +## Switching Between Version Profiles + +To switch between version profiles on a machine, in a new PowerShell window, +execute the following cmdlet: + +``` +Use-AzureRmProfile -Profile '2019-03-01-hybrid' +``` + +This loads the modules associated with the '2019-03-01-hybrid' profile in +the current session. You must open a new PowerShell session to target a +different version profile. + +## Updating and Removing Profiles + +To update a profile to the latest versions in that profile and import updated +modules to the current session, execute the following cmdlet: + +``` +Update-AzureRmProfile -Profile 'latest' +``` + +This checks if the latest versions of Azure PowerShell modules are +installed, if not prompts the user if they should be installed and imports +them into the current session. This should always be executed in a new +PowerShell session. + +If you would like to update to the latest modules in a Profile and remove +previously installed versions of the modules, use: + +``` +Update-AzureRmProfile -Profile 'latest' -RemovePreviousVersions +``` + +## Setting and Removing Default Profiles + +To set or update a profile as a default to be used with all Azure PowerShell +modules, execute the following cmdlet: + +``` +Set-AzureRmDefaultProfile -Profile '2019-03-01-hybrid' +``` +The default profile selection is persisted across shells and sessions. + +After default profile is set using the above cmdlet, the 'Import-Module' +when used with AzureRm modules will automatically load Azure PowerShell +modules compatible with the given profile. You may also use API version +profile cmdlets without the '-profile' parameter. + +``` +Import-Module AzureRM.Compute +Use-AzureRmProfile +Uninstall-AzureRmProfile +``` + +To remove a default profile from all sessions and shells, execute the following + cmdlet: + +``` +Remove-AzureRmDefaultProfile +``` diff --git a/src/AzureRM.BootStrapper/build-module.ps1 b/src/AzureRM.BootStrapper/build-module.ps1 new file mode 100644 index 00000000..37f4e5fb --- /dev/null +++ b/src/AzureRM.BootStrapper/build-module.ps1 @@ -0,0 +1,91 @@ +# ---------------------------------------------------------------------------------- +# +# Copyright Microsoft Corporation +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ---------------------------------------------------------------------------------- +param([switch]$Isolated, [switch]$Run, [switch]$Test, [switch]$Docs, [switch]$Pack, [switch]$Code, [switch]$Release, [switch]$Debugger, [switch]$NoDocs) +$ErrorActionPreference = 'Stop' + +$toss = Join-Path $PSScriptRoot "toss" +if (Test-Path $toss) +{ + Remove-Item -Path $toss -Recurse -Force | Out-null +} + +if($PSEdition -ne 'Core') { + Write-Error 'This script requires PowerShell Core to execute. [Note] Generated cmdlets will work in both PowerShell Core or Windows PowerShell.' +} + +if(-not $Isolated -and -not $Debugger) { + Write-Host -ForegroundColor Green 'Creating isolated process...' + $pwsh = [System.Diagnostics.Process]::GetCurrentProcess().Path + & "$pwsh" -NonInteractive -NoLogo -NoProfile -File $MyInvocation.MyCommand.Path @PSBoundParameters -Isolated + + if($LastExitCode -ne 0) { + # Build failed. Don't attempt to run the module. + return + } + + if($Test) { + . (Join-Path $PSScriptRoot 'test-module.ps1') + if($LastExitCode -ne 0) { + # Tests failed. Don't attempt to run the module. + return + } + } + + + if($Pack) { + . (Join-Path $PSScriptRoot 'pack-module.ps1') + if($LastExitCode -ne 0) { + # Packing failed. Don't attempt to run the module. + return + } + } + + $runModulePath = Join-Path $PSScriptRoot 'run-module.ps1' + if($Code) { + . $runModulePath -Code + } elseif($Run) { + . $runModulePath + } else { + Write-Host -ForegroundColor Cyan "To run this module in an isolated PowerShell session, run the 'run-module.ps1' script or provide the '-Run' parameter to this script." + } + return +} + +$binFolder = Join-Path $PSScriptRoot 'bin' +$objFolder = Join-Path $PSScriptRoot 'obj' + +if(-not $Debugger) { + Write-Host -ForegroundColor Green 'Cleaning build folders...' + $null = Remove-Item -Recurse -ErrorAction SilentlyContinue -Path $binFolder, $objFolder + + if((Test-Path $binFolder) -or (Test-Path $objFolder)) { + Write-Host -ForegroundColor Cyan 'Did you forget to exit your isolated module session before rebuilding?' + Write-Error 'Unable to clean ''bin'' or ''obj'' folder. A process may have an open handle.' + } + + Write-Host -ForegroundColor Green 'Compiling module...' + $buildConfig = 'Debug' + if($Release) { + $buildConfig = 'Release' + } + dotnet publish $PSScriptRoot --verbosity quiet --configuration $buildConfig /nologo + if($LastExitCode -ne 0) { + Write-Error 'Compilation failed.' + } + + $null = Remove-Item -Recurse -ErrorAction SilentlyContinue -Path (Join-Path $binFolder 'Debug'), (Join-Path $binFolder 'Release') +} + + +Write-Host -ForegroundColor Green '-------------Done-------------' diff --git a/src/AzureRM.BootStrapper/dummy.json b/src/AzureRM.BootStrapper/dummy.json new file mode 100644 index 00000000..7156833b --- /dev/null +++ b/src/AzureRM.BootStrapper/dummy.json @@ -0,0 +1,32 @@ +{ + "swagger": "2.0", + "info": { + "title": "AzureStack", + "description": "Placeholder for non-generation", + "version": "9999-12-31-preview" + }, + "host": "management.azure.com", + "schemes": [ + "https" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "securityDefinitions": { + "azure_auth": { + "type": "oauth2", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "flow": "implicit", + "description": "Azure Active Directory OAuth2 Flow", + "scopes": { + "user_impersonation": "impersonate your user account" + } + } + }, + "paths": {}, + "parameters": {}, + "definitions": {} +} \ No newline at end of file diff --git a/src/AzureRM.BootStrapper/pack-module.ps1 b/src/AzureRM.BootStrapper/pack-module.ps1 new file mode 100644 index 00000000..c22fad33 --- /dev/null +++ b/src/AzureRM.BootStrapper/pack-module.ps1 @@ -0,0 +1,16 @@ +# ---------------------------------------------------------------------------------- +# +# Copyright Microsoft Corporation +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ---------------------------------------------------------------------------------- +Write-Host -ForegroundColor Green 'Packing module...' +dotnet pack $PSScriptRoot --no-build /nologo +Write-Host -ForegroundColor Green '-------------Done-------------' \ No newline at end of file diff --git a/src/AzureRM.BootStrapper/readme.md b/src/AzureRM.BootStrapper/readme.md new file mode 100644 index 00000000..720d1359 --- /dev/null +++ b/src/AzureRM.BootStrapper/readme.md @@ -0,0 +1,17 @@ + ## Run Generation +In this directory, run AutoRest: +> `autorest` + +--- +### AutoRest Configuration +> see https://aka.ms/autorest + +``` yaml +require: + - $(this-folder)/../readme.azurestack.md + +input-file: + - $(this-folder)/dummy.json + +output-folder: $(this-folder)/toss/ +``` diff --git a/src/AzureRM.BootStrapper/test-module.ps1 b/src/AzureRM.BootStrapper/test-module.ps1 new file mode 100644 index 00000000..c3046ec1 --- /dev/null +++ b/src/AzureRM.BootStrapper/test-module.ps1 @@ -0,0 +1,37 @@ +param([switch]$Isolated, [switch]$Live, [switch]$Record, [switch]$Playback) +$ErrorActionPreference = 'Stop' + +if(-not $Isolated) { + Write-Host -ForegroundColor Green 'Creating isolated process...' + $pwsh = [System.Diagnostics.Process]::GetCurrentProcess().Path + & "$pwsh" -NonInteractive -NoLogo -NoProfile -File $MyInvocation.MyCommand.Path @PSBoundParameters -Isolated + return +} + +$ProgressPreference = 'SilentlyContinue' +. (Join-Path $PSScriptRoot 'check-dependencies.ps1') -Isolated -Accounts:$true -Pester + +$localModulesPath = Join-Path $PSScriptRoot 'generated\modules' +if(Test-Path -Path $localModulesPath) { + $env:PSModulePath = "$localModulesPath$([IO.Path]::PathSeparator)$env:PSModulePath" +} + +$modulePsd1 = Get-Item -Path (Join-Path $PSScriptRoot './Azs.Stack.psd1') +$modulePath = $modulePsd1.FullName +$moduleName = $modulePsd1.BaseName + +Import-Module -Name Pester +Import-Module -Name $modulePath + +$TestMode = 'playback' +if($Live) { + $TestMode = 'live' +} +if($Record) { + $TestMode = 'record' +} + +$testFolder = Join-Path $PSScriptRoot 'test' +Invoke-Pester -Script @{ Path = $testFolder } -EnableExit -OutputFile (Join-Path $testFolder "$moduleName-TestResults.xml") + +Write-Host -ForegroundColor Green '-------------Done-------------' \ No newline at end of file From 2a6f69a49fb3d075d0363a260e1d37f5e324a212 Mon Sep 17 00:00:00 2001 From: bganapa Date: Mon, 30 Nov 2020 12:18:44 -0800 Subject: [PATCH 2/3] Updating ProfileMap during Install and Use-AzureRMProfile cmdlet --- src/AzureRM.BootStrapper/AzureRM.BootStrapper.csproj | 2 +- src/AzureRM.BootStrapper/AzureRM.BootStrapper.nuspec | 2 +- src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.psd1 | 6 +++--- src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.psm1 | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/AzureRM.BootStrapper/AzureRM.BootStrapper.csproj b/src/AzureRM.BootStrapper/AzureRM.BootStrapper.csproj index d9a52059..8b504647 100644 --- a/src/AzureRM.BootStrapper/AzureRM.BootStrapper.csproj +++ b/src/AzureRM.BootStrapper/AzureRM.BootStrapper.csproj @@ -1,6 +1,6 @@ - 0.5.0 + 0.6.0 7.1 netstandard2.0 Module diff --git a/src/AzureRM.BootStrapper/AzureRM.BootStrapper.nuspec b/src/AzureRM.BootStrapper/AzureRM.BootStrapper.nuspec index 849b9f71..2cddefab 100644 --- a/src/AzureRM.BootStrapper/AzureRM.BootStrapper.nuspec +++ b/src/AzureRM.BootStrapper/AzureRM.BootStrapper.nuspec @@ -2,7 +2,7 @@ AzureRM.BootStrapper - 0.5.0 + 0.6.0 Microsoft Corporation Microsoft Corporation true diff --git a/src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.psd1 b/src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.psd1 index b7f7bd26..9ed9cbe6 100644 --- a/src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.psd1 +++ b/src/AzureRM.BootStrapper/Module/AzureRM.BootStrapper.psd1 @@ -12,7 +12,7 @@ RootModule = 'AzureRM.Bootstrapper.psm1' # Version number of this module. -ModuleVersion = '0.5.0' +ModuleVersion = '0.6.0' # Supported PSEditions # CompatiblePSEditions = @() @@ -114,8 +114,8 @@ PrivateData = @{ # IconUri = '' # ReleaseNotes of this module - ReleaseNotes = '* 0.5.0: Updated for the azure stack profile 2019-03-01-hybrid - * Bug fix to add UseBasicParsing for Invoke-WebRequest' + ReleaseNotes = '* 0.6.0: Bug fix for ProfileCache not being updated after installing Az.BootStrapper and then reverting back to use AzureRM.BootStrapper + * We always read the map from the azure blob now' # External dependent modules of this module # ExternalModuleDependencies = '' diff --git a/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.psm1 b/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.psm1 index 23610d6a..4a0cfc4a 100644 --- a/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.psm1 +++ b/src/AzureRM.BootStrapper/Module/AzureRM.Bootstrapper.psm1 @@ -984,7 +984,7 @@ function Use-AzureRmProfile PROCESS { $Force = $PSBoundParameters.Force - $ProfileMap = (Get-AzProfile) + $ProfileMap = (Get-AzProfile -Update) $Profile = $PSBoundParameters.Profile $Scope = $PSBoundParameters.Scope $Modules = $PSBoundParameters.Module @@ -1072,7 +1072,7 @@ function Install-AzureRmProfile } PROCESS { - $ProfileMap = (Get-AzProfile) + $ProfileMap = (Get-AzProfile -Update) $Profile = $PSBoundParameters.Profile $Scope = $PSBoundParameters.Scope $Modules = ($ProfileMap.$Profile | Get-Member -MemberType NoteProperty).Name @@ -1128,7 +1128,7 @@ function Uninstall-AzureRmProfile } PROCESS { - $ProfileMap = (Get-AzProfile) + $ProfileMap = (Get-AzProfile -Update) $Profile = $PSBoundParameters.Profile $Force = $PSBoundParameters.Force From 65069239b540c16ad8a06d06d21e9bd0f902b93b Mon Sep 17 00:00:00 2001 From: Bala Ganapathy Date: Sun, 10 Jan 2021 12:41:59 -0800 Subject: [PATCH 3/3] Adding tools folder in azurerm branch --- tools/SecurityTools/CredScanSuppressions.json | 25 ++ .../SecurityTools/PoliCheckFileExtensions.xml | 282 ++++++++++++++++++ tools/Test-AzAccount.ps1 | 201 +++++++++++++ tools/download-nupkg.ps1 | 126 ++++++++ tools/validate-signing.ps1 | 83 ++++++ 5 files changed, 717 insertions(+) create mode 100644 tools/SecurityTools/CredScanSuppressions.json create mode 100644 tools/SecurityTools/PoliCheckFileExtensions.xml create mode 100644 tools/Test-AzAccount.ps1 create mode 100644 tools/download-nupkg.ps1 create mode 100644 tools/validate-signing.ps1 diff --git a/tools/SecurityTools/CredScanSuppressions.json b/tools/SecurityTools/CredScanSuppressions.json new file mode 100644 index 00000000..aeceea81 --- /dev/null +++ b/tools/SecurityTools/CredScanSuppressions.json @@ -0,0 +1,25 @@ +{ + "tool": "Credential Scanner", + "suppressions": [ + { + "file": "src\\Azs.Backup.Admin\\test\\Common.ps1", + "_justification": "non live decryption key in test file" + }, + { + "file": "src\\Azs.Compute.Admin\\tests\\Common.ps1", + "_justification": "inernal test environment sas url" + }, + { + "file": "src\\Azs.Compute.Admin\\docs\\Add-AzsPlatformImage.md", + "_justification": "publicly known uri" + }, + { + "file": "src\\Azs.Compute.Admin\\examples\\Add-AzsPlatformImage.md", + "_justification": "publicly known uri" + }, + { + "file": "src\\Azs.Compute.Admin\\tests\\PlatformImage.Tests.Recording.json", + "_justification": "Test SAS URL, Not sensitive." + } + ] +} diff --git a/tools/SecurityTools/PoliCheckFileExtensions.xml b/tools/SecurityTools/PoliCheckFileExtensions.xml new file mode 100644 index 00000000..4e5f4257 --- /dev/null +++ b/tools/SecurityTools/PoliCheckFileExtensions.xml @@ -0,0 +1,282 @@ + + + + + Pure Text Files + + .txt + .des + .pwd + .asm + .cmd + .ini + .poc + .pwt + .hpj + .sql + .inf + .log + .def + .url + .bat + .aspx + .idl + .strings + .md + .yml + .yaml + .ps1 + .psm1 + .psd1 + .ps1xml + .ts + .php + .csv + .py + + + + CodeFiles + + .frm + .inc + .cpp + .cls + .c + .hpp + .vbs + .java + .cs + .cxx + .h + .jav + .bas + .hxx + .js + .pl + .rc + .vb + .json + .resjson + .fs + .fsi + .fsx + .m + .swift + .config + + + + XML Files + + .xml + .hxa + .hxk + .hxl + .xsl + .hxc + .hxt + .hxm + .resx + .hxe + .hxf + .hxv + .acctb + .accfl + .xaml + .ttml + .ddue + + + + Microsoft Word Documents + + .doc + .dot + .wiz + + + + Microsoft Access Database Compatible + + .mda + .mde + .mpd + .mdt + + + + Microsoft PowerPoint Presentation + + .ppt + .pot + .pps + + + + Microsoft Publisher Files + + .pub + + + + Microsoft Excel Workbooks + + .xls + .xlt + + + + Localization resource databases + + .lcl + .xlf + .xliff + + + + Microsoft Project Files + + .mpp + .mpt + + + + Microsoft Visio Files + + .vsd + .vdx + .vss + .vst + + + + Zip Files + + .zip + .accdt + .axtr + + + + Cabinet / MS Compression Files + + .cab + + + + HTML Help 2.0 Files / InfoTech5.x Storage System Files + + .its + .hxh + .hxr + .hxw + .hxi + .hxs + .hxq + + + + HTML Files / Web Page + + .htm + .dtd + .hhk + .htw + .asp + .htc + .htx + .html + .hhc + .css + .stm + + + + Rich Text Files + + .rtf + + + + Windows 3.x Write Files + + .wri + + + + MHTML Files + + .eml + .nws + .mht + + + + Word 2007 Files + + .docx + .docm + .dotx + .dotm + + + + Excel 2007 Files + + .xlsx + .xlsm + .xltx + .xltm + .xlsb + .xlam + + + + Power Point 2007 Files + + .pptx + .pptm + .potx + .potm + .ppsx + .ppsm + .ppam + + + + Access 2007 Files + + .accdb + .accde + .accdr + + + + LocStudio lsg + + .lsg + + + + Microsoft Office OneNote Files + + .one + .onepkg + + + + Custom Parsers + + + + + Visio 2011 Files + + .vstx + .vsdx + .vssx + + + + \ No newline at end of file diff --git a/tools/Test-AzAccount.ps1 b/tools/Test-AzAccount.ps1 new file mode 100644 index 00000000..00819afd --- /dev/null +++ b/tools/Test-AzAccount.ps1 @@ -0,0 +1,201 @@ +<# +.SYNOPSIS + This test is for testing new Az.Accounts modules. + + IMPORTANT! + 1. For this test to work, install the following "SpnCert.pfx" file to "Cert:\LocalMachine\My" from the following link: + Online + https://ms.portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/46d8b0b3-665f-4745-9fae-95ad05d82e15/resourceGroups/SpnRg/providers/Microsoft.KeyVault/vaults/SpnKvRedmond/certificates + + On the Microsoft Network: + \\baladev\public\wei + + 2. Get the password to SpnCert.pfx from someone on the AzureStack DevExp team, or check the following location: + \\baladev\public\wei + + 3. Convert the password to secure string: + $pass = ConvertTo-SecureString -String $unsecurePass -AsPlainText -Force + + 2. Then import it: + Import-PfxCertificate -FilePath C:\SpnCert.pfx -CertStoreLocation Cert:\LocalMachine\My -Password $pass +#> +param +( + [Parameter(Mandatory=$false)] + [ValidateNotNullOrEmpty()] + [string] $EnvironmentName = 'az-accounts-test', + + [Parameter(Mandatory=$true)] + [ValidateNotNull()] + [ValidateScript({ $_.Scheme -eq [System.Uri]::UriSchemeHttps })] + [System.Uri] $ResourceManagerEndpoint, + + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] $TenantId, + + [Parameter(Mandatory=$true)] + [ValidateNotNull()] + [string] $ClientIDForSecret, + + [Parameter(Mandatory=$true)] + [ValidateNotNull()] + [string] $ClientSecret, + + [Parameter(Mandatory=$true)] + [ValidateNotNull()] + [string] $ClientIDForCertificate, + + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [ValidatePattern('^([0-9A-Fa-f]{2})*$')] + [string] $CertificateThumbprint, + + # Optional subscription to select as the active / default subscription. + [Parameter(Mandatory=$false)] + [ValidateNotNullOrEmpty()] + [string] $SubscriptionId +) + +function Test +{ + Param + ( + [String] $TestName, + [ScriptBlock] $TestBlock + ) + + Write-Verbose -Message "[START]: ${TestName}" -Verbose + try + { + $TestBlock.Invoke() + } + catch + { + Write-Error $_ + } + Write-Verbose -Message "[END]: ${TestName}`n" -Verbose +} + +$TestAddAzEnvironment = +{ + Write-Verbose -Message "Running Add-AzEnvironment..." -Verbose + Add-AzEnvironment -Name $EnvironmentName -ARMEndpoint $ResourceManagerEndpoint -ErrorAction Stop + $getEnvironment = Get-AzEnvironment -Name $EnvironmentName + if ($null -eq $getEnvironment) + { + throw "Could not find the environment ${EnvironmentName} after adding it." + } + Get-AzEnvironment -Name $EnvironmentName | Format-List -Property * +} + +$TestServicePrincipalSecretLogin = +{ + $sleepSecondsUnit = 5 + $maxSleepSeconds = 30 + $currentSeconds = 0 + + # $currentSeconds should not be compared to a multiple of $sleepSecondUnit since it won't tell us if the operation was successful because it could be that + # it was successful at the last second. If the $currentSeconds is ($maxSleepSeconds+1) or over, we know for sure the operation took too long. + while ($currentSeconds -lt $maxSleepSeconds + 1) + { + Write-Verbose -Message "Connecting to service principal, ignore errors until timeout. $($maxSleepSeconds - $currentSeconds) seconds until timeout error." -Verbose + try + { + $servicePrincipalSecurePassword = $ClientSecret | ConvertTo-SecureString -AsPlainText -Force + $servicePrincipalCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ClientIDForSecret, $servicePrincipalSecurePassword + + if ($null -eq $SubscriptionId) + { + Connect-AzAccount -Environment $EnvironmentName -ServicePrincipal -Credential $servicePrincipalCredential -TenantId $TenantId + } + else + { + Connect-AzAccount -Environment $EnvironmentName -ServicePrincipal -Credential $servicePrincipalCredential -TenantId $TenantId -Subscription $SubscriptionId + } + + Start-Sleep -s $sleepSecondsUnit + $currentSeconds += $sleepSecondsUnit + + if (($null -eq (get-AzContext)) -or ($ClientIDForSecret -ne ((Get-AzContext).Account.Id))) + { + throw "Failed to connect to service principal with client ID ${clientIDForSecret}. Trying again..." + } + + Write-Verbose -Message "Current context:" -Verbose + Get-AzContext | Format-List -Property * | Out-String | Write-Verbose -Verbose + Write-Verbose -Message "Current account:" -Verbose + $(Get-AzContext).Account | Format-List -Property * | Out-String | Write-Verbose -Verbose + + break + } + catch + { + Write-Verbose -Message $_ -Verbose + } + } + + if ($currentSeconds -ge $maxSleepSeconds + 1) + { + throw "Connect-AzAccount command timed out." + } +} + +$TestServicePrincipalCertificateLogin = +{ + $sleepSecondsUnit = 5 + $maxSleepSeconds = 30 + $currentSeconds = 0 + + # $currentSeconds should not be compared to a multiple of $sleepSecondUnit since it won't tell us if the operation was successful because it could be that + # it was successful at the last second. If the $currentSeconds is ($maxSleepSeconds+1) or over, we know for sure the operation took too long. + while ($currentSeconds -lt $maxSleepSeconds + 1) + { + Write-Verbose "Connecting to service principal, ignore errors until timeout. $($maxSleepSeconds - $currentSeconds) seconds until timeout error." -Verbose + try + { + if ($null -eq $SubscriptionId) + { + Connect-AzAccount -Environment $EnvironmentName -ServicePrincipal -CertificateThumbprint $CertificateThumbprint -ApplicationId $clientIDForCertificate -TenantId $TenantId + } + else + { + Connect-AzAccount -Environment $EnvironmentName -ServicePrincipal -CertificateThumbprint $CertificateThumbprint -ApplicationId $clientIDForCertificate -TenantId $TenantId -Subscription $SubscriptionId + } + + Start-Sleep -s $sleepSecondsUnit + $currentSeconds += $sleepSecondsUnit + + if (($null -eq (get-AzContext)) -or ($clientIDForCertificate -ne ((Get-AzContext).Account.Id))) + { + throw "Failed to connect to service principal with client ID ${ClientIdForCertificate}. Try again..." + } + + Write-Verbose -Message "Current context:" -Verbose + Get-AzContext | Format-List -Property * | Out-String | Write-Verbose -Verbose + Write-Verbose -Message "Current account:" -Verbose + $(Get-AzContext).Account | Format-List -Property * | Out-String | Write-Verbose -Verbose + break + } + catch + { + Write-Verbose -Message $_ -Verbose + } + } + + if ($currentSeconds -ge $maxSleepSeconds + 1) + { + throw "Connect-AzAccount command timed out." + } +} + +function Main +{ + Import-Module Az.Accounts + + Test -TestName "TestAzAccounts" -TestBlock $TestAddAzEnvironment + Test -TestName "TestServicePrincipalCertificateLogin" -TestBlock $TestServicePrincipalCertificateLogin + Test -TestName "TestServicePrincipalSecretLogin" -TestBlock $TestServicePrincipalSecretLogin +} + +Main \ No newline at end of file diff --git a/tools/download-nupkg.ps1 b/tools/download-nupkg.ps1 new file mode 100644 index 00000000..d99c3d2f --- /dev/null +++ b/tools/download-nupkg.ps1 @@ -0,0 +1,126 @@ +<# + This script downloads artifacts from Azure Devops with build ID $buildId using your personal access token + $personalAccessToken, and downloads the artifacts as zip files to $destinationFolder, where the .nupkg files will be + extracted. + + $buildId: The numerical ID of the build. If empty, the latest build ID number will be used. + $destinationFolder: The destination folder to download the artifacts and extract the nupkg. Defaults to the current + script's directory. + $personalAccessToken: The personal access token granted to you from Azure Devops. Defaults to environment variable + named "PersonalAccessToken". + + Sample headers: + + Get the latest build information: + $buildReq = @{ + Uri = "https://dev.azure.com/azure-sdk/internal/_apis/build/latest/azurestack-powershell%20-%20gen-sign?branchName=dev&api-version=5.1-preview.1" + Headers = $headers + } + + Get multiple builds: + $listBuildsReq = @{ + Uri = "https://dev.azure.com/azure-sdk/internal/_apis/build/builds?buildNumber=api-version=5.1" + Headers = $headers + } + + Get the a single artifact of a single build: + $artifactReq = @{ + Uri = "https://dev.azure.com/azure-sdk/internal/_apis/build/builds/258451/artifacts?artifactName=module-Azs.Commerce.Admin&api-version=5.1" + Headers = $headers + } + + Get all artifacts of a build: + $listArtifactsReq = @{ + Uri = "https://dev.azure.com/azure-sdk/internal/_apis/build/builds/${buildId}/artifacts?api-version=5.1" + Headers = $headers + } +#> + +Param ( + [string]$buildId, + [string]$destinationFolder = $PSScriptRoot, + [string]$personalAccessToken = $env:PersonalAccessToken +) + +$user = $personalAccessToken +$pass = $personalAccessToken +$pair = "${user}:${pass}" +$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) +$base64 = [System.Convert]::ToBase64String($bytes) +$basicAuthValue = "Basic $base64" +$headers = @{ Authorization = $basicAuthValue } +$apiVersion = "5.1-preview" + +# If the $buildId is empty string, download from the latest build. +if ($buildId -eq "") +{ + $buildReq = @{ + Uri = "https://dev.azure.com/azure-sdk/internal/_apis/build/latest/azurestack-powershell%20-%20gen-sign?branchName=dev&api-version=${apiVersion}" + Headers = $headers + } + + $result = Invoke-RestMethod @buildReq -Method Get + $buildId = $result.buildNumber +} + +$listArtifactsReq = @{ + Uri = "https://dev.azure.com/azure-sdk/internal/_apis/build/builds/${buildId}/artifacts?api-version=${apiVersion}" + Headers = $headers +} + +$result = Invoke-RestMethod @listArtifactsReq -Method Get + +<##################################### + + DOWNLOAD ZIPS + +#####################################> +$downloadedModuleZips = New-Object System.Collections.Generic.List[System.Object] +foreach($resultValue in $result.value) { + Write-Host "Downloading: $($resultValue.resource.downloadUrl)" + $fileReq = @{ + Uri = $resultValue.resource.downloadUrl + Headers = $headers + } + $downloadedModuleZips.add("${destinationFolder}\$($resultValue.name).zip") + Invoke-RestMethod @fileReq -Method Get -Outfile "${destinationFolder}\$($resultValue.name).zip" +} + +<##################################### + + EXTRACT ZIPS + +#####################################> +$extractedModulesFolder = New-Object System.Collections.Generic.List[System.Object] + +foreach($moduleZip in $downloadedModuleZips) +{ + $expanded = Expand-Archive -LiteralPath $moduleZip -DestinationPath $destinationFolder -Force -PassThru + + foreach($expandedFolder in $expanded) + { + if ($expandedFolder.directory.name -match "module-[^\\]*$") + { + $extractedModulesFolder.add($expandedFolder.directory.fullName) + } + } + + Remove-Item $moduleZip +} + +<##################################### + + COPY NUPKG + +#####################################> +foreach($extractedModuleFolder in $extractedModulesFolder) +{ + $nupkgs = Get-ChildItem $extractedModuleFolder -Filter "*.nupkg" + + foreach($nupkg in $nupkgs) + { + copy-item $nupkg -Destination $destinationFolder -Force + } + + Remove-Item -LiteralPath $extractedModuleFolder -Force -Recurse +} \ No newline at end of file diff --git a/tools/validate-signing.ps1 b/tools/validate-signing.ps1 new file mode 100644 index 00000000..58435829 --- /dev/null +++ b/tools/validate-signing.ps1 @@ -0,0 +1,83 @@ +<# +.SYNOPSIS + Validates signed files. +.DESCRIPTION + Recursively validates folder for signed files. +.NOTES + Use this script to check files for valid signatures before release. +#> + +# Resolve -path +$path = Resolve-Path -Path "." +$moduleName = $path.Path.Split([IO.Path]::DirectorySeparatorChar)[-1] +Write-Verbose -Message "Current module: ${moduleName}" -Verbose +Write-Verbose -Message "Current folder: ${PSScriptRoot}" -Verbose + +$contents = Get-ChildItem -Path $path +$output = $contents | ForEach-Object {$_.FullName} | Out-String +Write-Verbose -Message "${path} contents: `n${output}" -Verbose + +################################# BEGIN EXTRACT NUPKG ################################# + +$nupkgFolder = Join-Path -Path $path -ChildPath 'bin' +$output = Get-ChildItem -Path $nupkgFolder | ForEach-Object {$_.FullName} | Out-String +Write-Verbose -Message "${nupkgFolder} contents: `n${output}" -Verbose +$pathToValidate = Join-Path -Path $nupkgFolder -ChildPath 'extracted' +$nupkgPath = Get-ChildItem -LiteralPath @($nupkgFolder) | Where-Object {$_.Extension -eq ".nupkg"} + +if ($null -eq $nupkgPath) +{ + throw "ERROR: No NUPKG files were found." +} +if ($nupkgPath -is [array]) +{ + throw "ERROR: More than one NUPKG file is detected in the ${$nupkgPath} folder." +} +if (!$pathToValidate) +{ + throw "ERROR: The path ${pathToValidate} is invalid." +} + +Write-Verbose -Message "Extracting $($nupkgPath.FullName) to ${pathToValidate}." -Verbose +Expand-Archive -LiteralPath $nupkgPath.FullName -DestinationPath $pathToValidate +Write-Verbose -Message "Current path to check for unsigned files: ${pathToValidate}." -Verbose + +################################# END EXTRACT NUPKG ################################# + +$exclude = @("Unprotect-SecureString.ps1") + +if (Test-Path -Path $pathToValidate -PathType Container) +{ + $fileInfos = Get-ChildItem -Path $pathToValidate -File -Recurse ` + | Where-Object { $_.Name -notin $exclude } + | Where-Object { $_.Extension -in @('.dll','.exe','.msi','.cab','.ps1','.psm1','.psd1','.pssc','.ps1xml') } ` + | Where-Object { $_.FullName -notlike $(Join-Path -Path "*${moduleName}" -ChildPath "test*") } +} +elseif (Test-Path -Path $pathToValidate -PathType Leaf) +{ + $fileInfos = Get-ChildItem -Path $pathToValidate -File +} +else +{ + Write-Error -Message "Invalid path: ${pathToValidate}" -Category InvalidArgument +} + +$filePaths = $fileInfos | ForEach-Object {$_.FullName} +$output = $filePaths | Out-String +Write-Verbose -Message "Files to be checked: `n${output}" -Verbose + +$authenticodeStatuses = $filePaths | Get-AuthenticodeSignature +Write-Verbose -Message "Statuses of files in the folder ${pathToValidate}:" -Verbose +$authenticodeStatuses | Select-Object -Property Status, Path | Out-String -width 4096 + +$unsignedFiles = $authenticodeStatuses | Where-Object {$_.Status -eq [System.Management.Automation.SignatureStatus]::NotSigned} +$unsignedFilesFormatted = $unsignedFiles | Select-Object -Property Status, Path | Out-String + +if ($unsignedFiles) +{ + Write-Verbose -Message "ERROR: These files in ${pathToValidate} are unsigned:" -Verbose + $unsignedFilesFormatted | Out-String -width 4096 + throw "ERROR: The module contains unsigned files." +} + +Remove-Item $pathToValidate -Recurse \ No newline at end of file