From 557bd6ce54a31d89f7f8607015a24efd1990a977 Mon Sep 17 00:00:00 2001 From: Maxime Kjaer Date: Fri, 14 Jun 2024 08:43:14 -0700 Subject: [PATCH] Prepare RestSetAcls for PowerShellGallery publish (#218) * Add github actions * Add psd1 file * Rework file structure for publishing * Fix dev build tools --- .github/workflows/RestSetAcls.yml | 54 +++++++ .gitignore | 3 + RestSetAcls/CONTRIBUTING.md | 4 +- RestSetAcls/{ => RestSetAcls}/PrintUtils.ps1 | 0 RestSetAcls/RestSetAcls/RestSetAcls.psd1 | 148 ++++++++++++++++++ .../{ => RestSetAcls}/RestSetAcls.psm1 | 1 + RestSetAcls/{ => RestSetAcls}/SddlUtils.ps1 | 0 RestSetAcls/TestSetup.ps1 | 75 --------- RestSetAcls/build.depend.psd1 | 13 ++ RestSetAcls/check-manifest.ps1 | 1 + RestSetAcls/init.ps1 | 15 ++ RestSetAcls/lint.ps1 | 8 + RestSetAcls/publish-local.ps1 | 47 ++++++ RestSetAcls/publish-psgallery.ps1 | 14 ++ RestSetAcls/test.ps1 | 1 + RestSetAcls/{ => test}/SddlUtils.Tests.ps1 | 2 +- RestSetAcls/test/TestSetup.ps1 | 75 +++++++++ RestSetAcls/unpublish-local.ps1 | 32 ++++ 18 files changed, 415 insertions(+), 78 deletions(-) create mode 100644 .github/workflows/RestSetAcls.yml rename RestSetAcls/{ => RestSetAcls}/PrintUtils.ps1 (100%) create mode 100644 RestSetAcls/RestSetAcls/RestSetAcls.psd1 rename RestSetAcls/{ => RestSetAcls}/RestSetAcls.psm1 (99%) rename RestSetAcls/{ => RestSetAcls}/SddlUtils.ps1 (100%) delete mode 100644 RestSetAcls/TestSetup.ps1 create mode 100644 RestSetAcls/build.depend.psd1 create mode 100644 RestSetAcls/check-manifest.ps1 create mode 100644 RestSetAcls/init.ps1 create mode 100644 RestSetAcls/lint.ps1 create mode 100644 RestSetAcls/publish-local.ps1 create mode 100644 RestSetAcls/publish-psgallery.ps1 create mode 100644 RestSetAcls/test.ps1 rename RestSetAcls/{ => test}/SddlUtils.Tests.ps1 (99%) create mode 100644 RestSetAcls/test/TestSetup.ps1 create mode 100644 RestSetAcls/unpublish-local.ps1 diff --git a/.github/workflows/RestSetAcls.yml b/.github/workflows/RestSetAcls.yml new file mode 100644 index 00000000..d67431ca --- /dev/null +++ b/.github/workflows/RestSetAcls.yml @@ -0,0 +1,54 @@ +name: Test RestSetAcls +on: + push: + paths: + - ".github/workflows/RestSetAcls.yml" + - "RestSetAcls/**" + pull_request: + paths: + - ".github/workflows/RestSetAcls.yml" + - "RestSetAcls/**" + +jobs: + pester-test: + name: Pester test + runs-on: windows-latest + defaults: + run: + working-directory: .\RestSetAcls + shell: pwsh + steps: + - name: Check out repository code + uses: actions/checkout@v4 + - name: Run all Pester tests + run: | + .\init.ps1 + .\test.ps1 + + Check-manifest: + name: Check .psd1 manifest file + runs-on: windows-latest + defaults: + run: + working-directory: .\RestSetAcls + shell: pwsh + steps: + - uses: actions/checkout@v4 + - name: Check .psd1 file with Test-ModuleManifest + run: | + .\init.ps1 + .\check-manifest.ps1 + + lint-with-PSScriptAnalyzer: + name: Install and run PSScriptAnalyzer + runs-on: windows-latest + defaults: + run: + working-directory: .\RestSetAcls + shell: pwsh + steps: + - uses: actions/checkout@v4 + - name: Lint with PSScriptAnalyzer + run: | + .\init.ps1 + .\lint.ps1 diff --git a/.gitignore b/.gitignore index c9317d36..e21efa98 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +# RestSetAcls +RestSetAcls/bin + # User-specific files *.suo *.user diff --git a/RestSetAcls/CONTRIBUTING.md b/RestSetAcls/CONTRIBUTING.md index 73453275..20200060 100644 --- a/RestSetAcls/CONTRIBUTING.md +++ b/RestSetAcls/CONTRIBUTING.md @@ -15,7 +15,7 @@ 2. Run the tests ```powershell - Invoke-Pester -Output Detailed + .\test.ps1 ``` ## Setting up a test run @@ -40,7 +40,7 @@ 1. Create test files ```powershell - .\RestSetAcls\TestSetup.ps1 -Context $context -FileShareName $FileShareName -NumberDirs 3 -NumberFilesPerDir 3 -Depth 1 + .\RestSetAcls\test\TestSetup.ps1 -Context $context -FileShareName $FileShareName -NumberDirs 3 -NumberFilesPerDir 3 -Depth 1 ``` This will create a directory structure with 3 directories, each containing 3 files: diff --git a/RestSetAcls/PrintUtils.ps1 b/RestSetAcls/RestSetAcls/PrintUtils.ps1 similarity index 100% rename from RestSetAcls/PrintUtils.ps1 rename to RestSetAcls/RestSetAcls/PrintUtils.ps1 diff --git a/RestSetAcls/RestSetAcls/RestSetAcls.psd1 b/RestSetAcls/RestSetAcls/RestSetAcls.psd1 new file mode 100644 index 00000000..bceec8d1 --- /dev/null +++ b/RestSetAcls/RestSetAcls/RestSetAcls.psd1 @@ -0,0 +1,148 @@ +# +# Module manifest for module 'RestSetAcls' +# +# Generated by: Microsoft Corporation +# +# Generated on: 5/8/2024 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'RestSetAcls.psm1' + +# Version number of this module. +ModuleVersion = '0.1.1' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = '1b9ea644-06b1-47d4-8da4-f8758a43fc49' + +# Author of this module +Author = 'Microsoft Corporation' + +# Company or vendor of this module +CompanyName = 'Microsoft Corporation' + +# Copyright statement for this module +Copyright = '(c) Microsoft Corporation. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'Set file permissions on an Azure Files share using REST API' + +# Minimum version of the PowerShell engine required by this module +PowerShellVersion = '5.1' + +# Name of the PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the 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 = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# ClrVersion = '' + +# 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 = @( + @{ + ModuleName = "Az.Storage" + GUID = "dfa9e4ea-1407-446d-9111-79122977ab20" + ModuleVersion = "6.0.0" + } +) + +# 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 = @() + +# 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-AzureFilesAclRecursive" +) + +# 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 = @() + +# 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. + # See: https://learn.microsoft.com/en-us/powershell/gallery/concepts/publishing-guidelines?view=powershellget-3.x#tag-your-package-with-the-compatible-pseditions-and-platforms + Tags = @( + "Azure", + "Storage", + "ACL", + "PSEdition_Desktop", # Packages that are compatible with Windows PowerShell + "PSEdition_Core", # Packages that are compatible with PowerShell 6 and higher + "Windows" # Packages that are compatible with the Windows Operating System + ) + + # A URL to the license for this module. + LicenseUri = 'https://raw.githubusercontent.com/Azure-Samples/azure-files-samples/master/LICENSE.md' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/Azure-Samples/azure-files-samples' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # 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/RestSetAcls/RestSetAcls.psm1 b/RestSetAcls/RestSetAcls/RestSetAcls.psm1 similarity index 99% rename from RestSetAcls/RestSetAcls.psm1 rename to RestSetAcls/RestSetAcls/RestSetAcls.psm1 index 2b49effc..552f9b37 100644 --- a/RestSetAcls/RestSetAcls.psm1 +++ b/RestSetAcls/RestSetAcls/RestSetAcls.psm1 @@ -322,6 +322,7 @@ function Get-AzureFilePermissionKey { } function Set-AzureFilesAclRecursive { + [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [Microsoft.WindowsAzure.Commands.Storage.AzureStorageContext]$Context, diff --git a/RestSetAcls/SddlUtils.ps1 b/RestSetAcls/RestSetAcls/SddlUtils.ps1 similarity index 100% rename from RestSetAcls/SddlUtils.ps1 rename to RestSetAcls/RestSetAcls/SddlUtils.ps1 diff --git a/RestSetAcls/TestSetup.ps1 b/RestSetAcls/TestSetup.ps1 deleted file mode 100644 index 7c234432..00000000 --- a/RestSetAcls/TestSetup.ps1 +++ /dev/null @@ -1,75 +0,0 @@ -param ( - [Microsoft.WindowsAzure.Commands.Storage.AzureStorageContext]$Context, - [string]$FileShareName, - [string]$BasePath, - [int]$NumberDirs, - [int]$NumberFilesPerDir, - [int]$Depth -) - -function New-Arborescence( - $context, - [string]$fileShareName, - [string]$basePath, - [int]$numberDirs, - [int]$numberFilesPerDir, - [int]$depth -) { - if ($depth -eq 0) - { - # Create file - for ($j = 1; $j -le $numberFilesPerDir; $j++) - { - $fileName = "file-$j.txt" - $filePath = $dirPath + "/" + $fileName - Write-Host "Creating file $filePath" - - # Create file locally - $localFilePath = Join-Path -Path $env:TEMP -ChildPath $fileName - New-Item -Path $localFilePath -Value "Hello world" -ItemType File -Force | Out-Null - - # Upload it - Set-AzStorageFileContent ` - -Context $context ` - -ShareName $FileShareName ` - -Path $filePath ` - -Source $localFilePath ` - -Force - - # Remove local file - Remove-Item -Path $localFilePath - } - } - else - { - for ($i = 1; $i -le $numberDirs; $i++) - { - # Create dir - $dirName = "dir-$i" - $dirPath = $basePath + "/" + $dirName - Write-Host "Creating dir $dirPath" - - New-AzStorageDirectory ` - -Context $context ` - -ShareName $FileShareName ` - -Path $dirPath ` - -ErrorAction SilentlyContinue | Out-Null - - # Recurse inside dir - New-Arborescence ` - -context $context ` - -fileShareName $fileShareName ` - -basePath $dirPath ` - -numberDirs $numberDirs ` - -numberFilesPerDir $numberFilesPerDir ` - -depth ($depth - 1) - } - } -} - -New-Arborescence ` - -context $context ` - -fileShareName $FileShareName ` - -numberDirs $NumberDirs ` - -numberFilesPerDir $NumberFilesPerDir ` - -depth $Depth \ No newline at end of file diff --git a/RestSetAcls/build.depend.psd1 b/RestSetAcls/build.depend.psd1 new file mode 100644 index 00000000..8b10eb78 --- /dev/null +++ b/RestSetAcls/build.depend.psd1 @@ -0,0 +1,13 @@ +# This file lists out development requirements for the module. +# For the runtime requirements, see the module manifest. +@{ + PSDependOptions = @{ + Target = 'bin\Dependencies' + Install = $true + AddToPath = $true + } + + Pester = 'latest' + PSScriptAnalyzer = 'latest' + 'Az.Storage' = '6.0.0' +} \ No newline at end of file diff --git a/RestSetAcls/check-manifest.ps1 b/RestSetAcls/check-manifest.ps1 new file mode 100644 index 00000000..7a68c5a3 --- /dev/null +++ b/RestSetAcls/check-manifest.ps1 @@ -0,0 +1 @@ +Test-ModuleManifest -Path $PSScriptRoot\RestSetAcls\RestSetAcls.psd1 diff --git a/RestSetAcls/init.ps1 b/RestSetAcls/init.ps1 new file mode 100644 index 00000000..531a79c1 --- /dev/null +++ b/RestSetAcls/init.ps1 @@ -0,0 +1,15 @@ +Write-Host "Checking if PSDepend is installed" -ForegroundColor White +$installed = $null -ne (Get-Module PSDepend -ListAvailable) + +if ($installed) { + Write-Host "Already installed" +} else { + Write-Host "Not installed" + Write-Host "`nInstalling PSDepend" -ForegroundColor White + Install-Module -Name PSDepend -Repository PSGallery -Force + Write-Host "Done" +} + +Write-Host "`nInstalling build dependencies" -ForegroundColor White +Invoke-PSDepend -Path $PSScriptRoot\build.depend.psd1 -Force +Write-Host "Done" \ No newline at end of file diff --git a/RestSetAcls/lint.ps1 b/RestSetAcls/lint.ps1 new file mode 100644 index 00000000..ae714d02 --- /dev/null +++ b/RestSetAcls/lint.ps1 @@ -0,0 +1,8 @@ +Invoke-ScriptAnalyzer -Path $PSScriptRoot\RestSetAcls -Severity Warning -Recurse -Outvariable issues +$errors = $issues.Where({$_.Severity -eq 'Error'}) +$warnings = $issues.Where({$_.Severity -eq 'Warning'}) +if ($errors) { + Write-Error "There were $($errors.Count) errors and $($warnings.Count) warnings total." -ErrorAction Stop +} else { + Write-Output "There were $($errors.Count) errors and $($warnings.Count) warnings total." +} diff --git a/RestSetAcls/publish-local.ps1 b/RestSetAcls/publish-local.ps1 new file mode 100644 index 00000000..532222a4 --- /dev/null +++ b/RestSetAcls/publish-local.ps1 @@ -0,0 +1,47 @@ +$repoName = "LocalRepo" +$RepoPath = "$PSScriptRoot\bin\LocalRepo" +$moduleName = "RestSetAcls" +$psd1 = Import-PowershellDataFile -Path "$PSScriptRoot\RestSetAcls\RestSetAcls.psd1" +$dependencies = $psd1.RequiredModules.ModuleName + +Write-Host "Creating $RepoPath" -ForegroundColor White +New-Item -Path $RepoPath -ItemType Directory -Force | Out-Null +Write-Host "Done" -ForegroundColor Gray + +Write-Host "`nRegistering $repoName with $RepoPath" -ForegroundColor White +Register-PSRepository -Name $repoName -SourceLocation $RepoPath -InstallationPolicy Trusted +Write-Host "Done" -ForegroundColor Gray + +Write-Host "`nPublishing $moduleName and dependencies in $repoName" -ForegroundColor White +# Check if dependencies are installed +foreach ($dependency in $dependencies) { + $modules = Get-Module $dependency -ListAvailable + if ($modules.Length -eq 0) { + throw "Module $dependency not found in the current environment. Please run .\init.ps1 first." + } +} + +# Publish dependencies +foreach ($dependency in $dependencies) { + $module = $(Get-Module $dependency -ListAvailable)[0] + Write-Host "Publishing $dependency v$($module.Version) to $repoName" -ForegroundColor Gray + $modulePath = Get-Item $module.Path + Publish-Module -Path $modulePath.Directory.FullName -Repository $repoName +} + +# Publish main module +Write-Host "Publishing $moduleName to $repoName" -ForegroundColor Gray +Publish-Module -Path $PSScriptRoot/$moduleName -Repository $repoName + +Write-Host "Done" -ForegroundColor Gray + +Write-Host "`nUnloading currently loaded modules" -ForegroundColor White +Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue +foreach ($dependency in $dependencies) { + Remove-Module -Name $dependency -Force -ErrorAction SilentlyContinue +} +Write-Host "Done" -ForegroundColor Gray + +Write-Host "`nInstalling $moduleName from $repoName" -ForegroundColor White +Install-Module -Name $moduleName -Repository $repoName -Force +Write-Host "Done" -ForegroundColor Gray diff --git a/RestSetAcls/publish-psgallery.ps1 b/RestSetAcls/publish-psgallery.ps1 new file mode 100644 index 00000000..cecb7668 --- /dev/null +++ b/RestSetAcls/publish-psgallery.ps1 @@ -0,0 +1,14 @@ +[CmdletBinding(SupportsShouldProcess)] +param ( + [Parameter(Mandatory=$true, HelpMessage="PowerShell Gallery API Key")] + [string]$apiKey +) + + +Write-Host "Running tests" -ForegroundColor White -NoNewline +Invoke-Pester -Path $PSScriptRoot\test -Output Minimal +Write-Host + +# This will also run Test-ModuleManifest +Write-Host "Publishing" -ForegroundColor White +Publish-Module -Path $PSScriptRoot\RestSetAcls -NuGetApiKey $apiKey -WhatIf:$WhatIfPreference diff --git a/RestSetAcls/test.ps1 b/RestSetAcls/test.ps1 new file mode 100644 index 00000000..aa2ef83f --- /dev/null +++ b/RestSetAcls/test.ps1 @@ -0,0 +1 @@ +Invoke-Pester -Path $PSScriptRoot\test -Output Detailed \ No newline at end of file diff --git a/RestSetAcls/SddlUtils.Tests.ps1 b/RestSetAcls/test/SddlUtils.Tests.ps1 similarity index 99% rename from RestSetAcls/SddlUtils.Tests.ps1 rename to RestSetAcls/test/SddlUtils.Tests.ps1 index bbb4d2c9..7a5a84b2 100644 --- a/RestSetAcls/SddlUtils.Tests.ps1 +++ b/RestSetAcls/test/SddlUtils.Tests.ps1 @@ -1,5 +1,5 @@ BeforeAll { - . $PSScriptRoot/SddlUtils.ps1 + . $PSScriptRoot/../RestSetAcls/SddlUtils.ps1 } Describe "ConvertTo-RawSecurityDescriptor" { diff --git a/RestSetAcls/test/TestSetup.ps1 b/RestSetAcls/test/TestSetup.ps1 new file mode 100644 index 00000000..1d91298f --- /dev/null +++ b/RestSetAcls/test/TestSetup.ps1 @@ -0,0 +1,75 @@ +function New-Arborescence +{ + [CmdletBinding(SupportsShouldProcess)] + param ( + [Microsoft.WindowsAzure.Commands.Storage.AzureStorageContext]$context, + [string]$FileShareName, + [string]$BasePath, + [int]$NumberDirs, + [int]$NumberFilesPerDir, + [int]$Depth + ) + + if ($Depth -eq 0) + { + # Create file + for ($j = 1; $j -le $NumberFilesPerDir; $j++) + { + $fileName = "file-$j.txt" + $filePath = $BasePath + "/" + $fileName + $localFilePath = Join-Path -Path $env:TEMP -ChildPath $fileName + + if ($WhatIfPreference) { + Write-Host "WhatIf: Creating file $filePath" + } else { + Write-Host "Creating file $filePath" + + # Create file locally + New-Item ` + -Path $localFilePath ` + -Value "Hello world" ` + -ItemType File ` + -Force | Out-Null + + # Upload it + Set-AzStorageFileContent ` + -Context $Context ` + -ShareName $FileShareName ` + -Path $FilePath ` + -Source $LocalFilePath ` + -Force + + # Remove local file + Remove-Item -Path $localFilePath + } + } + } + else + { + for ($i = 1; $i -le $NumberDirs; $i++) + { + # Create dir + $dirPath = "${BasePath}/dir-$i" + + if ($WhatIfPreference) { + Write-Host "WhatIf: Creating dir $dirPath" + } else { + Write-Host "Creating dir $dirPath" + New-AzStorageDirectory ` + -Context $Context ` + -ShareName $FileShareName ` + -Path $dirPath ` + -ErrorAction SilentlyContinue | Out-Null + } + + # Recurse inside dir + New-Arborescence ` + -Context $Context ` + -FileShareName $FileShareName ` + -BasePath $dirPath ` + -NumberDirs $NumberDirs ` + -NumberFilesPerDir $NumberFilesPerDir ` + -Depth ($Depth - 1) + } + } +} diff --git a/RestSetAcls/unpublish-local.ps1 b/RestSetAcls/unpublish-local.ps1 new file mode 100644 index 00000000..0bbc6f06 --- /dev/null +++ b/RestSetAcls/unpublish-local.ps1 @@ -0,0 +1,32 @@ +$repoName = "LocalRepo" +$repoPath = "$PSScriptRoot\bin\LocalRepo" +$moduleName = "RestSetAcls" +$psd1 = Import-PowershellDataFile -Path "$PSScriptRoot\RestSetAcls\RestSetAcls.psd1" +$dependencies = $psd1.RequiredModules.ModuleName + +function Uninstall-LocalRepoModule { + param ( + [string]$moduleName, + [string]$repoName + ) + Get-InstalledModule -Name $moduleName | Where-Object { $_.Repository -eq $repoName } | ForEach-Object { + Write-Host "Uninstalling $moduleName v$($_.Version) from $repoName" -ForegroundColor Gray + Uninstall-Module -Name $moduleName -RequiredVersion $_.Version -Force + } +} + +Write-Host "Unloading modules" -ForegroundColor White +Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue +$dependencies | ForEach-Object { Remove-Module -Name $_ -Force -ErrorAction SilentlyContinue } +Write-Host "Done" -ForegroundColor Gray + +Write-Host "`nUninstalling $moduleName" -ForegroundColor White +Uninstall-LocalRepoModule -moduleName $moduleName -repoName $repoName +Write-Host "Done" -ForegroundColor Gray + +Write-Host "`nUnregistering LocalRepo" -ForegroundColor White +Unregister-PSRepository -Name LocalRepo + +Write-Host "`nRemoving LocalRepo" -ForegroundColor White +Remove-Item -Path $RepoPath -Recurse -Force +Write-Host "Done" -ForegroundColor Gray \ No newline at end of file