From dbc460703462401753ec496953d46671bb3a784b Mon Sep 17 00:00:00 2001 From: Michael Zanatta Date: Wed, 17 Jan 2024 13:00:27 +1000 Subject: [PATCH 1/7] adding docker support (#1) Co-authored-by: Michael Zanatta --- .devcontainer/devcontainer.json | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..96950a109 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,39 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/powershell +{ + "name": "PowerShell", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/powershell:lts-debian-11", + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": "true", + "username": "vscode", + "upgradePackages": "false", + "nonFreePackages": "true" + } + }, + + "postCreateCommand": "sudo chsh vscode -s \"$(which pwsh)\"", + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.defaultProfile.linux": "pwsh" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-vscode.powershell" + ] + } + } + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} From afecb3e23f546d17c4cc7d88d17fc6266a42c73d Mon Sep 17 00:00:00 2001 From: Michael Zanatta Date: Mon, 29 Jan 2024 14:34:35 +1000 Subject: [PATCH 2/7] BREAKING CHANGE - CORE 'Add Azure DevOps Managed Identity Support' (#2) * Adding Initial Class * Creating ManagedIdentityToken class. Expanding ManagedIdentityClass Updated Invoke-AZDevOpsAPIRestMethod with ManagedIdentity Handeler * Refactoring 004.AzManagedIdentity into Functions Added APIRateLimit Enum * Renaming DataResources Updating AzureDevOpsDsc.Common.psd1 Initial ompleted Managed Identity Cmdlets. * Adding Initial Tests * Fixing Documentation Adding Unit Tests * Moving Tests Completed ManagedIdentityToken Test * Completed APIRateLimit Unit Testing * Bug Fixes on Azure VM to get Managed Identity Working * Updating Tests * Fixing Tests * Test Updates * Updating Paths * Update Paths * Bug fixes with build and testing process * Fixing Headers * Bug Fixes with the Token * Bug Fixes * More bug fixes * Fixing Bugs * Updated Documentation and ChangeLog to bring into line with guidelines. --------- Co-authored-by: Michael Zanatta --- CHANGELOG.md | 3 + .../000.ManagedIdentityDataResources.ps1 | 16 ++ .../Classes/003.AzDevOpsDscResourceBase.ps1 | 1 + source/Classes/004.ManagedIdentityToken.ps1 | 106 ++++++++++++ source/Classes/005.APIRateLimit.ps1 | 51 ++++++ .../4-AddProject-UsingManaged-Identity.ps1 | 43 +++++ .../Private/Get-AzDevOpsApiVersion.ps1 | 6 +- .../Private/Get-AzManagedIdentityToken.ps1 | 65 ++++++++ .../Private/Invoke-AzDevOpsApiRestMethod.ps1 | 80 ++++++++- .../Test-AzDevOpsApiHttpRequestHeader.ps1 | 11 +- .../Private/Test-AzManagedIdentityToken.ps1 | 51 ++++++ .../Private/Update-AzManagedIdentity.ps1 | 31 ++++ .../AzureDevOpsDsc.Common.psd1 | 6 +- .../AzureDevOpsDsc.Common.psm1 | 1 + .../Public/New-AzManagedIdentity.ps1 | 33 ++++ .../ManagedIdentity/APIRateLimit.tests.ps1 | 87 ++++++++++ .../ManagedIdentityToken.tests.ps1 | 96 +++++++++++ .../Invoke-AzDevOpsApiRestMethod.Tests.ps1 | 155 ++++++++++++++++++ ...ureDevOpsDsc.Common.TestInitialization.ps1 | 31 +++- .../Get-AzManagedIdentityToken.Tests.ps1 | 72 ++++++++ .../Private/New-AzDevOpsApiResource.Tests.ps1 | 9 + .../Private/New-AzManagedIdentity.Tests.ps1 | 53 ++++++ .../Test-AzManagedIdentityToken.Tests.ps1 | 59 +++++++ .../Update-AzManagedIdentity.Tests.ps1 | 28 ++++ .../Modules/TestHelpers/CommonTestHelper.psm1 | 19 +++ 25 files changed, 1098 insertions(+), 15 deletions(-) create mode 100644 source/Classes/000.ManagedIdentityDataResources.ps1 create mode 100644 source/Classes/004.ManagedIdentityToken.ps1 create mode 100644 source/Classes/005.APIRateLimit.ps1 create mode 100644 source/Examples/Resources/AzDevOpsProject/4-AddProject-UsingManaged-Identity.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzManagedIdentityToken.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzManagedIdentityToken.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Update-AzManagedIdentity.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzManagedIdentity.ps1 create mode 100644 tests/Unit/Classes/ManagedIdentity/APIRateLimit.tests.ps1 create mode 100644 tests/Unit/Classes/ManagedIdentity/ManagedIdentityToken.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Get-AzManagedIdentityToken.Tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzDevOpsApiResource.Tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzManagedIdentity.Tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzManagedIdentityToken.Tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Update-AzManagedIdentity.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index d480d6506..e8c637ab7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - AzureDevOpsDsc + - Azure Managed Identity supporting classes. These classes are used by `AzureDevOpsDsc.Common`. - Updated pipeline files to support change of default branch to main. - Added GitHub issue templates and pull request template ([issue #1](https://github.com/dsccommunity/AzureDevOpsDsc/issues/1)) @@ -29,7 +30,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 file `Home.md` will be updated with the correct module version on each publish to gallery (including preview). - AzureDevOpsDsc.Common + - Managed Identity has been added to the system. This feature can be used before invoking Invoke-DSCResource. With New-AzManagedIdentity, the bearer token is automatically authenticated, retrieved, and managed. It’s worth noting that bearer tokens take precedence over basic tokens. When using Invoke-AzDevOpsApiRestMethod, the token is automatically interpolated as required. - Added 'wrapper' functionality around the [Azure DevOps REST API](https://docs.microsoft.com/en-us/rest/api/azure/devops/) + - Added Supporting Functions for Azure Managed Identity. ### Changed diff --git a/source/Classes/000.ManagedIdentityDataResources.ps1 b/source/Classes/000.ManagedIdentityDataResources.ps1 new file mode 100644 index 000000000..4da713ddc --- /dev/null +++ b/source/Classes/000.ManagedIdentityDataResources.ps1 @@ -0,0 +1,16 @@ +data AzManagedIdentityLocalizedData { + + ConvertFrom-StringData @' + Global_AzureDevOps_Resource_Id=499b84ac-1321-427f-aa17-267ca6975798 + Global_Url_Azure_Instance_Metadata_Url=http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource={0} + Global_API_Azure_DevOps_Version=api-version=6.0 + Global_Url_AZDO_Project=https://dev.azure.com/{0}/_apis/projects + Error_ManagedIdentity_RestApiCallFailed=Error. Failed to call the Azure Instance Metadata Service. Please ensure that the Azure Instance Metadata Service is available. Error Details: {0} + Error_Azure_Instance_Metadata_Service_Missing_Token=Error. Access token not returned from Azure Instance Metadata Service. Please ensure that the Azure Instance Metadata Service is available. + Error_Azure_API_Call_Generic=Error. Failed to call the Azure DevOps API. Details: {0} + Error_Azure_Get_AzManagedIdentity_Invalid_Caller=Error. Get-AzManagedIdentity can only be called from New-AzManagedIdentity or Update-AzManagedIdentity. +'@ + +} + +New-Variable -Name AzManagedIdentityLocalizedData -Value $AzManagedIdentityLocalizedData -Option ReadOnly -Scope Global -Force diff --git a/source/Classes/003.AzDevOpsDscResourceBase.ps1 b/source/Classes/003.AzDevOpsDscResourceBase.ps1 index ccfa36b98..81df0d005 100644 --- a/source/Classes/003.AzDevOpsDscResourceBase.ps1 +++ b/source/Classes/003.AzDevOpsDscResourceBase.ps1 @@ -11,6 +11,7 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase [DscProperty()] [Alias('PersonalAccessToken')] + [Alias('AccessToken')] [System.String] $Pat diff --git a/source/Classes/004.ManagedIdentityToken.ps1 b/source/Classes/004.ManagedIdentityToken.ps1 new file mode 100644 index 000000000..c5a2c5d5c --- /dev/null +++ b/source/Classes/004.ManagedIdentityToken.ps1 @@ -0,0 +1,106 @@ + + +Class ManagedIdentityToken { + + [SecureString]$access_token + [DateTime]$expires_on + [Int]$expires_in + [String]$resource + [String]$token_type + hidden [bool]$linux = $IsLinux + + # Constructor + ManagedIdentityToken([PSCustomObject]$ManagedIdentityTokenObj) { + + # Validate that ManagedIdentityTokenObj is a HashTable and Contains the correct keys + if (-not $this.isValid($ManagedIdentityTokenObj)) { throw "The ManagedIdentityTokenObj is not valid." } + + $epochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) + + # Set the properties of the class + $this.access_token = $ManagedIdentityTokenObj.access_token | ConvertTo-SecureString -AsPlainText -Force + $this.expires_on = $epochStart.AddSeconds($ManagedIdentityTokenObj.expires_on) + $this.expires_in = $ManagedIdentityTokenObj.expires_in + $this.resource = $ManagedIdentityTokenObj.resource + $this.token_type = $ManagedIdentityTokenObj.token_type + + } + + # Function to validate the ManagedIdentityTokenObj + Hidden [Bool]isValid($ManagedIdentityTokenObj) { + + # Assuming these are the keys we expect in the hashtable + $expectedKeys = @('access_token', 'expires_on', 'expires_in', 'resource', 'token_type') + + # Check if all expected keys exist in the hashtable + foreach ($key in $expectedKeys) { + if (-not $ManagedIdentityTokenObj."$key") { + Write-Verbose "[ManagedIdentityToken] The hashtable does not contain the expected property: $key" + return $false + } + } + + # If all checks pass, return true + Write-Verbose "[ManagedIdentityToken] The hashtable is valid and contains all the expected keys." + return $true + } + + # Function to convert a SecureString to a String + hidden [String]ConvertFromSecureString([SecureString]$SecureString) { + # Convert a SecureString to a String + $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString) + $String = ($this.linux) ? [System.Runtime.InteropServices.Marshal]::PtrToStringUni($BSTR) : + [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) + [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) + return $String + } + + # Function to test the call stack + hidden [Bool]TestCallStack([String]$name) { + + $CallStack = Get-PSCallStack + + # Check if any of the callers in the call stack is Invoke-DSCResource + foreach ($stackFrame in $callStack) { + if ($stackFrame.Command -eq $name) { + Write-Verbose "[ManagedIdentityToken] The calling function is $name." + return $true + } + } + + return $false + + } + + [Bool]isExpired() { + # Remove 10 seconds from the expires_on time to account for clock skew. + if ($this.expires_on.AddSeconds(-10) -lt (Get-Date)) { return $true } + return $false + } + + # Return the access token + [String] Get() { + + # Prevent Execution and Writing to Files and Pipeline Variables. + + # Token can only be called within Invoke-AzDevOpsApiRestMethod. Test to see if the calling function is Invoke-AzDevOpsApiRestMethod + if (-not($this.TestCallStack('Invoke-AzDevOpsApiRestMethod'))) { throw "[ManagedIdentityToken] The Get() method can only be called within Invoke-AzDevOpsApiRestMethod." } + # Token cannot be returned within a Write-* function. Test to see if the calling function is Write-* + if ($this.TestCallStack('Write-')) { throw "[ManagedIdentityToken] The Get() method cannot be called within a Write-* function." } + # Token cannot be written to a file. Test to see if the calling function is Out-File + if ($this.TestCallStack('Out-File')) { throw "[ManagedIdentityToken] The Get() method cannot be called within Out-File." } + + # Return the access token + return ($this.ConvertFromSecureString($this.access_token)) + + } + +} + +# Function to create a new ManagedIdentityToken object +Function global:New-ManagedIdentityToken ([hashtable]$ManagedIdentityTokenObj) { + + # Create and return a new ManagedIdentityToken object + return [ManagedIdentityToken]::New($ManagedIdentityTokenObj) + +} diff --git a/source/Classes/005.APIRateLimit.ps1 b/source/Classes/005.APIRateLimit.ps1 new file mode 100644 index 000000000..5887f9be0 --- /dev/null +++ b/source/Classes/005.APIRateLimit.ps1 @@ -0,0 +1,51 @@ +Class APIRateLimit { + + [Int]$retryAfter = 0 + [Int]$xRateLimitRemaining = 0 + [Int]$xRateLimitReset = 0 + + # Constructor + APIRateLimit([HashTable]$APIRateLimitObj) { + + # Validate that APIRateLimitObj is a HashTable and Contains the correct keys + if (-not $this.isValid($APIRateLimitObj)) { throw "The APIRateLimitObj is not valid." } + + # Convert X-RateLimit-Reset from Unix Time to DateTime + $epochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) + + # Set the properties of the class + $this.retryAfter = [Int]($APIRateLimitObj.'Retry-After') + $this.XRateLimitRemaining = [Int]$APIRateLimitObj.'X-RateLimit-Remaining' + $this.XRateLimitReset = [Int]($APIRateLimitObj.'X-RateLimit-Reset') + + } + + # Constructor with retryAfter Parameters + APIRateLimit($retryAfter) { + + # Set the properties of the class + $this.retryAfter = [int]$retryAfter + + } + + Hidden [Bool]isValid($APIRateLimitObj) { + + # Assuming these are the keys we expect in the hashtable + $expectedKeys = @('Retry-After', 'X-RateLimit-Remaining', 'X-RateLimit-Reset') + + # Check if all expected keys exist in the hashtable + foreach ($key in $expectedKeys) { + if (-not $APIRateLimitObj.ContainsKey($key)) { + Write-Error "[APIRateLimit] The hashtable does not contain the expected key: $key" + return $false + } + } + + # If all checks pass, return true + Write-Verbose "[APIRateLimit] The hashtable is valid and contains all the expected keys." + return $true + + } + + +} diff --git a/source/Examples/Resources/AzDevOpsProject/4-AddProject-UsingManaged-Identity.ps1 b/source/Examples/Resources/AzDevOpsProject/4-AddProject-UsingManaged-Identity.ps1 new file mode 100644 index 000000000..3e140392d --- /dev/null +++ b/source/Examples/Resources/AzDevOpsProject/4-AddProject-UsingManaged-Identity.ps1 @@ -0,0 +1,43 @@ + +<# + .DESCRIPTION + This example shows how to ensure that the Azure DevOps project + called 'Test Project' exists (or is added if it does not exist). + This example uses Invoke-DSCResource to authenticate to Azure DevOps using a Managed Identity. +#> + +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [string] + $ApiUri + ) + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDevOpsProject 'AddProject' + { + Ensure = 'Present' + + ApiUri = $ApiUri + Pat = $Pat + + ProjectName = 'Test Project' + ProjectDescription = 'A Test Project' + + SourceControlType = 'Git' + } + + } +} + +# Create a new Azure Managed Identity and store the token in a global variable +New-AzManagedIdentity -OrganizationName "Contoso" + +# Using Invoke-DSCResource, invoke the 'Test' method of the 'AzDevOpsProject' resource. +# The global variable will be used to authenticate to Azure DevOps. +Invoke-DscResource -Name Example -Method Test -Verbose diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.ps1 index 370c89943..f867af536 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.ps1 @@ -19,15 +19,15 @@ function Get-AzDevOpsApiVersion $Default ) - [string]$defaultApiVersion = '6.0' + [string]$defaultApiVersion = '6.1' [string[]]$apiVersions = @( #'4.0', # Not supported #'5.0', # Not supported #'5.1', # Not supported - '6.0' - #'6.1', # Not supported + '6.0', + '6.1' ) diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzManagedIdentityToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzManagedIdentityToken.ps1 new file mode 100644 index 000000000..3f60ccd20 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzManagedIdentityToken.ps1 @@ -0,0 +1,65 @@ +<# +.SYNOPSIS +Obtains a managed identity token from Azure AD. + +.DESCRIPTION +The Get-AzManagedIdentityToken function is used to obtain an access token from Azure AD using a managed identity. It can only be called from the New-AzManagedIdentity or Update-AzManagedIdentity functions. + +.PARAMETER OrganizationName +Specifies the name of the organization. + +.PARAMETER Verify +Specifies whether to verify the connection. If this switch is not set, the function returns the managed identity token. If the switch is set, the function tests the connection and returns the access token. + +.EXAMPLE +Get-AzManagedIdentityToken -OrganizationName "Contoso" -Verify +Obtains the access token for the managed identity associated with the organization "Contoso" and verifies the connection. + +.NOTES +This function does not require the Azure PowerShell module. +#> + +Function Get-AzManagedIdentityToken { + [CmdletBinding()] + param ( + # Organization Name + [Parameter(Mandatory)] + [String] + $OrganizationName, + + # Verify the Connection + [Parameter()] + [Switch] + $Verify + ) + + # Obtain the access token from Azure AD using the Managed Identity + + $ManagedIdentityParams = @{ + # Define the Azure instance metadata endpoint to get the access token + Uri = $AzManagedIdentityLocalizedData.Global_Url_Azure_Instance_Metadata_Url -f $AzManagedIdentityLocalizedData.Global_AzureDevOps_Resource_Id + Method = 'Get' + Headers = @{Metadata="true"} + ContentType = 'Application/json' + } + + # Invoke the RestAPI + try { $response = Invoke-AzDevOpsApiRestMethod @ManagedIdentityParams } catch { Throw $_ } + # Test the response + if ($null -eq $response.access_token) { throw $AzManagedIdentityLocalizedData.Error_Azure_Instance_Metadata_Service_Missing_Token } + + # TypeCast the response to a ManagedIdentityToken object + $ManagedIdentity = New-ManagedIdentityToken -ManagedIdentityTokenObj $response + # Null the response + $null = $response + + # Return the token if the verify switch is not set + if (-not($verify)) { return $ManagedIdentity } + + # Test the Connection + if (-not(Test-AzManagedIdentityToken $ManagedIdentity)) { throw $AzManagedIdentityLocalizedData.Error_Azure_API_Call_Generic } + + # Return the AccessToken + return ($ManagedIdentity) + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.ps1 index d2b147534..2f1d458bd 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.ps1 @@ -89,11 +89,12 @@ function Invoke-AzDevOpsApiRestMethod ) $invokeRestMethodParameters = @{ - Uri = $ApiUri - Method = $HttpMethod - Headers = $HttpHeaders - Body = $HttpBody - ContentType = $HttpContentType + Uri = $ApiUri + Method = $HttpMethod + Headers = $HttpHeaders + Body = $HttpBody + ContentType = $HttpContentType + ResponseHeadersVariable = 'responseHeaders' } # Remove the 'Body' and 'ContentType' if not relevant to request @@ -108,12 +109,79 @@ function Invoke-AzDevOpsApiRestMethod while ($CurrentNoOfRetryAttempts -lt $RetryAttempts) { + + # + # Slow down the retry attempts if the API resource is close to being overwelmed + + # If there are any retry attempts, wait for the specified number of seconds before retrying + if ($Global:DSCAZDO_APIRateLimit.retryAfter -ge 0) + { + Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] Waiting for {0} seconds before retrying." -f $Global:DSCAZDO_APIRateLimit.retryAfter) + Start-Sleep -Seconds $Global:DSCAZDO_APIRateLimit.retryAfter + } + + # If the API resouce is close to beig overwelmed, wait for the specified number of seconds before sending the request + if (($Global:DSCAZDO_APIRateLimit.xRateLimitRemaining -le 50) -and ($Global:DSCAZDO_APIRateLimit.xRateLimitRemaining -ge 5)) + { + Write-Verbose -Message "[Invoke-AzDevOpsApiRestMethod] Resource is close to being overwelmed. Waiting for $RetryIntervalMs seconds before sending the request." + Start-Sleep -Milliseconds $RetryIntervalMs + } + # If the API resouce is overwelmed, wait for the specified number of seconds before sending the request + elseif ($Global:DSCAZDO_APIRateLimit.xRateLimitRemaining -lt 5) + { + Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] Resource is overwelmed. Waiting for {0} seconds to reset the TSTUs." -f $Global:DSCAZDO_APIRateLimit.xRateLimitReset) + Start-Sleep -Milliseconds $RetryIntervalMs + } + + # + # Test if a Managed Identity Token is required and if so, add it to the HTTP Headers + if ($Global:DSCAZDO_ManagedIdentityToken -ne $null) + { + # Test if the Managed Identity Token has expired + if ($Global:DSCAZDO_ManagedIdentityToken.isExpired()) + { + # If so, get a new token + $Global:DSCAZDO_ManagedIdentityToken = Update-AzManagedIdentityToken -OrganizationName $Global:DSCAZDO_OrganizationName + } + + # Add the Managed Identity Token to the HTTP Headers + $invokeRestMethodParameters.Headers.Authorization = 'Bearer {0}' -f $Global:DSCAZDO_ManagedIdentityToken.Get() + } + + + # + # Invoke the REST method + try { - return Invoke-RestMethod @invokeRestMethodParameters + $result = Invoke-RestMethod @invokeRestMethodParameters + + # Update + $Global:DSCAZDO_APIRateLimit = $null + return $result + } catch { + + # Check to see if it is an HTTP 429 (Too Many Requests) error + if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::TooManyRequests) + { + # If so, wait for the specified number of seconds before retrying + $retryAfter = $_.Exception.Response.Headers.'Retry-After' + if ($retryAfter) + { + $retryAfter = [int]$retryAfter + Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $retryAfter seconds before retrying." + $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($_.Exception.Response.Headers) + } else { + # If the Retry-After header is not present, wait for the specified number of milliseconds before retrying + Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $RetryIntervalMs milliseconds before retrying." + $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($RetryIntervalMs) + } + + } + # Increment the number of retries attempted and obtain any exception message $CurrentNoOfRetryAttempts++ $restMethodExceptionMessage = $_.Exception.Message diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.ps1 index f52ff871b..531c39f37 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.ps1 @@ -5,6 +5,8 @@ NOTE: Use of the '-IsValid' switch is required. + PAT Tokens and Managed Identity Tokens are allowed. + .PARAMETER HttpRequestHeader The 'HttpRequestHeader' to be tested/validated. @@ -36,7 +38,14 @@ function Test-AzDevOpsApiHttpRequestHeader $IsValid ) + # if Metadata is specifed within the header then it is a Managed Identity Token request + # and is valid. + + if ($HttpRequestHeader.Metadata) { return $true } + + # Otherwise, if the header is not valid, retrun false + return !($null -eq $HttpRequestHeader -or $null -eq $HttpRequestHeader.Authorization -or - $HttpRequestHeader.Authorization -inotlike 'Basic *') + $HttpRequestHeader.Authorization -match '^(Basic|Bearer):\s.+$') } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzManagedIdentityToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzManagedIdentityToken.ps1 new file mode 100644 index 000000000..79c97b483 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzManagedIdentityToken.ps1 @@ -0,0 +1,51 @@ +<# +.SYNOPSIS + Tests the Azure Managed Identity token for accessing Azure DevOps REST API. + +.DESCRIPTION + The Test-AzManagedIdentityToken function is used to test the Azure Managed Identity token for accessing the Azure DevOps REST API. + It calls the Azure DevOps REST API with the provided Managed Identity token and returns true if the token is valid, otherwise returns false. + +.PARAMETER ManagedIdentity + Specifies the Managed Identity token to be tested. + +.EXAMPLE + $token = Get-AzManagedIdentityToken -ResourceId 'https://management.azure.com' + $isValid = Test-AzManagedIdentityToken -ManagedIdentity $token + if ($isValid) { + Write-Host "Token is valid." + } else { + Write-Host "Token is invalid." + } +#> + +Function Test-AzManagedIdentityToken { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [Object] + $ManagedIdentity + ) + + # Define the Azure DevOps REST API endpoint to get the list of projects + $AZDOProjectUrl = $AzManagedIdentityLocalizedData.Global_Url_AZDO_Project -f $GLOBAL:DSCAZDO_OrganizationName + $FormattedUrl = "{0}?api-version=7.2-preview.4" -f $AZDOProjectUrl + + $params = @{ + Uri = $FormattedUrl + Method = 'Get' + Headers = @{ + Authorization ="Bearer {0}" -f $ManagedIdentity.Get() + } + } + + # Call the Azure DevOps REST API with the Managed Identity Bearer token + try { + $null = Invoke-AzDevOpsApiRestMethod @params + } catch { + return $false + } + + return $true + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Update-AzManagedIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Update-AzManagedIdentity.ps1 new file mode 100644 index 000000000..4a7ae4333 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Update-AzManagedIdentity.ps1 @@ -0,0 +1,31 @@ +<# +.SYNOPSIS +Updates the Azure Managed Identity. + +.DESCRIPTION +This function updates the Azure Managed Identity by refreshing the token. + +.PARAMETER OrganizationName +The name of the organization associated with the Managed Identity. + +.EXAMPLE +Update-AzManagedIdentity -OrganizationName "Contoso" + +This example updates the Azure Managed Identity for the organization named "Contoso". + +#> + +Function Update-AzManagedIdentity { + + # Test if the Global Var's Exist $Global:DSCAZDO_OrganizationName + if ($null -eq $Global:DSCAZDO_OrganizationName) { + Throw "[Update-AzManagedIdentity] Organization Name is not set. Please run 'New-AzManagedIdentity -OrganizationName '" + } + + # Clear the existing token. + $Global:DSCAZDO_ManagedIdentityToken = $null + + # Refresh the Token. + $Global:DSCAZDO_ManagedIdentityToken = Get-AzManagedIdentityToken -OrganizationName $Global:DSCAZDO_OrganizationName + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 index f10821fd2..cc2f6cc0e 100644 --- a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 +++ b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 @@ -22,6 +22,8 @@ # 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 = @( + 'New-AzManagedIdentity', + 'Get-AzDevOpsServicesUri', 'Get-AzDevOpsServicesApiUri', @@ -32,7 +34,9 @@ 'New-AzDevOpsProject', 'Set-AzDevOpsProject', 'Remove-AzDevOpsProject', - 'Test-AzDevOpsProject' + 'Test-AzDevOpsProject', + + 'New-AzManagedIdentity' ) # 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. diff --git a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 index f47986a47..71e157ea2 100644 --- a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 +++ b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 @@ -1,3 +1,4 @@ +#using module AzureDevOpsDsc # Setup/Import 'DscResource.Common' helper module #$script:resourceHelperModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common' #Import-Module -Name $script:resourceHelperModulePath diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzManagedIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzManagedIdentity.ps1 new file mode 100644 index 000000000..c18ce9d77 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzManagedIdentity.ps1 @@ -0,0 +1,33 @@ +<# +.SYNOPSIS +Creates a new Azure Managed Identity. + +.DESCRIPTION +The New-AzManagedIdentity function creates a new Azure Managed Identity for use in Azure DevOps DSC. + +.PARAMETER OrganizationName +Specifies the name of the organization associated with the Azure Managed Identity. + +.EXAMPLE +New-AzManagedIdentity -OrganizationName "Contoso" + +This example creates a new Azure Managed Identity for the organization named "Contoso". + +#> +Function New-AzManagedIdentity { + + [CmdletBinding()] + param ( + [Parameter()] + [Alias('OrgName')] + [String] + $OrganizationName + ) + + $Global:DSCAZDO_OrganizationName = $OrganizationName + $Global:DSCAZDO_ManagedIdentityToken = $null + + # If the Token is not Valid. Get a new Token. + $Global:DSCAZDO_ManagedIdentityToken = Get-AzManagedIdentityToken -OrganizationName $OrganizationName -Verify + +} diff --git a/tests/Unit/Classes/ManagedIdentity/APIRateLimit.tests.ps1 b/tests/Unit/Classes/ManagedIdentity/APIRateLimit.tests.ps1 new file mode 100644 index 000000000..8307e59d0 --- /dev/null +++ b/tests/Unit/Classes/ManagedIdentity/APIRateLimit.tests.ps1 @@ -0,0 +1,87 @@ +<# +.SYNOPSIS + Test suite for the APIRateLimit class. + +.DESCRIPTION + This test suite validates the functionality of the APIRateLimit class, ensuring it properly handles valid and invalid inputs. +#> + +# Initialize tests for module function +. $PSScriptRoot\..\Classes.TestInitialization.ps1 + +InModuleScope 'AzureDevOpsDsc' { + + Describe "APIRateLimit Class Tests" { + + It "Throws an exception when initialized with an invalid HashTable (missing keys)" { + # Arrange + $invalidHashTable = @{ 'Retry-After' = 120 } + # Act / Assert + { [APIRateLimit]::new($invalidHashTable) } | Should -Throw "The APIRateLimitObj is not valid." + } + + It "Does not throw an exception when initialized with a valid HashTable" { + # Arrange + $validHashTable = @{ + 'Retry-After' = 120 + 'X-RateLimit-Remaining' = 10 + 'X-RateLimit-Reset' = 1583000000 + } + # Act / Assert + { [APIRateLimit]::new($validHashTable) } | Should -Not -Throw + } + + It "Correctly sets the properties when initialized with a valid HashTable" { + # Arrange + $validHashTable = @{ + 'Retry-After' = 120 + 'X-RateLimit-Remaining' = 10 + 'X-RateLimit-Reset' = 1583000000 + } + $expectedEpochTime = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc).AddSeconds(1583000000) + # Act + $apiRateLimit = [APIRateLimit]::new($validHashTable) + # Assert + $apiRateLimit.retryAfter | Should -Be 120 + $apiRateLimit.XRateLimitRemaining | Should -Be 10 + $apiRateLimit.XRateLimitReset | Should -Be 1583000000 + } + + It "Initializes with only retryAfter parameter correctly" { + # Arrange + $retryAfterValue = 300 + # Act + $apiRateLimit = [APIRateLimit]::new($retryAfterValue) + # Assert + $apiRateLimit.retryAfter | Should -Be $retryAfterValue + $apiRateLimit.XRateLimitRemaining | Should -Be 0 + $apiRateLimit.XRateLimitReset | Should -Be 0 + } + + It "isValid method returns false when HashTable is missing keys" { + # Arrange + $incompleteHashTable = @{ 'Retry-After' = 120 } + $apiRateLimit = [APIRateLimit]::new(0) + # Act + $result = $apiRateLimit.isValid($incompleteHashTable) + # Assert + $result | Should -Be $false + } + + It "isValid method returns true when HashTable has all required keys" { + # Arrange + $completeHashTable = @{ + 'Retry-After' = 120 + 'X-RateLimit-Remaining' = 10 + 'X-RateLimit-Reset' = 1583000000 + } + $apiRateLimit = [APIRateLimit]::new(0) + # Act + $result = $apiRateLimit.isValid($completeHashTable) + # Assert + $result | Should -Be $true + } + } + +} +# Note: To execute this test suite, save it to a file named 'APIRateLimit.Tests.ps1' and run it using Pester. diff --git a/tests/Unit/Classes/ManagedIdentity/ManagedIdentityToken.tests.ps1 b/tests/Unit/Classes/ManagedIdentity/ManagedIdentityToken.tests.ps1 new file mode 100644 index 000000000..9512b78d1 --- /dev/null +++ b/tests/Unit/Classes/ManagedIdentity/ManagedIdentityToken.tests.ps1 @@ -0,0 +1,96 @@ +# Initialize tests for module function + +. $PSScriptRoot\..\Classes.TestInitialization.ps1 + +<# +.SYNOPSIS + Test suite for the ManagedIdentityToken class. + +.DESCRIPTION + This test suite validates the functionality of the ManagedIdentityToken class, ensuring it handles various scenarios correctly. +#> + +InModuleScope 'AzureDevOpsDsc' { + + Describe "ManagedIdentityToken Class Tests" { + + It "Throws an exception when invalid token object is provided" { + { [ManagedIdentityToken]::new(@{}) } | Should -Throw "The ManagedIdentityTokenObj is not valid." + } + + It "Creates a ManagedIdentityToken object with valid properties" { + # Arrange + $tokenProperties = @{ + access_token = "test_access_token" + expires_on = 3600 # Assuming this is seconds since epoch + expires_in = 3600 + resource = "https://my.resource.com" + token_type = "Bearer" + } + # Act + $tokenObject = [ManagedIdentityToken]::new((New-Object PSCustomObject -Property $tokenProperties)) + # Assert + $tokenObject.access_token | Should -Not -BeNullOrEmpty + $tokenObject.expires_on | Should -BeOfType [DateTime] + $tokenObject.expires_in | Should -Be 3600 + $tokenObject.resource | Should -Be "https://my.resource.com" + $tokenObject.token_type | Should -Be "Bearer" + } + + It "Determines if a token is expired" { + # Arrange + $tokenProperties = @{ + access_token = "test_access_token" + expires_on = (Get-Date).AddSeconds(-20).ToUniversalTime().Subtract([datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc)).TotalSeconds + expires_in = 3600 + resource = "https://my.resource.com" + token_type = "Bearer" + } + $tokenObject = [ManagedIdentityToken]::new((New-Object PSCustomObject -Property $tokenProperties)) + # Act / Assert + $tokenObject.isExpired() | Should -BeTrue + } + + It "Gets the access token when called from an allowed method" { + Mock Get-PSCallStack { + return @( + @{Command="Invoke-AzDevOpsApiRestMethod"} + ) + } + # Arrange + $tokenProperties = @{ + access_token = "test_access_token" + expires_on = 3600 # Assuming this is seconds since epoch + expires_in = 3600 + resource = "https://my.resource.com" + token_type = "Bearer" + } + $tokenObject = [ManagedIdentityToken]::new((New-Object PSCustomObject -Property $tokenProperties)) + # Act + $accessToken = $tokenObject.Get() + # Assert + $accessToken | Should -Be "test_access_token" + } + + It "Throws an exception when Get method is called from a disallowed method" { + Mock Get-PSCallStack { + return @( + @{Command="Write-Host"} + ) + } + # Arrange + $tokenProperties = @{ + access_token = "test_access_token" + expires_on = 3600 # Assuming this is seconds since epoch + expires_in = 3600 + resource = "https://my.resource.com" + token_type = "Bearer" + } + $tokenObject = [ManagedIdentityToken]::new((New-Object PSCustomObject -Property $tokenProperties)) + # Act / Assert + try { $tokenObject.Get() } catch { $exception = $_ } + $exception | Should -Be '[ManagedIdentityToken] The Get() method can only be called within Invoke-AzDevOpsApiRestMethod.' + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.Tests.ps1 index 5ffa3bbc0..6d9a19585 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.Tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.Tests.ps1 @@ -194,3 +194,158 @@ InModuleScope 'AzureDevOpsDsc.Common' { } } } +# Existing code... + +Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { + + # Existing code... + + Context 'When input parameters are valid' { + + # Existing code... + + Context 'When called just with mandatory, "ApiUri", "HttpMethod" and "HttpRequestHeader" parameters' { + + # Existing code... + + It 'Should not throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader } | Should -Not -Throw + } + + It 'Should output nothing/null - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + $output = Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader + + $output | Should -BeNullOrEmpty + } + + It 'Should invoke "Invoke-RestMethod" exactly once - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + Mock Invoke-RestMethod {} -Verifiable + + Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader + + Assert-MockCalled Invoke-RestMethod -Times 1 -Exactly -Scope 'It' + } + + Context 'When "Invoke-RestMethod" throws an exception on every retry' { + Mock Invoke-RestMethod { throw "Some exception" } + + It 'Should invoke "Invoke-RestMethod" number of times equal to "RetryAttempts" parameter value + 1 - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + Mock Invoke-RestMethod { throw "Some exception" } -Verifiable + Mock New-InvalidOperationException {} + + Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader + + Assert-MockCalled Invoke-RestMethod -Times $($defaultRetryAttempts+1) -Exactly -Scope 'It' + } + + + It 'Should invoke "Start-Sleep" number of times equal to "RetryAttempts" parameter value + 1 - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + Mock Start-Sleep { } -Verifiable + + Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader + + Assert-MockCalled Start-Sleep -Times $($defaultRetryAttempts+1) -Exactly -Scope 'It' + } + + + It 'Should invoke "New-InvalidOperationException" exactly once - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + Mock New-InvalidOperationException {} -Verifiable + + Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader + + Assert-MockCalled New-InvalidOperationException -Times 1 -Exactly -Scope 'It' + } + + } + + } + } + + + Context 'When input parameters are invalid' { + + # Existing code... + + Context 'When called without mandatory, "ApiUri" parameter' { + + It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader } | Should -Throw + } + + } + + Context 'When called without mandatory, "HttpMethod" parameter' { + + It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $null -HttpRequestHeader $HttpRequestHeader } | Should -Throw + } + + } + + Context 'When called without mandatory, "ApiUri" and "HttpMethod" parameters' { + + It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $null -HttpRequestHeader $HttpRequestHeader } | Should -Throw + } + + } + + Context 'When called without mandatory, "HttpRequestHeader" parameter' { + + It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $null } | Should -Throw + } + + } + + Context 'When called without mandatory, "ApiUri" and "HttpRequestHeader" parameters' { + + It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $HttpMethod -HttpRequestHeader $null } | Should -Throw + } + + } + + Context 'When called without mandatory, "HttpMethod" and "HttpRequestHeader" parameters' { + + It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $null -HttpRequestHeader $null } | Should -Throw + } + + } + + Context 'When called without mandatory, "ApiUri", "HttpMethod" and "HttpRequestHeader" parameters' { + + It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { + param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) + + { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $null -HttpRequestHeader $null } | Should -Throw + } + + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.TestInitialization.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.TestInitialization.ps1 index aeea1dc63..8fda79eaa 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.TestInitialization.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.TestInitialization.ps1 @@ -3,20 +3,43 @@ Automated unit test for classes in AzureDevOpsDsc. #> -Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '\..\Modules\TestHelpers\CommonTestHelper.psm1') -Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '\..\Modules\TestHelpers\CommonTestCases.psm1') + +Function Split-RecurivePath { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$Path, + [Parameter(Mandatory = $false)] + [int]$Times = 1 + ) + + 1 .. $Times | ForEach-Object { + $Path = Split-Path -Path $Path -Parent + } + + $Path +} + + +$script:RepositoryRoot = Split-RecurivePath $PSScriptRoot -Times 4 + +Import-Module -Name (Join-Path -Path $script:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestCases.psm1') +Import-Module -Name (Join-Path -Path $script:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1') + +Set-OutputDirAsModulePath -RepositoryRoot $script:RepositoryRoot $script:dscModuleName = 'AzureDevOpsDsc' $script:dscModule = Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1 $script:dscModuleFile = $($script:dscModule.ModuleBase +'\'+ $script:dscModuleName + ".psd1") + Get-Module -Name $script:dscModuleName -All | Remove-Module $script:dscModuleName -Force -ErrorAction SilentlyContinue $script:subModuleName = 'AzureDevOpsDsc.Common' -Import-Module -Name $script:dscModuleFile -Force +Import-Module -Name $script:dscModuleFile #-Force Get-Module -Name $script:subModuleName -All | Remove-Module -Force -ErrorAction SilentlyContinue $script:subModulesFolder = Join-Path -Path $script:dscModule.ModuleBase -ChildPath 'Modules' $script:subModuleFile = Join-Path $script:subModulesFolder "$($script:subModuleName)/$($script:subModuleName).psd1" -Import-Module -Name $script:subModuleFile -Force #-Verbose +Import-Module -Name $script:subModuleFile #-Force #-Verbose diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Get-AzManagedIdentityToken.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Get-AzManagedIdentityToken.Tests.ps1 new file mode 100644 index 000000000..9f2fe9b00 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Get-AzManagedIdentityToken.Tests.ps1 @@ -0,0 +1,72 @@ +<# +.SYNOPSIS + Test suite for the Get-AzManagedIdentityToken function. + +.DESCRIPTION + This test suite validates the functionality of the Get-AzManagedIdentityToken function, ensuring it handles various scenarios correctly. +#> + +. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 + +InModuleScope 'AzureDevOpsDsc.Common' { + + Describe "Get-AzManagedIdentityToken Function Tests" { + + BeforeAll { + + Import-Module 'AzureDevOpsDsc' + + Mock Invoke-AzDevOpsApiRestMethod { + return @{ + access_token = 'mock_access_token' + expires_on = 1000 + expires_in = 1000 + resource = "STRING" + token_type = "bearer" + } + } + + Mock Test-AzManagedIdentityToken { + return $true + } + + } + + It "Obtains a token without verifying if the Verify switch is not set" { + # Arrange + $organizationName = "Contoso" + # Act + $token = Get-AzManagedIdentityToken -OrganizationName $organizationName + # Assert + $token.access_token | Should -BeOfType [System.Security.SecureString] + } + + It "Verifies the connection and obtains a token when the Verify switch is set" { + # Arrange + $organizationName = "Contoso" + # Act / Assert + { Get-AzManagedIdentityToken -OrganizationName $organizationName -Verify } | Should -Not -Throw + } + + It "Throws an exception if the Invoke-AzDevOpsApiRestMethod does not return an access token" { + # Arrange + Mock Invoke-AzDevOpsApiRestMethod { + return @{} + } + $organizationName = "Contoso" + # Act / Assert + { Get-AzManagedIdentityToken -OrganizationName $organizationName } | Should -Throw + } + + It "Throws an exception if the Test-AzManagedIdentityToken returns false" { + # Arrange + Mock Test-AzManagedIdentityToken { + return $false + } + $organizationName = "Contoso" + # Act / Assert + { Get-AzManagedIdentityToken -OrganizationName $organizationName -Verify } | Should -Throw + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzDevOpsApiResource.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzDevOpsApiResource.Tests.ps1 new file mode 100644 index 000000000..b5c06decb --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzDevOpsApiResource.Tests.ps1 @@ -0,0 +1,9 @@ +Describe "New-AzDevOpsApiResource" { + Context "When ApiVersion parameter is provided" { + It "Should set the ApiVersion property" { + $apiVersion = "v1" + $resource = New-AzDevOpsApiResource -ApiVersion $apiVersion + $resource.ApiVersion | Should Be $apiVersion + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzManagedIdentity.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzManagedIdentity.Tests.ps1 new file mode 100644 index 000000000..bba7c3d70 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzManagedIdentity.Tests.ps1 @@ -0,0 +1,53 @@ +<# +.SYNOPSIS + Test suite for the New-AzManagedIdentity function. + +.DESCRIPTION + This test suite validates the functionality of the New-AzManagedIdentity function, ensuring it sets global variables correctly and handles token acquisition. +#> + +# Initialize tests for module function +. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 + +InModuleScope 'AzureDevOpsDsc.Common' { + + Describe "New-AzManagedIdentity Function Tests" { + + BeforeAll { + + Mock Get-AzManagedIdentityToken { + return @{ + access_token = "mocked_access_token" + expires_on = (Get-Date).AddHours(1).ToUniversalTime().Subtract([datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc)).TotalSeconds + expires_in = 3600 + resource = "https://management.azure.com/" + token_type = "Bearer" + } + } + } + + It "Sets the global organization name and managed identity token" { + # Arrange + $orgName = "TestOrganization" + + # Act + New-AzManagedIdentity -OrganizationName $orgName + + # Assert + $Global:DSCAZDO_OrganizationName | Should -Be $orgName + $Global:DSCAZDO_ManagedIdentityToken | Should -Not -Be $null + $Global:DSCAZDO_ManagedIdentityToken.access_token | Should -Be "mocked_access_token" + } + + It "Sets the global managed identity token to null if Get-AzManagedIdentityToken fails" { + # Arrange + Mock Get-AzManagedIdentityToken { throw "Failed to get token." } + $orgName = "TestOrganization" + + # Act / Assert + { New-AzManagedIdentity -OrganizationName $orgName } | Should -Throw "Failed to get token." + $Global:DSCAZDO_ManagedIdentityToken | Should -Be $null + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzManagedIdentityToken.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzManagedIdentityToken.Tests.ps1 new file mode 100644 index 000000000..c38435312 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzManagedIdentityToken.Tests.ps1 @@ -0,0 +1,59 @@ +# Initialize tests for module function + +. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 + +<# +.SYNOPSIS + Test suite for the Test-AzManagedIdentityToken function. + +.DESCRIPTION + This test suite validates the functionality of the Test-AzManagedIdentityToken function, ensuring it properly tests the managed identity token. +#> + +InModuleScope 'AzureDevOpsDsc.Common' { + Describe "Test-AzManagedIdentityToken Function Tests" { + + Mock Invoke-AzDevOpsApiRestMethod { + return @{ + value = @("Project1", "Project2") + } + } + + BeforeAll { + # Define a mock Managed Identity object with a Get method + $mockManagedIdentity = New-Object -TypeName psobject + $mockManagedIdentity | Add-Member -MemberType ScriptMethod -Name Get -Value { return "mocked_access_token" } + + # Set up a global variable as expected by the function + $GLOBAL:DSCAZDO_OrganizationName = "MockOrganization" + } + + It "Returns true when the managed identity token is valid" { + # Arrange + $AzManagedIdentityLocalizedData = @{ + Global_Url_AZDO_Project = "https://dev.azure.com/{0}/_apis/projects" + } + + # Act + $result = Test-AzManagedIdentityToken -ManagedIdentity $mockManagedIdentity + + # Assert + $result | Should -Be $true + } + + It "Returns false when the managed identity token is not valid" { + # Arrange + Mock Invoke-AzDevOpsApiRestMethod { throw "Unauthorized access." } + $AzManagedIdentityLocalizedData = @{ + Global_Url_AZDO_Project = "https://dev.azure.com/{0}/_apis/projects" + } + + # Act + $result = Test-AzManagedIdentityToken -ManagedIdentity $mockManagedIdentity + + # Assert + $result | Should -Be $false + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Update-AzManagedIdentity.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Update-AzManagedIdentity.Tests.ps1 new file mode 100644 index 000000000..6f4faecdf --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Update-AzManagedIdentity.Tests.ps1 @@ -0,0 +1,28 @@ +# Initialize tests for module function +. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 + +InModuleScope 'AzureDevOpsDsc.Common' { + + Describe 'Update-AzManagedIdentity' { + + Context 'When OrganizationName is set' { + + It 'Should update the global variable DSCAZDO_ManagedIdentityToken' { + $organizationName = 'MyOrganization' + $global:DSCAZDO_OrganizationName = $organizationName + + Update-AzManagedIdentity + + $global:DSCAZDO_ManagedIdentityToken | Should -Not -Be $null + } + } + + Context 'When OrganizationName is not set' { + + It 'Should throw an error' { + { Update-AzManagedIdentity } | Should -Throw + } + } + } + +} diff --git a/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1 b/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1 index e809c4c45..a07d32939 100644 --- a/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1 +++ b/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1 @@ -249,3 +249,22 @@ function Get-CommandParameterSetParameter Where-Object { $_.Name -like $ParameterSetName}).Parameters } + + +function Set-OutputDirAsModulePath +{ + [CmdletBinding()] + param ( + [Parameter()] + [String] + $RepositoryRoot + ) + + # Set the module path if it is not already set + if ($ENV:PSModulePath -like "*$($RepositoryRoot)*") { return } + + $ModulePath = '{0}{1}\' -f (($IsLinux) ? ':' : ';'), $RepositoryRoot + $ENV:PSModulePath = "{0}{1}\output" -f $ENV:PSModulePath, $ModulePath + $ENV:PSModulePath = "{0}{1}\output\AzureDevOpsDsc\0.0.0\Modules" -f $ENV:PSModulePath, $ModulePath + +} From 783df57f1325a279e7a8c9523fb58c13a6bb9b7b Mon Sep 17 00:00:00 2001 From: Michael Zanatta Date: Mon, 21 Oct 2024 07:03:22 +1000 Subject: [PATCH 3/7] Resource Refactor and Update (#3) * Finished Testing Set Method * Update Add Resource * Adding Debugging for New-AzDoOrgGroup * Added Remove-xAzDoOrganizationGroup Added Add-xAzDoOrganizationGroup * Adding AzDoProject* Group * Fixing Bugs * Renaming Files * Daily Commit * Revert Export Cache Object * Prep work to finish LCM * Refactor Logic for LCM * Add DscPlaybook Refactor Code within LCM into Playbook Updated logic to include DependsOn * Adding Datum Compiling Configuration and Modules * Add Configuration * Adding baseline * Rename Fix Bugs with Datum Lookups * Update Configuration * Refactored Configuration and Updated Merge Config * Start work on build * Refactoring Logic Adding Verbose Logging * Code Refactor * Update GitIgnore Update Configuration * Fixed Logic Bug with Ensure:Absent * Fixed Logic Bug with Ensure Absent * Removing Debugging Code * Bug Fixes * Update * Removing Endpoint Configuration * Renaming Caching Functions * Rename Functions * Update Module Manifest * Adding Group Member * Adding top level tests * Archiving Tests Updating Tests * Adding Tests Sorting Tests * Adding Cachine Tests * Add Cache * Adding Cache Test Initialization * Reorganizing Tests * Commenced Work of Adding GroupMembers Resource Refactor of existing caching resource to preference groups prior to membership * Adding Usercache and GroupMembership Cache * Adding Logic to dertmine if the group needs to be modified. * Bug Fixes * Bug fixes with Caching * Rename Functions * Clearing Work on AzDoGroupMember * Renamed files * Bug Fixes * Adding Format-AzDoProjectName Adding Unit Tests * Bug Fix * Perform Code Cleanup Fixed code for xAzDoGroupMember resource * Accidently removed CLIXML Export * Renamed file Added Add-xAxDoGroupMember Renamed: Format-UserPrincipalName to Format-AzDoGroup * Added New-DevOpsGroupMember Renamed: Format-UserPrincipalName to Format-AzDoGroup * Daily Commit * Bug Fixes Adding Verbose Logging Daily Commit * Bug Fixes with Searching for Display Name * Add Remove-xAzDoGroupMember * Daily Commit Refactoring Authentication Logic to include differnt auth types * Updated ResourceObject to load caching settings. Added SecureString support for PAT Tokens Bug Fixes Fixing Plumbing issues with functions * Performing Code Cleanup * Code Refactor Bug Fixes * Daily Commit Bug Fixes * Daily Commit Bug Fixes * Daily Commit * Added Initial Logic * Adding wrapping logic to complete the set function * Bugs Fixes * Bug Fixes * Remove ValidateSet with ValidateScript removing duplicate arrays Move API Functions into Resources directories rather then using Verb. * Cache Cleanup Adding GitRepositoryCache Enumeratoion * Added GitRepository Resource * Bug Fixes * Bug Fixes * Adding Permission Classes and descriptor types * Adding Permission Classes * Adding Class Method Files * Daily Commit * Daily Commit * Daily Commit * Daily Commit * Daily Commit * Daily Commit * Completed Initial ACLS resolution * Daily Commit * Bug Fixes * Daily Commit * Daily Commit * Daily Commit Bug Fixes * Daily Commit * Code cleanup * Daily Commit Added Compare-ACL function Commenced Initial Testing * Bug Fixes * Daily Commit * Adding TODO * Daily Commit * Daily Commit * Renamed Files Updated Test-ACLListForChanges * Renamed Functions and Performed Code Cleanup * Bug Fixes * Bug Fixes with Caching * Daily Commit * Bug Fixes * Bug Fixes File Movement * Disabling Logging * Bug Fixes * Bug Fixes * Bug Fixes * Removing Enums - Not required * File cleanup. Remove redundent files. * File Cleanup * Code Cleanup. Recreating Tests * Adding Authentication Unit Testing * Adding Tests * Adding Additional Tests * Removing Unused Enums Removing Permissions.ps1 * Adding AI Tests * Adding Unit Tests * Formatting cleanup for tests. * Renaming Files to make place for new resources * Adding xAzDoProjectGroupPermission * Daily Commit * Daily Commit * Refactored ACL logic * Adding xAzDoProjectGroupPermission * Bug Fixes * Adding Documentation * Bug Fixes * Bug Fixes * Daily Commit * Bug Fixes Adding New Resource: xAzDoGroupPermission * Adding Tests * Adding Automated Testing * Bug Fixes and Renaming Functions Correcting File Names * Bug Fixes * Removing unused tests. Writing a parameterized Initialization Script for intergration testing. * Adding xAzDevOpsProjectServices resource * Renamed Files * Bug Fixes with AZDOProjectServices. * Refactor of DevOps Project to include new settings * Adding documentation * Bug Fixes with Project Resource. More work is required. * Bug Fixes * Disabling Logging * Adding ProjectAbbreviation Bug Fixes with xAzDoProject Resource * Remove ProjectAbbreviation * Bug fixes with Description * Fixed Bug with inherited flat within ACL check Added additional logging * Bug Fixes with GroupMembers Starting work fixing issues with GroupMembers. * Fixed caching bug with Set-xAzDoGroupMember when running Local Configuration Manager (LCM) twice. * Fixing Spelling Issue Adding Resource Documentation * Adding More Documentation * Adding More Documentation * Bug fixes with Test method within LCM * Adding Todo's * Remove Export Clixml TypeCast ReferanceACLs as Array Adding Refresh-Cache Identity to fix bug with creating and setting group permissions in the same LCM run. * Adding Documentation * Adding Documentation * Adding Documentation * Renaming Directories Updating Documentation * Updating Documentation * Adding Documentation * Updating Documentation * Updating Documentation * Daily Commit * Adjusting Logic for Test Framework * Adding Unit Tests * Update tests * Adding Intergration Tests Removing ProcessPermission Resource (not needed) * Adding Initial Intergration Tests * Bug Fixes with Intergration Testing * Bug Fixes * Remove -Verbose logging Fixed bug in Get-xAzDoProject with blank description * Moving untested intergrating tests. Bug fixes. Enabled logging for write-verbose. * Bug Fixes * Bug Fixes * Update Logging Moving Files * Updating Documentation * Big Fixes * Adding New Tests Removing Bugs * Bug Fixes Adding Logging Completed Intergration Testing * Bug Fixes * Finish Testing * Bug Fixes with DSCGetSummary State Fixed Bug with Invoke-Tests * Bug Fixes Disabling Group Permission Resource * Enabled All Tests Fixed bugs with teardown logic * Bug Fixes * Starting unit tests Reworking unit tests * Adding CommonTestFunctions Module Updating Tests to support CommonTestFunctions Module * Adding Tests Daily Commit * Adding -ParameterFilter * Adding Unit Tests for Cache * Adding GitRepo Tests * Fixing More Tests * Update Project Tests * Project Services * Adding Security Descriptor * Updating Tests * Adding Managed Identity Token Tests More work is needed * Bug Fixes Adding Authentication Tests * Bug Fixes with Tests * Adding Cache Tests * Adding Tests * Initialize-CacheObject * Adding Unit Tests * Add ProjectCache * Adding GroupCache * Adding Cache * Bug Fixes * Bug Fixes * Adding Tests * Adding Tests * Bug Fixes with Tests * Reformat Tests * Test Fixes * Rename File * Rename Files * Bug Fixes Adding Tests * Finishing Tests Adding Tags * Adding Test Tags * Bug Fixes Updating Tests * Bug Fixes Adding Tests * Bug Fixes Adding Tests * Bug Fixes with Test * Bug Fixes * Disabling Tests * Disabling Archived Tests * Renaming Files * Bug Fixes Fixing Spelling * Rename File * Daily Commit * Completed initial tests for xAzDoGitPermission * Bug Fixes Adding xAzDoGitRepository * Rename files Bug Fixes * Adding tests * Adding Tests to xAzDoGroupPermission * Adding xAzDoOrganizationGroup * Remove Wait-Debugger * Add AzDoProject Tests * Bug Fixes * Bug Fixes * Bug Fixes * Fixed bug within Find-Identity Disabling Unused Tests Adding additional Verbose logging * Fixed bug with caching after creating git repository * Adding: Refresh-AzDoCache Adding Logging Fixed bug where identities are being added to the wrong cache Replaced AzDOAPI_0_ProjectCache / Refresh-AzDoCache * Add MI support for Azure Arc * Bug Fixes with Azure Arc Authentication * Add MI support for Azure Arc * Bug Fixes with Azure Arc Authentication --------- Co-authored-by: Michael Zanatta Co-authored-by: david --- .gitignore | 8 + CHANGELOG.md | 4 +- CONTRIBUTING.md | 829 +++++++++++++++++- README.md | 4 + USAGE.md | 139 +++ source/AzureDevOpsDsc.psd1 | 14 +- .../000.ManagedIdentityDataResources.ps1 | 16 - source/Classes/001.AuthenticationToken.ps1 | 78 ++ source/Classes/002.PersonalAccessToken.ps1 | 41 + ...Token.ps1 => 003.ManagedIdentityToken.ps1} | 57 +- ...sourceBase.ps1 => 004.DscResourceBase.ps1} | 0 ...ps1 => 005.AzDevOpsApiDscResourceBase.ps1} | 3 +- ...se.ps1 => 006.AzDevOpsDscResourceBase.ps1} | 251 ++++-- ....APIRateLimit.ps1 => 007.APIRateLimit.ps1} | 2 +- source/Classes/009.xAzDoGroupPermission.ps1 | 105 +++ source/Classes/010.AzDevOpsProject.ps1 | 78 -- source/Classes/011.xAzDoOrganizationGroup.ps1 | 99 +++ source/Classes/020.xAzDoProject.ps1 | 110 +++ source/Classes/021.xAzDoProjectServices.ps1 | 122 +++ source/Classes/022.xAzDoProjectGroup.ps1 | 109 +++ source/Classes/031.xAzDoGroupMember.ps1 | 91 ++ source/Classes/040.xAzDoGitRepository.ps1 | 97 ++ source/Classes/041.xAzDoGitPermission.ps1 | 99 +++ source/Enum/DSCGetSummaryState.ps1 | 13 + source/Enum/DescriptorType.ps1 | 64 ++ source/Enum/TokenType.ps1 | 5 + .../Api/Classes/000.CacheItem.ps1 | 20 + .../Api/Functions/Private/Api/ACE/Empty.txt | 0 .../Private/Api/ACL/Get-DevOpsACL.ps1 | 37 + .../Api/ACL/Get-DevOpsDescriptorIdentity.ps1 | 66 ++ .../Private/Api/ApiResource/Empty.txt | 0 .../AzDoPermission/Remove-xAzDoPermission.ps1 | 50 ++ .../AzDoPermission/Set-xAzDoPermission.ps1 | 49 ++ .../Api/Cache/List-DevOpsGitRepository.ps1 | 35 + .../Api/Cache/List-DevOpsGroupMembers.ps1 | 33 + .../Private/Api/Cache/List-DevOpsGroups.ps1 | 34 + .../Private/Api/Cache/List-DevOpsProcess.ps1 | 31 + .../Private/Api/Cache/List-DevOpsProjects.ps1 | 32 + .../Cache/List-DevOpsSecurityNamespaces.ps1 | 30 + .../Cache/List-DevOpsServicePrinciples.ps1 | 31 + .../Private/Api/Cache/List-UserCache.ps1 | 30 + .../Api/GitRepository/New-GitRepository.ps1 | 56 ++ .../GitRepository/Remove-GitRepository.ps1 | 44 + .../Private/Api/Group/New-DevOpsGroup.ps1 | 94 ++ .../Private/Api/Group/Remove-DevOpsGroup.ps1 | 57 ++ .../Private/Api/Group/Set-DevOpsGroup.ps1 | 109 +++ .../Api/GroupMember/New-DevOpsGroupMember.ps1 | 62 ++ .../GroupMember/Remove-DevOpsGroupMember.ps1 | 62 ++ .../Private/Api/Permission/Empty.txt | 0 .../Private/Api/Project/New-DevOpsProject.ps1 | 102 +++ .../Api/Project/Remove-DevOpsProject.ps1 | 60 ++ .../Api/Project/Update-DevOpsProject.ps1 | 102 +++ .../Api/Project/Wait-DevOpsProject.ps1 | 86 ++ .../Get-ProjectServiceStatus.ps1 | 43 + .../Set-ProjectServiceStatus.ps1 | 41 + .../Functions/Private/Api/Resource/Empty.txt | 0 .../Private/Api/RoleAssignments/Empty.txt | 0 .../Private/Api/RoleDefinition/empty.txt | 0 .../Get-DevOpsSecurityDescriptor.ps1 | 57 ++ .../Private/Api/ServiceEndpoint/empty.txt | 0 .../Private/Api/ServicePrincipal/empty.txt | 0 .../Add-AuthenticationHTTPHeader.ps1 | 53 ++ .../Get-AzManagedIdentityToken.ps1 | 115 +++ .../Update-AzManagedIdentity.ps1 | 4 +- .../Set-AzPersonalAccessToken.ps1 | 56 ++ .../Test-AzToken.ps1} | 21 +- .../Functions/Private/Cache/Add-CacheItem.ps1 | 93 ++ .../Cache Initalization/0.ProjectCache.ps1 | 86 ++ .../Cache Initalization/1.GroupCache.ps1 | 62 ++ .../Cache/Cache Initalization/2.UserCache.ps1 | 50 ++ .../3.GroupMemberCache.ps1 | 78 ++ .../4.GitRepositoryCache.ps1 | 57 ++ .../5.PermissionsCache.ps1 | 40 + .../6.ServicePrinciple.ps1 | 50 ++ .../7.IdentitySubjectDescriptors.ps1 | 131 +++ .../8.ProcessTemplates.ps1 | 50 ++ .../Private/Cache/Export-CacheObject.ps1 | 79 ++ .../Private/Cache/Find-CacheItem.ps1 | 55 ++ .../Functions/Private/Cache/Get-CacheItem.ps1 | 57 ++ .../Private/Cache/Get-CacheObject.ps1 | 74 ++ .../Private/Cache/Import-CacheObject.ps1 | 92 ++ .../Private/Cache/Initialize-CacheObject.ps1 | 86 ++ .../Private/Cache/Refresh-AzDoCache.ps1 | 21 + .../Private/Cache/Refresh-CacheIdentity.ps1 | 51 ++ .../Private/Cache/Refresh-CacheObject.ps1 | 21 + .../Private/Cache/Remove-CacheItem.ps1 | 54 ++ .../Private/Cache/Set-CacheObject.ps1 | 74 ++ .../Private/Get-AzDevOpsApiResource.ps1 | 107 --- .../Private/Get-AzManagedIdentityToken.ps1 | 65 -- .../Private/Helper/ACL/ConvertTo-ACEList.ps1 | 98 +++ .../Helper/ACL/ConvertTo-ACETokenList.ps1 | 68 ++ .../Private/Helper/ACL/ConvertTo-ACL.ps1 | 111 +++ .../Helper/ACL/ConvertTo-ACLHashtable.ps1 | 165 ++++ .../Helper/ACL/ConvertTo-FormattedACL.ps1 | 114 +++ .../Helper/ACL/ConvertTo-FormattedToken.ps1 | 62 ++ .../Private/Helper/ACL/Format-ACEs.ps1 | 60 ++ .../Helper/ACL/Get-BitwiseOrResult.ps1 | 55 ++ .../Private/Helper/ACL/Group-ACEs.ps1 | 64 ++ .../Private/Helper/ACL/New-ACLToken.ps1 | 113 +++ .../Private/Helper/ACL/Parse-ACLToken.ps1 | 78 ++ .../Private/Helper/ACL/Resolve-ACLToken.ps1 | 26 + .../Helper/ACL/Test-ACLListforChanges.ps1 | 226 +++++ .../Test-AzDevOpsApiHttpRequestHeader.ps1 | 19 +- .../API}/Test-AzDevOpsApiResourceId.ps1 | 2 +- .../API}/Test-AzDevOpsApiTimeoutExceeded.ps1 | 0 .../{ => Helper/API}/Test-AzDevOpsApiUri.ps1 | 7 +- .../API}/Test-AzDevOpsApiVersion.ps1 | 4 +- .../API}/Wait-AzDevOpsApiResource.ps1 | 0 .../Private/Helper/ConvertTo-Base64String.ps1 | 34 + .../Private/Helper/Find-AzDoIdentity.ps1 | 115 +++ .../Private/Helper/Find-Identity.ps1 | 189 ++++ .../Private/Helper/Format-AzDoGroup.ps1 | 44 + .../Private/Helper/Format-AzDoGroupMember.ps1 | 18 + .../Private/Helper/Format-AzDoProjectName.ps1 | 47 + .../Private/Helper/Format-DescriptorType.ps1 | 24 + .../Get-AzDevOpsApiHttpRequestHeader.ps1 | 0 .../Get-AzDevOpsApiResourceName.ps1 | 0 .../Get-AzDevOpsApiResourceUri.ps1 | 0 .../Get-AzDevOpsApiUriAreaName.ps1 | 0 .../Get-AzDevOpsApiUriResourceName.ps1 | 0 .../{ => Helper}/Get-AzDevOpsApiVersion.ps1 | 7 +- .../Get-AzDevOpsApiWaitIntervalMs.ps1 | 0 .../Get-AzDevOpsApiWaitTimeoutMs.ps1 | 0 .../Private/Helper/Get-AzDoCacheObjects.ps1 | 19 + .../Helper/Invoke-AzDevOpsApiRestMethod.ps1 | 261 ++++++ .../Private/Helper/Logging/Flush-Log.txt | 36 + .../Private/Helper/Logging/Initialize-Log.txt | 31 + .../Logging/Proxy Functions/Write-Error.ps1 | 19 + .../Logging/Proxy Functions/Write-Verbose.ps1 | 21 + .../Logging/Proxy Functions/Write-Warning.ps1 | 20 + .../Private/Helper/Logging/Write-Log.txt | 51 ++ .../Private/Helper/New-AzDevOpsACLToken.ps1 | 52 ++ .../Helper/New-InvalidOperationException.ps1 | 37 + .../Private/Helper/PreCommandLookupAction.ps1 | 30 + .../Private/Helper/System/New-Thread.ps1 | 21 + .../Private/Invoke-AzDevOpsApiRestMethod.ps1 | 199 ----- .../Private/New-AzDevOpsApiResource.ps1 | 115 --- .../Private/Remove-AzDevOpsApiResource.ps1 | 122 --- .../Private/Set-AzDevOpsApiResource.ps1 | 121 --- .../Private/Test-AzDevOpsApiResource.ps1 | 73 -- .../Private/Test-AzDevOpsApiResourceName.ps1 | 41 - .../AzureDevOpsDsc.Common.psd1 | 64 +- .../AzureDevOpsDsc.Common.psm1 | 49 +- .../000.LocalizedDataAzACLTokenPatten.ps1 | 28 + ...001.LocalizedDataAzResourceTokenPatten.ps1 | 26 + ...002.LocalizedDataAzSerializationPatten.ps1 | 19 + .../003.LocalizedDataAzURLParams.ps1 | 13 + .../Functions/Private/Test-AzDevOpsPat.ps1 | 10 +- .../Functions/Private/Test-ObjectProperty.ps1 | 60 ++ .../Functions/Public/Get-AzDevOpsProject.ps1 | 113 --- .../Functions/Public/New-AzDevOpsProject.ps1 | 108 --- .../Public/New-AzDoAuthenticationProvider.ps1 | 144 +++ .../Public/New-AzManagedIdentity.ps1 | 33 - .../Public/Remove-AzDevOpsProject.ps1 | 65 -- .../Functions/Public/Set-AzDevOpsProject.ps1 | 121 --- .../Functions/Public/Test-AzDevOpsProject.ps1 | 87 -- .../Get-xAzDoGitPermission.ps1 | 161 ++++ .../New-xAzDoGitPermission.ps1 | 60 ++ .../Remove-xAzDoGitPermission.ps1 | 90 ++ .../Set-xAzDoGitPermission.ps1 | 65 ++ .../Get-xAzDoGitRepository.ps1 | 68 ++ .../New-xAzDoGitRepository.ps1 | 57 ++ .../Remove-xAzDoGitRepository.ps1 | 54 ++ .../Set-xAzDoGitRepository.ps1 | 31 + .../xAzDoGroupMember/Get-xAzDoGroupMember.ps1 | 147 ++++ .../xAzDoGroupMember/New-xAzDoGroupMember.ps1 | 103 +++ .../Remove-xAzDoGroupMember.ps1 | 91 ++ .../xAzDoGroupMember/Set-xAzDoGroupMember.ps1 | 124 +++ .../Test-xAzDoGroupMember.ps1 | 28 + .../Get-xAzDoGroupPermission.ps1 | 150 ++++ .../New-xAzDoGroupPermission.ps1 | 70 ++ .../Remove-xAzDoGroupPermission.ps1 | 73 ++ .../Set-xAzDoGroupPermission.ps1 | 70 ++ .../Get-xAzDoOrganizationGroup.ps1 | 201 +++++ .../New-xAzDoOrganizationGroup.ps1 | 53 ++ .../Remove-xAzDoOrganizationGroup.ps1 | 61 ++ .../Set-xAzDoOrganizationGroup.ps1 | 69 ++ .../Test-xAzDoOrganizationGroup.ps1 | 106 +++ .../Public/xAzDoProject/Get-xAzDoProject.ps1 | 118 +++ .../Public/xAzDoProject/New-xAzDoProject.ps1 | 77 ++ .../xAzDoProject/Remove-xAzDoProject.ps1 | 62 ++ .../Public/xAzDoProject/Set-xAzDoProject.ps1 | 74 ++ .../Public/xAzDoProject/Test-xAzDoProject.ps1 | 44 + .../Get-xAzDoProjectGroup.ps1 | 207 +++++ .../New-xAzDoProjectGroup.ps1 | 64 ++ .../Remove-xAzDoProjectGroup.ps1 | 65 ++ .../Set-xAzDoProjectGroup.ps1 | 74 ++ .../Test-xAzDoProjectGroup.ps1 | 110 +++ .../Get-xAzDoProjectServices.ps1 | 124 +++ .../New-xAzDoProjectServices.ps1 | 48 + .../Remove-xAzDoProjectServices.ps1 | 48 + .../Set-xAzDoProjectServices.ps1 | 67 ++ .../Test-xAzDoProjectServices.ps1 | 48 + .../Public/Initalize-AzDevOpsCache.ps1 | 44 + .../Functions/Public/Refresh-Cache.ps1 | 0 source/WikiSource/Home.md | 16 +- .../Resources/xAzDoGitPermission.md | 195 ++++ .../Resources/xAzDoGitRepository.md | 83 ++ .../WikiSource/Resources/xAzDoGroupMember.md | 127 +++ .../Resources/xAzDoGroupPermission.md | 144 +++ .../Resources/xAzDoOrganizationGroup.md | 96 ++ source/WikiSource/Resources/xAzDoProject.md | 123 +++ .../WikiSource/Resources/xAzDoProjectGroup.md | 102 +++ .../Resources/xAzDoProjectServices.md | 111 +++ tests/DSC/InModuleScope.ps1 | 45 + .../AzDevOpsProject.Integration.Tests.ps1 | 0 .../{ => Archive}/AzDevOpsProject.config.ps1 | 0 tests/Integration/Invoke-Tests.ps1 | 29 + .../Resources/xAzDoGitPermission.tests.ps1 | 147 ++++ .../Resources/xAzDoGitRepository.tests.ps1 | 127 +++ .../Resources/xAzDoGroupMember.tests.ps1 | 133 +++ .../Resources/xAzDoGroupPermission.tests.ps1 | 130 +++ ...zDoOrganizationGroup.Description.tests.ps1 | 153 ++++ ...oOrganizationGroup.NoDescription.tests.ps1 | 158 ++++ .../xAzDoProject.Description.tests.ps1 | 152 ++++ .../xAzDoProject.NoDescription.tests.ps1 | 116 +++ .../xAzDoProjectGroup.Description.tests.ps1 | 167 ++++ .../Resources/xAzDoProjectServices.tests.ps1 | 133 +++ .../Integration/Supporting/API/Add-Header.ps1 | 40 + .../Supporting/API/ConvertTo-Base64String.ps1 | 14 + .../Supporting/API/Get-AzDevOpsApiVersion.ps1 | 35 + .../Supporting/API/Get-MIToken.ps1 | 35 + .../Supporting/API/Invoke-APIRestMethod.ps1 | 177 ++++ .../Supporting/API/New-AuthProvider.ps1 | 51 ++ .../Supporting/APICalls/List-DevOpsGroups.ps1 | 34 + .../APICalls/List-DevOpsProjects.ps1 | 32 + .../APICalls/Remove-DevOpsGroup.ps1 | 56 ++ .../APICalls/Remove-DevOpsProject.ps1 | 60 ++ .../Functions/SupportingFunctions.ps1 | 73 ++ .../Supporting/Initalize-TestFramework.ps1 | 41 + tests/Integration/Supporting/Teardown.ps1 | 45 + .../TestFrameworkConfiguration.json | 4 + .../Classes/API/007.APIRateLimit.tests.ps1 | 72 ++ .../001.AuthenticationToken.tests.ps1 | 91 ++ .../002.PersonalAccessToken.tests.ps1 | 85 ++ .../003.ManagedIdentityToken.tests.ps1 | 136 +++ ...piDscResourceBase.Initialization.Tests.ps1 | 12 - tests/Unit/Classes/Classes.BeforeAll.ps1 | 42 + .../Classes/Classes.TestInitialization.ps1 | 22 - .../ManagedIdentity/APIRateLimit.tests.ps1 | 87 -- .../ManagedIdentityToken.tests.ps1 | 96 -- .../009.xAzDoGroupPermission.tests.ps1 | 59 ++ .../011.xAzDoOrganizationGroup.tests.ps1 | 67 ++ .../Resources/020.xAzDevOpsProject.tests.ps1 | 71 ++ .../Resources/022.xAzDoProjectGroup.tests.ps1 | 89 ++ .../Resources/031.xAzDoGroupMember.tests.ps1 | 82 ++ .../040.xAzDoGitRepository.tests.ps1 | 87 ++ .../041.xAzDoGitPermission.tests.ps1 | 91 ++ .../AzureDevOpsDsc.Common.Functions.Tests.ps1 | 211 ----- ...eDevOpsDsc.Common.Tests.Initialization.ps1 | 22 - .../Private/Api/ACL/Get-DevOpsACL.tests.ps1 | 62 ++ .../Get-DevOpsDescriptorIdentity.tests.ps1 | 82 ++ .../Remove-xAzDoPermission.tests.ps1 | 70 ++ .../Set-xAzDoPermission.tests.ps1 | 75 ++ .../Cache/List-DevOpsGitRepository.tests.ps1 | 76 ++ .../Cache/List-DevOpsGroupMembers.tests.ps1 | 78 ++ .../Api/Cache/List-DevOpsGroups.tests.ps1 | 78 ++ .../Api/Cache/List-DevOpsProcess.tests.ps1 | 90 ++ .../Api/Cache/List-DevOpsProjects.tests.ps1 | 49 ++ .../List-DevOpsSecurityNamespaces.tests.ps1 | 67 ++ .../List-DevOpsServicePrinciples.tests.ps1 | 85 ++ .../Api/Cache/List-UserCache.tests.ps1 | 75 ++ .../GitRepository/New-GitRepository.tests.ps1 | 57 ++ .../Remove-GitRepository.tests.ps1 | 63 ++ .../Api/Group/New-DevOpsGroup.tests.ps1 | 78 ++ .../Api/Group/Remove-DevOpsGroup.tests.ps1 | 70 ++ .../Api/Group/Set-DevOpsGroup.tests.ps1 | 63 ++ .../New-DevOpsGroupMember.tests.ps1 | 91 ++ .../Remove-DevOpsGroupMember.tests.ps1 | 65 ++ .../Api/Project/New-DevOpsProject.tests.ps1 | 89 ++ .../Project/Remove-DevOpsProject.tests.ps1 | 50 ++ .../Project/Update-DevOpsProject.tests.ps1 | 105 +++ .../Api/Project/Wait-DevOpsProject.tests.ps1 | 111 +++ .../Get-ProjectServiceStatus.tests.ps1 | 83 ++ .../Set-ProjectServiceStatus.tests.ps1 | 80 ++ .../Get-DevOpsSecurityDescriptor.tests.ps1 | 52 ++ .../Add-AuthenticationHTTPHeader.tests.ps1 | 87 ++ .../Get-AzManagedIdentityToken.tests.ps1 | 95 ++ .../Update-AzManagedIdentity.tests.ps1 | 65 ++ .../Set-AzPersonalAccessToken.tests.ps1 | 109 +++ .../Authentication/Test-AzToken.tests.ps1 | 65 ++ .../Private/Cache/Add-CacheItem.tests.ps1 | 129 +++ .../0.ProjectCache.tests.ps1 | 108 +++ .../1.GroupCache.tests.ps1 | 105 +++ .../Cache Initalization/2.UserCache.tests.ps1 | 84 ++ .../3.GroupMemberCache.tests.ps1 | 99 +++ .../4.GitRepositoryCache.tests.ps1 | 109 +++ .../5.PermissionsCache.tests.ps1 | 55 ++ .../6.ServicePrinciple.tests.ps1 | 74 ++ .../7.IdentitySubjectDescriptors.tests.ps1 | 144 +++ .../8.ProcessTemplates.tests.ps1 | 79 ++ .../Cache/Export-CacheObject.tests.ps1 | 133 +++ .../Private/Cache/Find-CacheItem.tests.ps1 | 68 ++ .../Private/Cache/Get-CacheItem.tests.ps1 | 93 ++ .../Private/Cache/Get-CacheObject.tests.ps1 | 59 ++ .../Cache/Import-CacheObject.tests.ps1 | 82 ++ .../Cache/Initialize-CacheObject.tests.ps1 | 101 +++ .../Private/Cache/Refresh-AzDoCache.tests.ps1 | 60 ++ .../Cache/Refresh-CacheIdentity.tests.ps1 | 77 ++ .../Cache/Refresh-CacheObject.tests.ps1 | 65 ++ .../Private/Cache/Remove-CacheItem.tests.ps1 | 75 ++ .../Private/Cache/Set-CacheObject.tests.ps1 | 65 ++ ...Get-AzDevOpsApiHttpRequestHeader.Tests.ps1 | 87 -- .../Private/Get-AzDevOpsApiResource.Tests.ps1 | 278 ------ .../Get-AzDevOpsApiResourceName.Tests.ps1 | 80 -- .../Get-AzDevOpsApiUriAreaName.Tests.ps1 | 127 --- .../Get-AzDevOpsApiUriResourceName.Tests.ps1 | 127 --- .../Private/Get-AzDevOpsApiVersion.Tests.ps1 | 141 --- .../Get-AzDevOpsApiWaitIntervalMs.Tests.ps1 | 51 -- .../Get-AzDevOpsApiWaitTimeoutMs.Tests.ps1 | 51 -- .../Helper/ACL/ConvertTo-ACEList.tests.ps1 | 78 ++ .../ACL/ConvertTo-ACETokenList.tests.ps1 | 84 ++ .../Helper/ACL/ConvertTo-ACL.tests.ps1 | 57 ++ .../ACL/ConvertTo-ACLHashtable.tests.ps1 | 130 +++ .../ACL/ConvertTo-FormattedACL.tests.ps1 | 141 +++ .../ACL/ConvertTo-FormattedToken.tests.ps1 | 82 ++ .../Private/Helper/ACL/Format-ACEs.tests.ps1 | 60 ++ .../Helper/ACL/Get-BitwiseOrResult.tests.ps1 | 60 ++ .../Private/Helper/ACL/Group-ACEs.tests.ps1 | 102 +++ .../Private/Helper/ACL/New-ACLToken.tests.ps1 | 76 ++ .../Helper/ACL/Parse-ACLToken.tests.ps1 | 83 ++ .../Helper/ACL/Resolve-ACLToken.tests.ps1 | 57 ++ .../ACL/Test-ACLListforChanges.tests.ps1 | 279 ++++++ ...est-AzDevOpsApiHttpRequestHeader.tests.ps1 | 68 ++ .../API/Test-AzDevOpsApiResourceId.tests.ps1 | 36 + .../Test-AzDevOpsApiTimeoutExceeded.tests.ps1 | 48 + .../Helper/API/Test-AzDevOpsApiUri.tests.ps1 | 46 + .../API/Test-AzDevOpsApiVersion.tests.ps1 | 28 + .../API/Wait-AzDevOpsApiResource.tests.ps1 | 82 ++ .../Helper/ConvertTo-Base64String.tests.ps1 | 54 ++ .../Helper/Find-AzDoIdentity.tests.ps1 | 185 ++++ .../Private/Helper/Find-Identity.tests.ps1 | 118 +++ .../Private/Helper/Format-AzDoGroup.tests.ps1 | 63 ++ .../Helper/Format-AzDoProjectName.tests.ps1 | 59 ++ .../Helper/Format-DescriptorType.tests.ps1 | 36 + ...Get-AzDevOpsApiHttpRequestHeader.tests.ps1 | 52 ++ .../Get-AzDevOpsApiResourceName.tests.ps1 | 24 + .../Get-AzDevOpsApiResourceUri.tests.ps1 | 46 + .../Get-AzDevOpsApiUriAreaName.tests.ps1 | 46 + .../Get-AzDevOpsApiUriResourceName.tests.ps1 | 45 + .../Helper/Get-AzDevOpsApiVersion.tests.ps1 | 31 + .../Get-AzDevOpsApiWaitIntervalMs.tests.ps1 | 28 + .../Get-AzDevOpsApiWaitTimeoutMs.tests.ps1 | 23 + .../Helper/Get-AzDoCacheObjects.tests.ps1 | 43 + .../Invoke-AzDevOpsApiRestMethod.tests.ps1 | 179 ++++ .../Helper/New-AzDevOpsACLToken.tests.ps1 | 55 ++ .../New-InvalidOperationException.tests.ps1 | 53 ++ .../Helper/PreCommandLookupAction.tests.ps1 | 56 ++ .../Helper/System/New-Thread.tests.ps1 | 46 + .../Invoke-AzDevOpsApiRestMethod.Tests.ps1 | 351 -------- ...est-AzDevOpsApiHttpRequestHeader.Tests.ps1 | 107 --- .../Test-AzDevOpsApiResource.Tests.ps1 | 165 ---- .../Test-AzDevOpsApiResourceId.Tests.ps1 | 107 --- .../Test-AzDevOpsApiResourceName.Tests.ps1 | 107 --- .../Test-AzDevOpsApiTimeoutExceeded.Tests.ps1 | 202 ----- .../Private/Test-AzDevOpsApiUri.Tests.ps1 | 107 --- .../Private/Test-AzDevOpsApiVersion.Tests.ps1 | 107 --- .../Wait-AzDevOpsApiResource.Tests.ps1 | 615 ------------- .../AzureDevOpsDsc.Common.InvokeTests.ps1 | 37 + ...ureDevOpsDsc.Common.TestInitialization.ps1 | 45 - .../Archive/Get-AzDevOpsCache.ps1.disabled | 87 ++ ...AzManagedIdentityToken.Tests.ps1.disabled} | 6 +- .../New-AzDevOpsACLToken.Tests.ps1.disabled | 68 ++ .../New-AzDevOpsApiCache.Tests.ps1.disabled | 68 ++ ...ew-AzDevOpsApiResource.Tests.ps1.disabled} | 0 .../New-AzManagedIdentity.Tests.ps1.disabled} | 6 +- ...-AzDevOpsPatCredential.Tests.ps1.disabled} | 0 ...AzManagedIdentityToken.Tests.ps1.disabled} | 10 +- ...date-AzManagedIdentity.Tests.ps1.disabled} | 2 +- ...st-AzDevOpsOperationId.Tests.ps1.disabled} | 0 ...DevOpsOrganizationName.Tests.ps1.disabled} | 0 ...s1 => Test-AzDevOpsPat.Tests.ps1.disabled} | 0 ...vOpsProjectDescription.Tests.ps1.disabled} | 0 ...Test-AzDevOpsProjectId.Tests.ps1.disabled} | 0 ...st-AzDevOpsProjectName.Tests.ps1.disabled} | 0 ...Wait-AzDevOpsOperation.Tests.ps1.disabled} | 0 .../Get-AzDevOpsOperation.Tests.ps1.disabled} | 0 .../Get-AzDevOpsProject.Tests.ps1.disabled} | 0 ...ew-AzDevOpsProject.Tests.Old.ps1.disabled} | 0 ...Test-AzDevOpsOperation.Tests.ps1.disabled} | 0 .../Test-AzDevOpsProject.Tests.ps1.disabled} | 0 .../Get-xAzDoGitPermission.tests.ps1 | 196 +++++ .../New-xAzDoGitPermission.tests.ps1 | 136 +++ .../Remove-xAzDoGitPermission.tests.ps1 | 158 ++++ .../Set-xAzDoGitPermission.tests.ps1 | 107 +++ .../Get-xAzDoGitRepository.tests.ps1 | 90 ++ .../New-xAzDoGitRepository.tests.ps1 | 137 +++ .../Remove-xAzDoGitRepository.tests.ps1 | 100 +++ .../Set-xAzDoGitRepository.tests.ps1 | 25 + .../Get-xAzDoGroupMember.tests.ps1 | 160 ++++ .../New-xAzDoGroupMember.tests.ps1 | 118 +++ .../Remove-xAzDoGroupMember.tests.ps1 | 139 +++ .../Set-xAzDoGroupMember.tests.ps1 | 228 +++++ .../Test-xAzDoGroupMember.tests.ps1 | 79 ++ .../Get-xAzDoGroupPermission.tests.ps1 | 137 +++ .../New-xAzDoGroupPermission.tests.ps1 | 120 +++ .../Remove-xAzDoGroupPermission.tests.ps1 | 100 +++ .../Set-xAzDoGroupPermission.tests.ps1 | 129 +++ .../Get-xAzDoOrganizationGroup.tests.ps1 | 139 +++ .../New-xAzDoOrganizationGroup.tests.ps1 | 76 ++ .../Remove-xAzDoOrganizationGroup.tests.ps1 | 127 +++ .../Set-xAzDoOrganizationGroup.tests.ps1 | 175 ++++ .../Test-xAzDoOrganizationGroup.tests.ps1 | 77 ++ .../xAzDoProject/Get-xAzDoProject.tests.ps1 | 117 +++ .../xAzDoProject/New-xAzDoProject.tests.ps1 | 143 +++ .../Remove-xAzDoProject.tests.ps1 | 97 ++ .../xAzDoProject/Set-xAzDoProject.tests.ps1 | 94 ++ .../xAzDoProject/Test-xAzDoProject.tests.ps1 | 78 ++ .../Get-xAzDoProjectGroup.tests.ps1 | 148 ++++ .../New-xAzDoProjectGroup.tests.ps1 | 106 +++ .../Remove-xAzDoProjectGroup.tests.ps1 | 121 +++ .../Set-xAzDoProjectGroup.tests.ps1 | 129 +++ .../Test-xAzDoProjectGroup.tests.ps1 | 124 +++ ...Get-AzDevOpsServicesApiUri.Tests.disabled} | 0 ...=> Get-AzDevOpsServicesUri.Tests.disabled} | 0 .../TestHelpers/CommonTestFunctions.psm1 | 109 +++ ...c.Common.Resources.Functions.Tests.Old.ps1 | 161 ---- 417 files changed, 26689 insertions(+), 5142 deletions(-) create mode 100644 USAGE.md delete mode 100644 source/Classes/000.ManagedIdentityDataResources.ps1 create mode 100644 source/Classes/001.AuthenticationToken.ps1 create mode 100644 source/Classes/002.PersonalAccessToken.ps1 rename source/Classes/{004.ManagedIdentityToken.ps1 => 003.ManagedIdentityToken.ps1} (51%) rename source/Classes/{001.DscResourceBase.ps1 => 004.DscResourceBase.ps1} (100%) rename source/Classes/{002.AzDevOpsApiDscResourceBase.ps1 => 005.AzDevOpsApiDscResourceBase.ps1} (96%) rename source/Classes/{003.AzDevOpsDscResourceBase.ps1 => 006.AzDevOpsDscResourceBase.ps1} (54%) rename source/Classes/{005.APIRateLimit.ps1 => 007.APIRateLimit.ps1} (93%) create mode 100644 source/Classes/009.xAzDoGroupPermission.ps1 delete mode 100644 source/Classes/010.AzDevOpsProject.ps1 create mode 100644 source/Classes/011.xAzDoOrganizationGroup.ps1 create mode 100644 source/Classes/020.xAzDoProject.ps1 create mode 100644 source/Classes/021.xAzDoProjectServices.ps1 create mode 100644 source/Classes/022.xAzDoProjectGroup.ps1 create mode 100644 source/Classes/031.xAzDoGroupMember.ps1 create mode 100644 source/Classes/040.xAzDoGitRepository.ps1 create mode 100644 source/Classes/041.xAzDoGitPermission.ps1 create mode 100644 source/Enum/DSCGetSummaryState.ps1 create mode 100644 source/Enum/DescriptorType.ps1 create mode 100644 source/Enum/TokenType.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 rename tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.Get-AzDevOpsApiResourceUri.Tests.ps1 => source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACE/Empty.txt (100%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.ps1 rename tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.New-AzDevOpsApiResource.Tests.ps1 => source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ApiResource/Empty.txt (100%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.ps1 rename tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.Remove-AzDevOpsApiResource.Tests.ps1 => source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Permission/Empty.txt (100%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.ps1 rename tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.Set-AzDevOpsApiResource.Tests.ps1 => source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Resource/Empty.txt (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/_TODO2.New-AzDevOpsProject.Tests.ps1 => source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/RoleAssignments/Empty.txt (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/_TODO2.Remove-AzDevOpsProject.Tests.ps1 => source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/RoleDefinition/empty.txt (100%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.ps1 rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/_TODO2.Set-AzDevOpsProject.Tests.ps1 => source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ServiceEndpoint/empty.txt (100%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ServicePrincipal/empty.txt create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Authentication/ManagedIdentity}/Update-AzManagedIdentity.ps1 (81%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.ps1 rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{Test-AzManagedIdentityToken.ps1 => Authentication/Test-AzToken.ps1} (56%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResource.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzManagedIdentityToken.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.ps1 rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper/API}/Test-AzDevOpsApiHttpRequestHeader.ps1 (77%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper/API}/Test-AzDevOpsApiResourceId.ps1 (96%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper/API}/Test-AzDevOpsApiTimeoutExceeded.ps1 (100%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper/API}/Test-AzDevOpsApiUri.ps1 (85%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper/API}/Test-AzDevOpsApiVersion.ps1 (94%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper/API}/Wait-AzDevOpsApiResource.ps1 (100%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroupMember.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.ps1 rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper}/Get-AzDevOpsApiHttpRequestHeader.ps1 (100%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper}/Get-AzDevOpsApiResourceName.ps1 (100%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper}/Get-AzDevOpsApiResourceUri.ps1 (100%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper}/Get-AzDevOpsApiUriAreaName.ps1 (100%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper}/Get-AzDevOpsApiUriResourceName.ps1 (100%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper}/Get-AzDevOpsApiVersion.ps1 (84%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper}/Get-AzDevOpsApiWaitIntervalMs.ps1 (100%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/{ => Helper}/Get-AzDevOpsApiWaitTimeoutMs.ps1 (100%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Flush-Log.txt create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/New-AzDevOpsApiResource.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Remove-AzDevOpsApiResource.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Set-AzDevOpsApiResource.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResource.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceName.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/LocalizedData/001.LocalizedDataAzResourceTokenPatten.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/LocalizedData/002.LocalizedDataAzSerializationPatten.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/LocalizedData/003.LocalizedDataAzURLParams.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-ObjectProperty.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsProject.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDevOpsProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzManagedIdentity.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Remove-AzDevOpsProject.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Set-AzDevOpsProject.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Test-AzDevOpsProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Get-xAzDoProjectServices.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/New-xAzDoProjectServices.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Remove-xAzDoProjectServices.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Set-xAzDoProjectServices.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Test-xAzDoProjectServices.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Initalize-AzDevOpsCache.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 create mode 100644 source/WikiSource/Resources/xAzDoGitPermission.md create mode 100644 source/WikiSource/Resources/xAzDoGitRepository.md create mode 100644 source/WikiSource/Resources/xAzDoGroupMember.md create mode 100644 source/WikiSource/Resources/xAzDoGroupPermission.md create mode 100644 source/WikiSource/Resources/xAzDoOrganizationGroup.md create mode 100644 source/WikiSource/Resources/xAzDoProject.md create mode 100644 source/WikiSource/Resources/xAzDoProjectGroup.md create mode 100644 source/WikiSource/Resources/xAzDoProjectServices.md create mode 100644 tests/DSC/InModuleScope.ps1 rename tests/Integration/DSCClassResources/{ => Archive}/AzDevOpsProject.Integration.Tests.ps1 (100%) rename tests/Integration/DSCClassResources/{ => Archive}/AzDevOpsProject.config.ps1 (100%) create mode 100644 tests/Integration/Invoke-Tests.ps1 create mode 100644 tests/Integration/Resources/xAzDoGitPermission.tests.ps1 create mode 100644 tests/Integration/Resources/xAzDoGitRepository.tests.ps1 create mode 100644 tests/Integration/Resources/xAzDoGroupMember.tests.ps1 create mode 100644 tests/Integration/Resources/xAzDoGroupPermission.tests.ps1 create mode 100644 tests/Integration/Resources/xAzDoOrganizationGroup.Description.tests.ps1 create mode 100644 tests/Integration/Resources/xAzDoOrganizationGroup.NoDescription.tests.ps1 create mode 100644 tests/Integration/Resources/xAzDoProject.Description.tests.ps1 create mode 100644 tests/Integration/Resources/xAzDoProject.NoDescription.tests.ps1 create mode 100644 tests/Integration/Resources/xAzDoProjectGroup.Description.tests.ps1 create mode 100644 tests/Integration/Resources/xAzDoProjectServices.tests.ps1 create mode 100644 tests/Integration/Supporting/API/Add-Header.ps1 create mode 100644 tests/Integration/Supporting/API/ConvertTo-Base64String.ps1 create mode 100644 tests/Integration/Supporting/API/Get-AzDevOpsApiVersion.ps1 create mode 100644 tests/Integration/Supporting/API/Get-MIToken.ps1 create mode 100644 tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 create mode 100644 tests/Integration/Supporting/API/New-AuthProvider.ps1 create mode 100644 tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 create mode 100644 tests/Integration/Supporting/APICalls/List-DevOpsProjects.ps1 create mode 100644 tests/Integration/Supporting/APICalls/Remove-DevOpsGroup.ps1 create mode 100644 tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 create mode 100644 tests/Integration/Supporting/Functions/SupportingFunctions.ps1 create mode 100644 tests/Integration/Supporting/Initalize-TestFramework.ps1 create mode 100644 tests/Integration/Supporting/Teardown.ps1 create mode 100644 tests/Integration/TestFrameworkConfiguration.json create mode 100644 tests/Unit/Classes/API/007.APIRateLimit.tests.ps1 create mode 100644 tests/Unit/Classes/Authentication/001.AuthenticationToken.tests.ps1 create mode 100644 tests/Unit/Classes/Authentication/002.PersonalAccessToken.tests.ps1 create mode 100644 tests/Unit/Classes/Authentication/003.ManagedIdentityToken.tests.ps1 delete mode 100644 tests/Unit/Classes/AzDevOpsApiDscResourceBase/AzDevOpsApiDscResourceBase.Initialization.Tests.ps1 create mode 100644 tests/Unit/Classes/Classes.BeforeAll.ps1 delete mode 100644 tests/Unit/Classes/Classes.TestInitialization.ps1 delete mode 100644 tests/Unit/Classes/ManagedIdentity/APIRateLimit.tests.ps1 delete mode 100644 tests/Unit/Classes/ManagedIdentity/ManagedIdentityToken.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/020.xAzDevOpsProject.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common.Functions.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common.Tests.Initialization.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiHttpRequestHeader.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResource.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResourceName.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriAreaName.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriResourceName.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitIntervalMs.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitTimeoutMs.Tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiHttpRequestHeader.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceName.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceUri.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriAreaName.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriResourceName.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitIntervalMs.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitTimeoutMs.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResource.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceId.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceName.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiTimeoutExceeded.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiUri.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiVersion.Tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Wait-AzDevOpsApiResource.Tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.InvokeTests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.TestInitialization.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzDevOpsCache.ps1.disabled rename tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/{Get-AzManagedIdentityToken.Tests.ps1 => Archive/Get-AzManagedIdentityToken.Tests.ps1.disabled} (92%) create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsACLToken.Tests.ps1.disabled create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiCache.Tests.ps1.disabled rename tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/{New-AzDevOpsApiResource.Tests.ps1 => Archive/New-AzDevOpsApiResource.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/{New-AzManagedIdentity.Tests.ps1 => Archive/New-AzManagedIdentity.Tests.ps1.disabled} (87%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/{Test-AzDevOpsPatCredential.Tests.ps1 => Archive/Test-AzDevOpsPatCredential.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/{Test-AzManagedIdentityToken.Tests.ps1 => Archive/Test-AzManagedIdentityToken.Tests.ps1.disabled} (77%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/{Update-AzManagedIdentity.Tests.ps1 => Archive/Update-AzManagedIdentity.Tests.ps1.disabled} (90%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/{Test-AzDevOpsOperationId.Tests.ps1 => Test-AzDevOpsOperationId.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/{Test-AzDevOpsOrganizationName.Tests.ps1 => Test-AzDevOpsOrganizationName.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/{Test-AzDevOpsPat.Tests.ps1 => Test-AzDevOpsPat.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/{Test-AzDevOpsProjectDescription.Tests.ps1 => Test-AzDevOpsProjectDescription.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/{Test-AzDevOpsProjectId.Tests.ps1 => Test-AzDevOpsProjectId.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/{Test-AzDevOpsProjectName.Tests.ps1 => Test-AzDevOpsProjectName.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/{Wait-AzDevOpsOperation.Tests.ps1 => Wait-AzDevOpsOperation.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{Get-AzDevOpsOperation.Tests.ps1 => Archive/Get-AzDevOpsOperation.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{Get-AzDevOpsProject.Tests.ps1 => Archive/Get-AzDevOpsProject.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{New-AzDevOpsProject.Tests.Old.ps1 => Archive/New-AzDevOpsProject.Tests.Old.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{Test-AzDevOpsOperation.Tests.ps1 => Archive/Test-AzDevOpsOperation.Tests.ps1.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{Test-AzDevOpsProject.Tests.ps1 => Archive/Test-AzDevOpsProject.Tests.ps1.disabled} (100%) create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 rename tests/Unit/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/{Get-AzDevOpsServicesApiUri.Tests.ps1 => Get-AzDevOpsServicesApiUri.Tests.disabled} (100%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/{Get-AzDevOpsServicesUri.Tests.ps1 => Get-AzDevOpsServicesUri.Tests.disabled} (100%) create mode 100644 tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1 delete mode 100644 tests/Unit/Modules/_USEFUL.AzureDevOpsDsc.Common.Resources.Functions.Tests.Old.ps1 diff --git a/.gitignore b/.gitignore index 3239758a6..27c9e187a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,14 @@ output/ +.devcontainer/ +internal-tests/ +LCM/Datum/* **.bak *.local.* !**/README.md .kitchen/ +*.clixml *.suo *.user *.coverage @@ -16,3 +20,7 @@ node_modules package-lock.json CodeCoverage.JaCoCo.xml +.vscode/launch.json +Temp/* +TestProject.yml +TestProject2.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index e8c637ab7..d5fb5979e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - AzureDevOpsDsc - - Azure Managed Identity supporting classes. These classes are used by `AzureDevOpsDsc.Common`. + - Azure Managed Identity supporting classes. These classes are used by 'AzureDevOpsDsc.Common'. - Updated pipeline files to support change of default branch to main. - Added GitHub issue templates and pull request template ([issue #1](https://github.com/dsccommunity/AzureDevOpsDsc/issues/1)) @@ -30,7 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 file `Home.md` will be updated with the correct module version on each publish to gallery (including preview). - AzureDevOpsDsc.Common - - Managed Identity has been added to the system. This feature can be used before invoking Invoke-DSCResource. With New-AzManagedIdentity, the bearer token is automatically authenticated, retrieved, and managed. It’s worth noting that bearer tokens take precedence over basic tokens. When using Invoke-AzDevOpsApiRestMethod, the token is automatically interpolated as required. + - Managed Identity has been added to the system. This feature can be used before invoking Invoke-DSCResource. With New-AzManagedIdentity, the bearer token is automatically authenticated, retrieved, and managed. It's worth noting that bearer tokens take precedence over basic tokens. When using Invoke-AzDevOpsApiRestMethod, the token is automatically interpolated as required. - Added 'wrapper' functionality around the [Azure DevOps REST API](https://docs.microsoft.com/en-us/rest/api/azure/devops/) - Added Supporting Functions for Azure Managed Identity. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cafa70440..8d0765878 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,21 +15,23 @@ The `AzureDevOpsDsc` module consists of a few key components: * The nested, `AzureDevOpsDsc.Common` module, which itself, consists of the following sets of functions/commands: - * `Api` - These are used as generic wrappers around the - Azure DevOps REST API), aimed to minimise duplication of functionality and - code across all `Resources`. + * `Api` - These functions serve as generic wrappers around the Azure DevOps REST API, designed to minimize code duplication and functionality overlap across all resources. The private functions directory is organized into the following categories: - * `Connection` - These support connection to the Azure DevOps REST API. + * `Api` - Contains common functions that perform API calls to the Azure DevOps API using `Invoke-AZDORestMethod`. + * `Authentication` - Includes common functions that manage authentication with the Azure DevOps API. + * `Cache` - Houses common functions that handle caching within Azure DevOps. + > **Note:* The `Cache Initialization` directory is an ordered directory used to initialize and populate the cache when the module is loaded. + * `Helper` - Contains common helper functions used by resource functions to perform specific tasks. - * `Resources` - These invoke the `Api` functions/commands to - manage specific, Azure DevOps REST API resources (e.g. `Projects`). + * `Resources` - These invoke the `Api` functions/commands to + manage specific, Azure DevOps REST API resources (e.g. `Projects`). - * `Server` - These are specific to Azure DevOps - **Server** - the self-hosted, typically on-premise, edition of Azure DevOps. + * `Server` - These are specific to Azure DevOps + **Server** - the self-hosted, typically on-premise, edition of Azure DevOps. - * `Services` - These are specific to Azure DevOps - **Services** - the Microsoft, cloud-hosted, 'Software-as-a-Service' (SaaS) - solution. + * `Services` - These are specific to Azure DevOps + **Services** - the Microsoft, cloud-hosted, 'Software-as-a-Service' (SaaS) + solution. The layout/structure of the module and it's resources/components is designed to: @@ -39,12 +41,811 @@ The layout/structure of the module and it's resources/components is designed to: * Minimise the complexity of the DSC Resources themselves, in turn, aiming to: * Reduce the amount of new functionality and effort required to add new, DSC Resources * Increase the reliability and robustness of the DSC Resources -* Allow all the DSC Resources to be able to use independent, Personal Access Tokens - (PATs) to allow distinct PATs, with distinct privileges, to perform distinct - operations (as opposed to requiring a single PAT with excess privileges). --- +## Using the Caching Mechanism + +The caching mechanism implemented in this module leverages CLIXML (Command-Line Interface XML) to store cached content. This approach ensures that the cached data is serialized into a structured XML format. + +### Cache Initialization + +Before the cache can be used, it must first be loaded from disk. The behavior during initialization varies based on the type of cache: + +* **Conditional Clearing:** The decision to clear the cache upon initialization depends on its type. +* **'Live' Prefix:** If the cache name begins with the prefix 'Live', it will be cleared when loaded into memory. This is because this cache type represents the current live environment. + +This method guarantees that caches containing live data are always current, while other cache types maintain their previously stored content. +After the cache is initialized, the `Live*` cache types are populated using functions located in `source\Modules\AzureDevOpsDsc.Common\Api\Functions\Private\Cache\Cache Initialization`. +These functions run sequentially and create or add items as necessary. + +To start the cache process, simply call `New-AzDoAuthenticationProvider`. +For manual initialization: + +```powershell +# Initialize the Cache + +# Initialize the Cache Objects +Get-AzDoCacheObjects | ForEach-Object { + Initialize-CacheObject -CacheType $_ +} + +# Iterate through each of the caching commands and initialize the cache. +Get-Command "AzDoAPI_*" | Where-Object Source -eq 'AzureDevOpsDsc.Common' | ForEach-Object { + . $_.Name -OrganizationName $AzureDevopsOrganizationName -Verbose +} +``` + +### Cache Update Requirements + +Certain resources will need cache updates when their configuration changes. +Please ensure you update the cache accordingly. +If you are dealing with identity-based caches that utilize permissions, it is crucial to include the `ACLIdentity` property attached to the object. +The `ACLIdentity` property is used by the ACL/ACE helper functions to create ACEs/ACLs for the respective security namespace. + +For Example: + +``` PowerShell +$identity = Get-DevOpsDescriptorIdentity @params -SubjectDescriptor $AzDoLiveGroup.value.descriptor + +$ACLIdentity = [PSCustomObject]@{ + id = $identity.id + descriptor = $identity.descriptor + subjectDescriptor = $identity.subjectDescriptor + providerDisplayName = $identity.providerDisplayName + isActive = $identity.isActive + isContainer = $identity.isContainer +} + +$AzDoLiveGroup.value | Add-Member -MemberType NoteProperty -Name 'ACLIdentity' -Value $ACLIdentity + +$cacheParams = @{ + Key = $AzDoLiveGroup.Key + Value = $AzDoLiveGroup + Type = 'LiveGroups' + SuppressWarning = $true +} + +# Add to the cache +Add-CacheItem @cacheParams +``` + +**Note:** This applies only to objects that do not have the `ACLIdentity` property. + +If you are updating the cache after modifying an *existing* object, you can use `Refresh-CacheIdentity` to update the object: + +``` PowerShell + # Update the cache with the new group + Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' +``` + +### Adding an Item to the Cache + +To add an item to the cache use the command `Add-CacheItem` to add the item into the cache memory. +> Please note that it only exists in memory. + +``` PowerShell +Add-CacheItem -Key 'KeyName' -Value $Object -Type 'Type' +``` + +### Writing a CacheItem to Disk + +To save the cache to the disk, you can use the `Export-CacheObject` command. +This function writes the specified cache content to a file on your disk. + +Here is an example of how to export the cache to a file: + +```PowerShell +# Export the 'LiveUsers' cache to a file +Export-CacheObject -CacheType 'LiveUsers' -Content $AzDoLiveUsers +``` + +In this example: +- `-CacheType 'LiveUsers'` specifies the type of cache being exported. +- `-Content $AzDoLiveUsers` provides the actual cache data to be written to the disk. + +### Getting a Cache Item + +To retrieve a cache item, you can use either the `Get-CacheItem` or `Find-CacheItem` functions. + +### `Get-CacheItem` + +The `Get-CacheItem` function is used to perform lookups based on the key set within the cache. +This is useful when you know the specific key of the item you want to retrieve. For example: + +```PowerShell +# Check the cache for the group using its key +$livegroup = Get-CacheItem -Key $Key -Type 'LiveGroups' +``` + +### `Find-CacheItem` + +The `Find-CacheItem` function is used to search for calculated properties against a __cache list__. +This is helpful when you need to perform more complex queries, such as checking if an item with certain criteria exists in the cache. For example: + +```PowerShell +# Perform a lookup in the live cache to see if the group has been deleted and recreated +$renamedGroup = $livegroup | Find-CacheItem { $_.originId -eq $livegroup.originId } +``` + +In summary, use `Get-CacheItem` for direct lookups by key and `Find-CacheItem` for more advanced searches based on calculated properties. + +### Adding a new Cache Type + +To introduce a new cache type into the project, several steps must be completed. + +First, find the `Get-AzDoCacheObjects` function and insert the new cache item type into the array. +The `Get-AzDoCacheObjects` function is utilized by components within the module to validate the cache type. +> Note: If this cache item requires updating each time the module loads, prefix it with `'Live*'`. + +``` PowerShell + return @( + 'Project', + 'Team', + 'Group', + 'SecurityDescriptor', + 'LiveGroups', + 'LiveProjects', + 'LiveUsers', + 'LiveGroupMembers', + 'LiveRepositories', + 'LiveServicePrinciples', + 'LiveACLList', + 'LiveProcesses', + 'SecurityNamespaces', + 'NewCacheItem' + ) +``` + +### Getting an Entire Cache List + +There are two ways to retrieve the cache list. +You can retrieve the cache using `Get-CacheObject -CacheType $Type`, or you can directly access the variable in memory by prefixing `AZDO` with the cache type (e.g., `"AZDO$Type"`). + +For example, you can use `Get-CacheObject` to retrieve a list of items from the `LiveGroups` cache: + +``` PowerShell +$AzDoLiveGroups = Get-CacheObject -CacheType 'LiveGroups' +``` + +In this example, you can directly access the variable stored in memory and export its contents to the cache: + +```PowerShell +# Export the contents of the $AzDoLiveProjects variable to the LiveProjects cache +Export-CacheObject -CacheType 'LiveProjects' -Content $AzDoLiveProjects +``` + +This command uses the `Export-CacheObject` cmdlet to save the data from the `$AzDoLiveProjects` variable into the `LiveProjects` cache. +The `-CacheType` parameter specifies the type of cache, while the `-Content` parameter provides the actual data to be cached. + +# Creating your First Resource + +## Resource Template + +Below is a template for creating a class-based DSC resource in PowerShell. + +```PowerShell +[DscResource()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] +class ClassName : AzDevOpsDscResourceBase +{ + [DscProperty(Key, Mandatory)] + [Alias('Parameter')] + [System.String]$ResourceParameter + + ClassName() + { + $this.Construct() + } + + [ClassName] Get() + { + return [xAzDoProject]$($this.GetDscCurrentStateProperties()) + } + + hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() + { + # Properties that don't support Set, yet support New/Remove + return @('') + } + + hidden [Hashtable]GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) + { + $properties = @{ + Ensure = [Ensure]::Absent + } + + # If the resource object is null, return the properties + if ($null -eq $CurrentResourceObject) { return $properties } + + $properties.ResourceParameter = $CurrentResourceObject.ResourceParameter + $properties.LookupResult = $CurrentResourceObject.LookupResult + $properties.Ensure = $CurrentResourceObject.Ensure + + return $properties + } +} +``` + +## Create the Class Based Resource + +To create a class-based DSC Resource, follow the template provided above. +This will serve as the foundation for defining the resource's properties and methods. + +## Write Documentation for Resource + +### Overview + +When documenting your resource, it is essential to use the Get-Help format to ensure consistency and clarity. +This format helps users understand each property and method of your resource, providing them with the necessary information to utilize it effectively. + +### Naming the Resource + +The resource name must start with the prefix xAzDo, such as xAzDoProject. + +### Guidelines for Documentation + +1. **Clear and Concise Information**: + * Ensure that the description of each property and method is straightforward and easy to understand. + * Avoid unnecessary jargon or overly technical language that may confuse the user. + +1. **Detailed Descriptions**: + * Provide detailed explanations for each property and method. + * Explain what each property represents and how each method functions within the resource. + +1. **Examples**: + * Include examples where necessary to illustrate usage. + * Examples should be relevant and demonstrate common use cases to help users understand how to apply the resource in real-world scenarios. + +### Get-Help Format + +Below is a template you can follow to document your resource using the Get-Help format: + +``` PowerShell +<# +# .SYNOPSIS +Briefly describe what the resource does. + +# .DESCRIPTION +Provide a more detailed explanation of the resource, including its purpose and functionality. + +# .PARAMETER +Describe each parameter required by the resource. Include details such as data type, default values, and any constraints. + +# .EXAMPLE +Show an example of how to use the resource. Include both the code and an explanation of what the example demonstrates. + +# .NOTES +Include any additional information that might be relevant, such as author details, version history, or related resources. + +# .LINK +Provide links to any related documentation or external resources. +#> +``` + +### Example Documentation + +Here’s an example of how you might document a sample resource: + +```Powershell +<# +# .SYNOPSIS +This resource manages the configuration of a web server. + +# .DESCRIPTION +The WebServerResource allows administrators to configure various aspects of a web server, including setting up virtual hosts, managing security settings, and configuring modules. + +# .PARAMETER ServerName +Specifies the name of the web server. This is a mandatory parameter. +Type: String +Default value: None + +# .PARAMETER Port +Specifies the port on which the web server listens. +Type: Integer +Default value: 80 + +# .EXAMPLE +PS C:\> Set-WebServerConfiguration -ServerName "MyWebServer" -Port 8080 + +This command configures the web server named 'MyWebServer' to listen on port 8080. + +# .NOTES +Author: Jane Doe +Version: 1.0.0 + +# .LINK +https://docs.example.com/WebServerResource +#> +``` + +By following these guidelines and utilizing the Get-Help format, you can create comprehensive and user-friendly documentation for your resource. + +## Create `Get`, `Test`, `Set`, `New`, `Remove` Functions + +### Lifecycle Management Functions for DSC Resources + +These functions are crucial for managing the lifecycle of your Desired State Configuration (DSC) resource. They are located at: + +`source\Modules\AzureDevOpsDsc.Common\Resources\Functions\Public\ResourceName` + +When creating these functions, make sure to add them to the `FunctionsToExport` section in the file located at: + +`source\Modules\AzureDevOpsDsc.Common\AzureDevOpsDsc.Common.psd1`. + +#### Function Parameters + +The parameters of these functions must be consistent and should reflect the properties of the resource. For instance: + +##### Resource Definition +```PowerShell +class ClassName : AzDevOpsDscResourceBase { + [DscProperty(Key, Mandatory)] + [Alias('Parameter')] + [System.String]$ResourceParameter +} +``` + +##### Get-Function Example +```PowerShell +Function Get-ClassName { + [CmdletBinding()] + param ( + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure + ) +} +``` + +#### Additional Parameters + +Include additional parameters such as `Ensure` and `LookupResult` to ensure comprehensive functionality. +Here's an example: + +```PowerShell +Function Get-ClassName { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ResourceParameter, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure + ) +} +``` + +By following this structure, you ensure that your DSC resource management functions are well-defined and consistent with the resource properties. + +#### The `Get` Function + +The `Get` function performs the lookup of the resource properties and calculates what has changed. +The changed contents are stored within the `LookupResult.PropertiesChanged` property. + +```PowerShell +Function Get-xAzDoProjectServices { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Repos')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$GitRepositories = 'Enabled', + + [Parameter()] + [Alias('Board')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$WorkBoards = 'Enabled', + + [Parameter()] + [Alias('Pipelines')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$BuildPipelines = 'Enabled', + + [Parameter()] + [Alias('Tests')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$TestPlans = 'Enabled', + + [Parameter()] + [Alias('Artifacts')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$AzureArtifact = 'Enabled', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # + # Construct a hashtable detailing the group + + $Result = @{ + #Reasons = $() + Ensure = [Ensure]::Absent + propertiesChanged = @() + status = [DSCGetSummaryState]::Unchanged + } + + # + # Attempt to retrive the Project from the Live Cache. + Write-Verbose "[Get-xAzDevOpsProjectServices] Retriving the Project from the Live Cache." + + # Retrive the Repositories from the Live Cache. + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + # If the Project does not exist in the Live Cache, return the Project object. + if ($null -eq $Project) { + Write-Warning "[Get-xAzDevOpsProjectServices] The Project '$ProjectName' was not found in the Live Cache." + $Result.Status = [DSCGetSummaryState]::NotFound + return $Result + } + + $params = @{ + Organization = $Global:DSCAZDO_OrganizationName + ProjectId = $Project.id + } + + # Enumerate the Project Services. + $Result.LiveServices = @{ + Repos = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Repos + Boards = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Boards + Pipelines = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Pipelines + Tests = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_TestPlans + Artifacts = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Artifacts + } + + # Compare the Project Services with the desired state. + if ($GitRepositories -ne $Result.LiveServices.Repos.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $GitRepositories + FeatureId = $LocalizedDataAzURLParams.ProjectService_Repos + } + } + if ($WorkBoards -ne $Result.LiveServices.Boards.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $WorkBoards + FeatureId = $LocalizedDataAzURLParams.ProjectService_Boards + } + } + if ($BuildPipelines -ne $Result.LiveServices.Pipelines.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $BuildPipelines + FeatureId = $LocalizedDataAzURLParams.ProjectService_Pipelines + } + } + if ($TestPlans -ne $Result.LiveServices.Tests.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $TestPlans + FeatureId = $LocalizedDataAzURLParams.ProjectService_TestPlans + } + } + if ($AzureArtifact -ne $Result.LiveServices.Artifacts.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $AzureArtifact + FeatureId = $LocalizedDataAzURLParams.ProjectService_Artifacts + } + } + + return $Result + +} +``` + +### The `LookupResult` Property + +The `LookupResult` property holds the results of the resource lookup and any changes detected. + +Example: + +```PowerShell +# Enumerate the Project Services. +$Result.LiveServices = @{ + Repos = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Repos + Boards = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Boards + Pipelines = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Pipelines + Tests = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_TestPlans + Artifacts = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Artifacts +} + +# Compare the Project Services with the desired state. +if ($GitRepositories -ne $Result.LiveServices.Repos.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $GitRepositories + FeatureId = $LocalizedDataAzURLParams.ProjectService_Repos + } +} +``` + +This example demonstrates how to enumerate project services and compare them against the desired state, updating the `LookupResult` property accordingly. + +#### The `Status` Property + +The `Status` property provides detailed information about the changes identified by the `get` method. +This property indicates the specific status of the item being evaluated, allowing for a clear understanding of its current state. + +#### Possible Status Values + +* **Changed**: Indicates that the item has undergone modifications. +* **Unchanged**: Signifies that the item remains in its original state with no alterations. +* **NotFound**: Denotes that the item could not be located within AZDO. +* **Renamed**: Implies that the item has been renamed from its original identifier. +* **Missing**: Suggests that the item is absent or has been deleted within AZDO. + +Each status value aids in classifying the result of the `get` method, offering clear insights into the changes affecting the item. +This information is utilized by the `Test` method, along with the `Ensure` enum, to determine the appropriate course of action. + +For Example: + +``` PowerShell +# +# Construct a hashtable detailing the group +$result = @{ + Ensure = [Ensure]::Absent + ProjectName = $ProjectName + # Other Key/Value items + propertiesChanged = @() + status = $null +} + +# Test if the project exists. If the project does not exist, return NotFound +if (($null -eq $project) -and ($null -ne $ProjectName)) +{ + $result.Status = [DSCGetSummaryState]::NotFound + return $result +} +``` + +#### Making API Calls + +API calls are not made directly within the Get, Set, Test, New, and Remove functions. +Instead, they are abstracted to the `source\Modules\AzureDevOpsDsc.Common\Api\Functions\Private\Api` directory. +This directory contains functions that handle API calls to the respective endpoints. + +Here are some sample code snippets: + +``` PowerShell +Write-Verbose "[New-GitRepository] Creating new repository '$($RepositoryName)' in project '$($Project.name)'" + +# Define parameters for creating a new DevOps group +$params = @{ + ApiUri = "{0}/{1}/_apis/git/repositories?api-version={2}" -f $ApiUri, $Project.name, $ApiVersion + Method = 'POST' + ContentType = 'application/json' + Body = @{ + name = $RepositoryName + project = @{ + id = $Project.id + } + } | ConvertTo-Json +} + +# Try to invoke the REST method to create the group and return the result +try { + $repo = Invoke-AzDevOpsApiRestMethod @params + Write-Verbose "[New-GitRepository] Repository Created: '$($repo.name)'" + return $repo +} +# Catch any exceptions and write an error message +catch { + Write-Error "[New-GitRepository] Failed to Create Repository: $_" +} +``` + +When writing API calls, utilize `Invoke-AzDevOpsApiRestMethod` to execute the call. +This function will automatically inject authentication headers, manage pagination, and handle rate-limiting. + +# Integrating Enhanced Authentication Mechanisms + +With the continuous release of new technologies by Microsoft, this module is engineered to support a variety of advanced authentication mechanisms. +This ensures compatibility with the latest security standards and provides flexibility in choosing the most suitable authentication method for your needs. + +Important: The current authentication mechanism depends on the Authorization HTTP header. + +This means that all authentication requests must include the appropriate credentials within the Authorization header to ensure secure access. +This method is crucial for maintaining the integrity and confidentiality of the data being transmitted. + +## 1. Create an Authentication Class + +To create an authentication class, you need to add a new class within the `source\Classes` directory. +Ensure that this class is declared before the `[DSCResourceBase]` class. +Additionally, include a global function named `New-ClassName` in this class, which will be invoked by nested modules. + +> **Important:** It's acceptable to adjust the execution order to accommodate the class. + +### Implementation Template + +Below is a template for creating a sample authentication class called `SampleAuthenticationType`. +This class inherits from the `AuthenticationToken` base class and includes methods for validating tokens, checking expiration, and retrieving the access token. +The global function `New-SampleAuthenticationType` is also provided to instantiate the class. + +```PowerShell +# Define the SampleAuthenticationType class +Class SampleAuthenticationType : AuthenticationToken { + + # Property to store the value of the authentication token + [Property]$Value + + # Constructor to initialize the SampleAuthenticationType instance + SampleAuthenticationType() { + } + + # Hidden function to validate the ManagedIdentityTokenObj + Hidden [Bool]isValid($ManagedIdentityTokenObj) { + } + + # Function to check if the token has expired + [Bool]isExpired() { + } + + # Function to return the access token as a string + [String] Get() { + + # Test the caller + $this.TestCaller() + + # Return the access token + return ($this.ConvertFromSecureString($this.access_token)) + + } +} + +# Global function to create a new SampleAuthenticationType object +Function global:New-SampleAuthenticationType ([PSCustomObject]$Obj) { + # Create and return a new SampleAuthenticationType object + return [SampleAuthenticationType]::New($Obj) +} +``` + +### Detailed Explanation + +#### Class Definition + +* **Class Declaration**: The `SampleAuthenticationType` class is defined and inherits from the `AuthenticationToken` base class. + +* **Properties**: + * `$Value`: A property to store the value of the authentication token. + +* **Constructor**: + * `SampleAuthenticationType()`: A constructor method to initialize instances of the `SampleAuthenticationType` class. + +* **Methods**: + * `isValid($ManagedIdentityTokenObj)`: A hidden method to validate the provided managed identity token object. + * `isExpired()`: A method to check if the current token has expired. + * `Get()`: A method to return the access token as a string. This is used by the Modules + + > **IMPORTANT**: All tokens must be stored as `[SecureString]` in memory to prevent accidental leakage. Additionally, the `Get()` method can only be invoked by approved functions/methods to further mitigate the risk. + +#### Global Function + +* **New-SampleAuthenticationType**: + * A global function that creates and returns a new instance of the `SampleAuthenticationType` class using a provided `PSCustomObject`. + +### Usage Example + +Here is an example of how to use the `SampleAuthenticationType` class and its associated global function: + +```PowerShell +# Create a PSCustomObject with necessary properties +$customObject = [PSCustomObject]@{ + Property1 = "Value1" + Property2 = "Value2" +} + +# Create a new SampleAuthenticationType object +$authToken = New-SampleAuthenticationType -Obj $customObject + +# Check if the token is valid +$isValid = $authToken.isValid($managedIdentityTokenObj) + +# Check if the token is expired +$isExpired = $authToken.isExpired() + +# Get the access token +$accessToken = $authToken.Get() +``` + +By following this template and detailed explanation, you can create a robust authentication class that integrates seamlessly with your PowerShell DSC resources and nested modules. + +## 2. Integrate the Authentication Mechanism into `New-AzDoAuthenticationProvider` + +In this step, you need to incorporate the authentication mechanism within the `New-AzDoAuthenticationProvider` function. +Ensure that the necessary authentication protocols are properly implemented to facilitate secure access. + +## 3. Integrate Changes into `Add-AuthenticationHTTPHeader` + +Next, apply the changes to the `Add-AuthenticationHTTPHeader` function. +This involves updating the function to include the new authentication headers, ensuring that each HTTP request is authenticated correctly. + +# Enable Verbose Logging + +> **IMPORTANT**: Enabling verbose logging will impact performance. + +To enable `Write-Verbose` logging, follow these steps: + +1. **Create a System Environment Variable**: + * Name the variable `AZDO_VERBOSELOGGING_FILEPATH`. + * Set the value of this variable to the desired file path where you want the verbose logs to be stored. + +1. **Steps to Create the Environment Variable**: + * **Windows**: + 1. Open the Start Menu and search for "Environment Variables". + 1. Select "Edit the system environment variables". + 1. In the System Properties window, click on the "Environment Variables" button. + 1. In the Environment Variables window, under the "System variables" section, click "New". + 1. Enter `AZDO_VERBOSELOGGING_FILEPATH` as the variable name. + 1. Enter the full path of the file where you want the logs to be saved as the variable value (e.g., `C:\Logs\AzDoVerbose.log`). + 1. Click "OK" to save the new variable. + 1. Click "OK" again to close the Environment Variables window, and then "OK" to close the System Properties window. + + * **Linux/macOS**: + 1. Open a terminal window. + 1. Edit your shell profile file (e.g., `~/.bashrc`, `~/.zshrc`) using a text editor. + 1. Add the following line to set the environment variable: + ```sh + export AZDO_VERBOSELOGGING_FILEPATH="/path/to/your/logfile.log" + ``` + 1. Save the file and close the text editor. + 1. Source the profile file to apply the changes: + ```sh + source ~/.bashrc # or source ~/.zshrc depending on your shell + ``` + +1. **Verify the Environment Variable**: + * To ensure that the environment variable is set correctly, you can use the following command: + * **Windows** (Command Prompt): + ```cmd + echo %AZDO_VERBOSELOGGING_FILEPATH% + ``` + * ***Windows** (PowerShell): + ```powershell + $env:AZDO_VERBOSELOGGING_FILEPATH + ``` + * **Linux/macOS**: + ```sh + echo $AZDO_VERBOSELOGGING_FILEPATH + ``` + +By setting the `AZDO_VERBOSELOGGING_FILEPATH` environment variable, you direct the `Write-Verbose` output to the specified file, enabling detailed logging for troubleshooting and monitoring purposes. + +Below is an example of the `Write-Verbose` logfile: + +``` Text +[2024-08-13 14:10:45] [Invoke-AzDevOpsApiRestMethod] Invoking the Azure DevOps API REST method 'Get'. +[2024-08-13 14:10:45] [Invoke-AzDevOpsApiRestMethod] API URI: https://vssps.dev.azure.com/sample/_apis/graph/Memberships/vssgp.string?direction=down +[2024-08-13 14:10:45] [Add-AuthenticationHTTPHeader] Adding Managed Identity Token to the HTTP Headers. +[2024-08-13 14:10:45] [Add-AuthenticationHTTPHeader] Adding Header +[2024-08-13 14:10:45] [Invoke-AzDevOpsApiRestMethod] No continuation token found. Breaking loop. +[2024-08-13 14:10:45] No members found for group '[TEAM FOUNDATION]\Enterprise Service Accounts'; skipping. +``` + +# Tests + ## Running the Tests If want to know how to run this module's tests you can look at the [Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines/#running-tests) diff --git a/README.md b/README.md index f6a79d298..842236cb7 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ configuration of Azure DevOps and Azure DevOps Server. This project has adopted this [Code of Conduct](CODE_OF_CONDUCT.md). +## Usage + +Please review the following [Usage Documentation](USAGE.md) + ## Releases For each merge to the branch `main` a preview release will be diff --git a/USAGE.md b/USAGE.md new file mode 100644 index 000000000..e02ceaaa8 --- /dev/null +++ b/USAGE.md @@ -0,0 +1,139 @@ +# Usage Documentation + +This document provides detailed instructions on how to use the module effectively. + +## Prerequisites + +Ensure you have the following prerequisites before proceeding: + +- **PowerShell 7.0** +- **Required Modules**: + - `ChangelogManagement` + - `Configuration` + - `DscResource.AnalyzerRules` + - `DscResource.Common` + - `DscResource.DocGenerator` + - `DscResource.Test` + - `InvokeBuild` + - `MarkdownLinkCheck` + - `Metadata` + - `ModuleBuilder` + - `Pester` + - `Plaster` + - `PSDepend` + - `PSDscResources` + - `PSScriptAnalyzer` + - `Sampler` + - `xDSCResourceDesigner` + +### Setting Up: *AZDODSC_CACHE_DIRECTORY* Environment Variable + +The system environment variable `AZDODSC_CACHE_DIRECTORY` is used by the module to store caching settings and the cache itself. +Make sure this variable is properly set up in your system environment. + +## Setting Up Managed Identity + +Please use the following [documentation](https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/service-principal-managed-identity?view=azure-devops) as a guide to create a managed identity in Azure DevOps. + +## Authentication + +Prior to accessing any resources, it is necessary to configure authentication by utilizing the `New-AzDoAuthenticationProvider` cmdlet. + +```powershell +New-AzDoAuthenticationProvider -OrganizationName $AzureDevopsOrganizationName -UseManagedIdentity +``` + +## Sample Invocation + +Here is an example of how to invoke a resource using the module: + +1. Import the necessary modules: + + ```powershell + Import-Module "\AzureDevOpsDsc\0.0.1\Modules\DscResource.Common\0.17.1\DscResource.Common.psd1" + Import-Module "\AzureDevOpsDsc\0.0.1\Modules\AzureDevOpsDsc.Common\AzureDevOpsDsc.Common.psd1" + Import-Module "\AzureDevOpsDsc\0.0.1\AzureDevOpsDsc.psd1" + ``` + +1. Create a Managed Identity Token: + + ```powershell + New-AzDoAuthenticationProvider -OrganizationName "akkodistestorg" -UseManagedIdentity + ``` + +1. Define the properties to be used by the module: + + ```powershell + $properties = @{ + ProjectName = 'UpdateSharePoint' + GroupName = 'TESTGROUP5' + } + ``` + +1. Invoke the DSC Resource: + + ```powershell + Invoke-DscResource -Name 'xAzDoProjectGroup' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' + ``` + +By following these steps, you can successfully set up and use the module with Azure DevOps. + +## Implementation using `xAzDoDSCDatum` + +[Current Source](https://github.com/ZanattaMichael/xAzDoDSCDatum) + +This module includes a custom Local Configuration Manager (LCM) built on Datum. By utilizing YAML resource files, similar to Ansible playbooks, administrators can manage their environment using Configuration as Code (CaC). + +Below is an example of how you can define parameters, variables, and resources in a YAML file to manage your Azure DevOps environment: + +**FileName: ProjectPolicies\Project.yml** +```yaml +parameters: {} + +variables: { +} + +resources: + + - name: Project + type: AzureDevOpsDsc/xAzDoProject + properties: + projectName: $ProjectName + projectDescription: $ProjectDescription + visibility: private + SourceControlType: Git + ProcessTemplate: Agile +``` + +**FileName: AllNodes\SampleProject\Project.yml** +```yaml +parameters: {} + +variables: + ProjectName: SampleProject + ProjectDescription: 'Never gonna give you up, never gonna let you down!' + +resources: + - name: Project Services + type: AzureDevOpsDsc/xAzDoProjectServices + dependsOn: + - AzureDevOpsDsc/xAzDoProject/Project + properties: + projectName: $ProjectName + BuildPipelines: disabled + AzureArtifact: disabled +``` + +### Explanation + +- **Parameters**: This section is reserved for any input parameters that the configuration might require. +- **Variables**: Here, you can define reusable variables such as `ProjectName` and `ProjectDescription`. +- **Resources**: This section defines the actual resources to be managed. In this example, we have a resource named "Project Services" of type `AzureDevOpsDsc/xAzDoProjectServices`. + +#### Resource Properties + +- `projectName`: Uses the variable `$ProjectName` defined earlier. +- `BuildPipelines`: Set to `disabled`. +- `AzureArtifact`: Set to `disabled`. + +The `dependsOn` attribute ensures that the "Project Services" resource will only be configured after the `AzureDevOpsDsc/xAzDoProject/Project` resource has been set up. diff --git a/source/AzureDevOpsDsc.psd1 b/source/AzureDevOpsDsc.psd1 index b99801be9..28c0d54d8 100644 --- a/source/AzureDevOpsDsc.psd1 +++ b/source/AzureDevOpsDsc.psd1 @@ -2,7 +2,7 @@ RootModule = 'AzureDevOpsDsc.psm1' # Version number of this module. - moduleVersion = '0.0.0' + moduleVersion = '0.0.1' # ID used to uniquely identify this module GUID = '3f8bbada-0fa9-4d80-b3d8-f019c3c60230' @@ -20,16 +20,16 @@ Description = 'Module with DSC Resources for deployment and configuration of Azure DevOps Server/Services.' # Minimum version of the Windows PowerShell engine required by this module - PowerShellVersion = '5.0' + PowerShellVersion = '7.0' # Minimum version of the common language runtime (CLR) required by this module CLRVersion = '4.0' # Functions to export from this module - FunctionsToExport = @() + #FunctionsToExport = @() # Cmdlets to export from this module - CmdletsToExport = @() + #CmdletsToExport = @() # Variables to export from this module VariablesToExport = @() @@ -40,7 +40,11 @@ # Import all the 'DSCClassResource', modules as part of this module NestedModules = @() - DscResourcesToExport = @('AzDevOpsProject') + DscResourcesToExport = @( + 'xAzDevOpsProject', + 'xAzDoOrganizationGroup', + 'xAzDoProjectGroup' + ) RequiredAssemblies = @() diff --git a/source/Classes/000.ManagedIdentityDataResources.ps1 b/source/Classes/000.ManagedIdentityDataResources.ps1 deleted file mode 100644 index 4da713ddc..000000000 --- a/source/Classes/000.ManagedIdentityDataResources.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -data AzManagedIdentityLocalizedData { - - ConvertFrom-StringData @' - Global_AzureDevOps_Resource_Id=499b84ac-1321-427f-aa17-267ca6975798 - Global_Url_Azure_Instance_Metadata_Url=http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource={0} - Global_API_Azure_DevOps_Version=api-version=6.0 - Global_Url_AZDO_Project=https://dev.azure.com/{0}/_apis/projects - Error_ManagedIdentity_RestApiCallFailed=Error. Failed to call the Azure Instance Metadata Service. Please ensure that the Azure Instance Metadata Service is available. Error Details: {0} - Error_Azure_Instance_Metadata_Service_Missing_Token=Error. Access token not returned from Azure Instance Metadata Service. Please ensure that the Azure Instance Metadata Service is available. - Error_Azure_API_Call_Generic=Error. Failed to call the Azure DevOps API. Details: {0} - Error_Azure_Get_AzManagedIdentity_Invalid_Caller=Error. Get-AzManagedIdentity can only be called from New-AzManagedIdentity or Update-AzManagedIdentity. -'@ - -} - -New-Variable -Name AzManagedIdentityLocalizedData -Value $AzManagedIdentityLocalizedData -Option ReadOnly -Scope Global -Force diff --git a/source/Classes/001.AuthenticationToken.ps1 b/source/Classes/001.AuthenticationToken.ps1 new file mode 100644 index 000000000..8a0b23d05 --- /dev/null +++ b/source/Classes/001.AuthenticationToken.ps1 @@ -0,0 +1,78 @@ + + +Class AuthenticationToken { + + [TokenType]$tokenType + hidden [bool]$linux = $isLinux + hidden [SecureString]$access_token + + # Function to convert a SecureString to a String + hidden [String]ConvertFromSecureString([SecureString]$SecureString) { + # Convert a SecureString to a String + $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString) + $String = ($this.linux) ? [System.Runtime.InteropServices.Marshal]::PtrToStringUni($BSTR) : + [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) + [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) + return $String + } + + # Function to test the call stack + hidden [Bool]TestCallStack([String]$name) { + + # Get the call stack + Write-Verbose "[AuthenticationToken] Getting the call stack." + + $CallStack = Get-PSCallStack + + # Check if any of the callers in the call stack is Invoke-DSCResource + foreach ($stackFrame in $callStack) { + if ($stackFrame.Command -eq $name) { + Write-Verbose "[AuthenticationToken] The calling function is $name." + return $true + } + } + + return $false + + } + + # Function to prevent unauthorized access to the Get() method + TestCaller() { + # + # Prevent Execution and Writing to Files and Pipeline Variables. + + # Token can only be called within Test-AzAuthenticationToken. Test to see if the calling function is Test-AzAuthenticationToken + if ( + (-not($this.TestCallStack('Add-AuthenticationHTTPHeader'))) -and + (-not($this.TestCallStack('Invoke-AzDevOpsApiRestMethod'))) -and + (-not($this.TestCallStack('New-AzDoAuthenticationProvider'))) + ) { + # Token can only be called within Invoke-AzDevOpsApiRestMethod. Test to see if the calling function is Invoke-AzDevOpsApiRestMethod + throw "[AuthenticationToken][Access Denied] The Get() method can only be called within AzureDevOpsDsc.Common." + } + + # Token cannot be returned within a Write-* function. Test to see if the calling function is Write-* + if ($this.TestCallStack('Write-')) { throw "[AuthenticationToken][Access Denied] The Get() method cannot be called within a Write-* function." } + # Token cannot be written to a file. Test to see if the calling function is Out-File + if ($this.TestCallStack('Out-File')) { throw "[AuthenticationToken][Access Denied] The Get() method cannot be called within Out-File." } + + } + + # Return the access token + [String] Get() { + + # Verbose output + Write-Verbose "[AuthenticationToken] Getting the access token:" + Write-Verbose "[AuthenticationToken] Ensuring that the calling function is allowed to call the Get() method." + + # Test the caller + $this.TestCaller() + + Write-Verbose "[AuthenticationToken] Token Retrival Successful." + + # Return the access token + return ($this.ConvertFromSecureString($this.access_token)) + + } + +} diff --git a/source/Classes/002.PersonalAccessToken.ps1 b/source/Classes/002.PersonalAccessToken.ps1 new file mode 100644 index 000000000..df42f72f9 --- /dev/null +++ b/source/Classes/002.PersonalAccessToken.ps1 @@ -0,0 +1,41 @@ + + +Class PersonalAccessToken : AuthenticationToken { + + + + PersonalAccessToken([String]$PersonalAccessToken) { + $this.tokenType = [TokenType]::PersonalAccessToken + $this.access_token = ConvertTo-Base64String -InputObject ":$($PersonalAccessToken)" | ConvertTo-SecureString -AsPlainText -Force + } + + PersonalAccessToken([SecureString]$SecureStringPersonalAccessToken) { + $this.tokenType = [TokenType]::PersonalAccessToken + $this.access_token = $SecureStringPersonalAccessToken + } + + [Bool]isExpired() { + # Personal Access Tokens do not expire. + return $false + } + + +} + +# Function to create a new PersonalAccessToken object +Function global:New-PersonalAccessToken ([String]$PersonalAccessToken, [SecureString]$SecureStringPersonalAccessToken) { + + # Verbose output + Write-Verbose "[PersonalAccessToken] Creating a new ManagedIdentityToken object." + + if ($PersonalAccessToken) { + # Create a new PersonalAccessToken object + return [PersonalAccessToken]::New($PersonalAccessToken) + } elseif ($SecureStringPersonalAccessToken) { + # Create a new PersonalAccessToken object + return [PersonalAccessToken]::New($SecureStringPersonalAccessToken) + } else { + throw "Error. A Personal Access Token or SecureString Personal Access Token must be provided." + } + +} diff --git a/source/Classes/004.ManagedIdentityToken.ps1 b/source/Classes/003.ManagedIdentityToken.ps1 similarity index 51% rename from source/Classes/004.ManagedIdentityToken.ps1 rename to source/Classes/003.ManagedIdentityToken.ps1 index c5a2c5d5c..914372771 100644 --- a/source/Classes/004.ManagedIdentityToken.ps1 +++ b/source/Classes/003.ManagedIdentityToken.ps1 @@ -1,19 +1,19 @@ -Class ManagedIdentityToken { +Class ManagedIdentityToken : AuthenticationToken { - [SecureString]$access_token [DateTime]$expires_on [Int]$expires_in [String]$resource [String]$token_type - hidden [bool]$linux = $IsLinux # Constructor ManagedIdentityToken([PSCustomObject]$ManagedIdentityTokenObj) { + $this.tokenType = [TokenType]::ManagedIdentity + # Validate that ManagedIdentityTokenObj is a HashTable and Contains the correct keys - if (-not $this.isValid($ManagedIdentityTokenObj)) { throw "The ManagedIdentityTokenObj is not valid." } + if (-not $this.isValid($ManagedIdentityTokenObj)) { throw "[ManagedIdentityToken] The ManagedIdentityTokenObj is not valid." } $epochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) @@ -29,6 +29,9 @@ Class ManagedIdentityToken { # Function to validate the ManagedIdentityTokenObj Hidden [Bool]isValid($ManagedIdentityTokenObj) { + # Write-Verbose + Write-Verbose "[ManagedIdentityToken] Validating the ManagedIdentityTokenObj." + # Assuming these are the keys we expect in the hashtable $expectedKeys = @('access_token', 'expires_on', 'expires_in', 'resource', 'token_type') @@ -45,33 +48,6 @@ Class ManagedIdentityToken { return $true } - # Function to convert a SecureString to a String - hidden [String]ConvertFromSecureString([SecureString]$SecureString) { - # Convert a SecureString to a String - $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString) - $String = ($this.linux) ? [System.Runtime.InteropServices.Marshal]::PtrToStringUni($BSTR) : - [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) - [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) - return $String - } - - # Function to test the call stack - hidden [Bool]TestCallStack([String]$name) { - - $CallStack = Get-PSCallStack - - # Check if any of the callers in the call stack is Invoke-DSCResource - foreach ($stackFrame in $callStack) { - if ($stackFrame.Command -eq $name) { - Write-Verbose "[ManagedIdentityToken] The calling function is $name." - return $true - } - } - - return $false - - } - [Bool]isExpired() { # Remove 10 seconds from the expires_on time to account for clock skew. if ($this.expires_on.AddSeconds(-10) -lt (Get-Date)) { return $true } @@ -81,14 +57,14 @@ Class ManagedIdentityToken { # Return the access token [String] Get() { - # Prevent Execution and Writing to Files and Pipeline Variables. + # Verbose output + Write-Verbose "[ManagedIdentityToken] Getting the access token:" + Write-Verbose "[ManagedIdentityToken] Ensuring that the calling function is allowed to call the Get() method." + + # Test the caller + $this.TestCaller() - # Token can only be called within Invoke-AzDevOpsApiRestMethod. Test to see if the calling function is Invoke-AzDevOpsApiRestMethod - if (-not($this.TestCallStack('Invoke-AzDevOpsApiRestMethod'))) { throw "[ManagedIdentityToken] The Get() method can only be called within Invoke-AzDevOpsApiRestMethod." } - # Token cannot be returned within a Write-* function. Test to see if the calling function is Write-* - if ($this.TestCallStack('Write-')) { throw "[ManagedIdentityToken] The Get() method cannot be called within a Write-* function." } - # Token cannot be written to a file. Test to see if the calling function is Out-File - if ($this.TestCallStack('Out-File')) { throw "[ManagedIdentityToken] The Get() method cannot be called within Out-File." } + Write-Verbose "[ManagedIdentityToken] Token Retrival Successful." # Return the access token return ($this.ConvertFromSecureString($this.access_token)) @@ -98,7 +74,10 @@ Class ManagedIdentityToken { } # Function to create a new ManagedIdentityToken object -Function global:New-ManagedIdentityToken ([hashtable]$ManagedIdentityTokenObj) { +Function global:New-ManagedIdentityToken ([PSCustomObject]$ManagedIdentityTokenObj) { + + # Verbose output + Write-Verbose "[ManagedIdentityToken] Creating a new ManagedIdentityToken object." # Create and return a new ManagedIdentityToken object return [ManagedIdentityToken]::New($ManagedIdentityTokenObj) diff --git a/source/Classes/001.DscResourceBase.ps1 b/source/Classes/004.DscResourceBase.ps1 similarity index 100% rename from source/Classes/001.DscResourceBase.ps1 rename to source/Classes/004.DscResourceBase.ps1 diff --git a/source/Classes/002.AzDevOpsApiDscResourceBase.ps1 b/source/Classes/005.AzDevOpsApiDscResourceBase.ps1 similarity index 96% rename from source/Classes/002.AzDevOpsApiDscResourceBase.ps1 rename to source/Classes/005.AzDevOpsApiDscResourceBase.ps1 index 191cd9721..273c4b69e 100644 --- a/source/Classes/002.AzDevOpsApiDscResourceBase.ps1 +++ b/source/Classes/005.AzDevOpsApiDscResourceBase.ps1 @@ -79,7 +79,8 @@ class AzDevOpsApiDscResourceBase : DscResourceBase [RequiredAction]::Remove, [RequiredAction]::Test)) { - return "$($RequiredAction)-AzDevOps$($this.ResourceName)" + $result = "$($RequiredAction)-$($this.ResourceName)" + return $result } return $null diff --git a/source/Classes/003.AzDevOpsDscResourceBase.ps1 b/source/Classes/006.AzDevOpsDscResourceBase.ps1 similarity index 54% rename from source/Classes/003.AzDevOpsDscResourceBase.ps1 rename to source/Classes/006.AzDevOpsDscResourceBase.ps1 index 81df0d005..aeaa5457d 100644 --- a/source/Classes/003.AzDevOpsDscResourceBase.ps1 +++ b/source/Classes/006.AzDevOpsDscResourceBase.ps1 @@ -4,31 +4,106 @@ #> class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase { - [DscProperty()] - [Alias('Uri')] - [System.String] - $ApiUri - - [DscProperty()] - [Alias('PersonalAccessToken')] - [Alias('AccessToken')] - [System.String] - $Pat - [DscProperty()] [Ensure] $Ensure + [DscProperty(NotConfigurable)] + [Alias('result')] + [HashTable]$LookupResult + + hidden Construct() + { + Import-Module AzureDevOpsDsc.Common -ArgumentList @($true) + + # Ensure that $ENV:AZDODSC_CACHE_DIRECTORY is set. If not, throw an error. + if (-not($ENV:AZDODSC_CACHE_DIRECTORY)) + { + Write-Verbose "[AzDevOpsDscResourceBase] The Environment Variable 'AZDODSC_CACHE_DIRECTORY' is not set." + Throw "[AzDevOpsDscResourceBase] The Environment Variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the Environment Variable 'AZDODSC_CACHE_DIRECTORY' to the Cache Directory." + } + + # Attempt to import the ModuleSettings.clixml file. If it does not exist, throw an error. + $moduleSettingsPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "ModuleSettings.clixml" + Write-Verbose "[AzDevOpsDscResourceBase] Looking for ModuleSettings.clixml at path: $moduleSettingsPath" + + # Check if the ModuleSettings.clixml file exists in the Cache Directory. If not, throw an error. + if (-not(Test-Path -Path $moduleSettingsPath)) + { + Throw "[AzDevOpsDscResourceBase] The ModuleSettings.clixml file does not exist in the Cache Directory. Please ensure that the file exists." + } + + Write-Verbose "[AzDevOpsDscResourceBase] Found ModuleSettings.clixml file." + + # Import the ModuleSettings.clixml file + $objectSettings = Import-Clixml -LiteralPath $moduleSettingsPath + Write-Verbose "[AzDevOpsDscResourceBase] Successfully imported ModuleSettings.clixml." + + # + # Import the Token information from the Cache Directory + + $organizationName = $objectSettings.OrganizationName + $tokenObject = $objectSettings.Token + $access_token = $tokenObject.access_token + + # Ensure that the access_token is not null or empty. If it is, throw an error. + if ([String]::IsNullOrEmpty($access_token)) + { + Throw "[AzDevOpsDscResourceBase] The Token information does not exist in the Cache Directory. Please ensure that the Token information exists." + } + + Write-Verbose "[AzDevOpsDscResourceBase] Access token retrieved successfully." + + # + # Determine the type of Token (PersonalAccessToken or ManagedIdentity) + + switch ($tokenObject.tokenType.ToString()) { + + # If the Token is empty + { [String]::IsNullOrEmpty($_) } { + Write-Verbose "[AzDevOpsDscResourceBase] Token type is null or empty." + Throw "[AzDevOpsDscResourceBase] The Token information does not exist in the Cache Directory. Please ensure that the Token information exists." + } + # If the Token is a Personal Access Token + { $_ -eq 'PersonalAccessToken' } { + Write-Verbose "[AzDevOpsDscResourceBase] Token type is Personal Access Token." + New-AzDoAuthenticationProvider -OrganizationName $organizationName -SecureStringPersonalAccessToken $access_token -isResource -NoVerify + } + # If the Token is a Managed Identity Token + { $_ -eq 'ManagedIdentity' } { + Write-Verbose "[AzDevOpsDscResourceBase] Token type is Managed Identity." + New-AzDoAuthenticationProvider -OrganizationName $organizationName -useManagedIdentity -isResource -NoVerify + } + # Default + default { + Write-Verbose "[AzDevOpsDscResourceBase] Unknown token type." + Throw "[AzDevOpsDscResourceBase] The Token information does not exist in the Cache Directory. Please ensure that the Token information exists." + } + + } + + # + # Initialize the cache objects. Don't delete the cache objects since they are used by other resources. + Get-AzDoCacheObjects | ForEach-Object { + Initialize-CacheObject -CacheType $_ -BypassFileCheck -Debug + Write-Verbose "[AzDevOpsDscResourceBase] Initialized cache object of type: $_" + } + + + } hidden [Hashtable]GetDscCurrentStateObjectGetParameters() { # Setup a default set of parameters to pass into the resource/object's 'Get' method $getParameters = @{ - ApiUri = $this.ApiUri - Pat = $this.Pat "$($this.GetResourceKeyPropertyName())" = $this.GetResourceKey() } + # Append all other properties to the hashtable + $this.GetDscResourcePropertyNames() | ForEach-Object { + $getParameters."$_" = $this."$_" + } + # If there is an available 'ResourceId' value, add it to the parameters/hashtable if (![System.String]::IsNullOrWhiteSpace($this.GetResourceId())) { @@ -38,7 +113,6 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase return $getParameters } - hidden [PsObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) { # Obtain the 'Get' function name for the object, then invoke it @@ -46,21 +120,24 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase return $(& $thisResourceGetFunctionName @GetParameters) } - - hidden [System.Management.Automation.PSObject]GetDscCurrentStateObject() + hidden [HashTable]GetDscCurrentStateObject() { - $getParameters = $this.GetDscCurrentStateObjectGetParameters() - $dscCurrentStateResourceObject = $this.GetDscCurrentStateResourceObject($getParameters) + # Declare the result hashtable + $props = @{} - # If no object was returned (i.e it does not exist), create a default/empty object - if ($null -eq $dscCurrentStateResourceObject) - { - return New-Object -TypeName 'System.Management.Automation.PSObject' -Property @{ - Ensure = [Ensure]::Absent - } + $getParameters = $this.GetDscCurrentStateObjectGetParameters() + + # Add all properties from the current object to the hashtable + $getParameters.Keys | ForEach-Object { + $props."$_" = $this."$_" } - return $dscCurrentStateResourceObject + + $props.LookupResult = $this.GetDscCurrentStateResourceObject($getParameters) + $props.Ensure = $props.LookupResult.Ensure + + return $props + } @@ -83,6 +160,23 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase return $null } + hidden [Object[]]FindEnumValuesForInteger([System.Type]$EnumType, [Int32]$Value) + { + [System.Collections.ArrayList]$enumValues = @() + + [System.Array]$enumValues = [System.Enum]::GetValues($EnumType) + + [System.Collections.ArrayList]$matchingEnumValues = @() + + $enumValues | ForEach-Object { + if ($Value -band $_ -eq $_) + { + $matchingEnumValues.Add($_) + } + } + + return $matchingEnumValues.ToArray() + } hidden [Hashtable]GetDscDesiredStateProperties() { @@ -99,95 +193,66 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase hidden [RequiredAction]GetDscRequiredAction() { + # Initialize required action as None + $dscRequiredAction = [RequiredAction]::None + + # Retrieve current and desired properties [Hashtable]$currentProperties = $this.GetDscCurrentStateProperties() [Hashtable]$desiredProperties = $this.GetDscDesiredStateProperties() + # Retrieve property names [System.String[]]$dscPropertyNamesWithNoSetSupport = $this.GetDscResourcePropertyNamesWithNoSetSupport() [System.String[]]$dscPropertyNamesToCompare = $this.GetDscResourcePropertyNames() - - # Update 'Id' property: - # Set $desiredProperties."$IdPropertyName" to $currentProperties."$IdPropertyName" if it's desired - # value is blank/null but it's current/existing value is known (and can be recovered from $currentProperties). - # - # This ensures that alternate keys (typically ResourceIds) not provided in the DSC configuration do not flag differences - [System.String]$IdPropertyName = $this.GetResourceIdPropertyName() - - if ([System.String]::IsNullOrWhiteSpace($desiredProperties[$IdPropertyName]) -and - ![System.String]::IsNullOrWhiteSpace($currentProperties[$IdPropertyName])) - { - $desiredProperties."$IdPropertyName" = $currentProperties."$IdPropertyName" - } - - - # Perform logic with 'Ensure' (to determine whether resource should be created or dropped (or updated, if already [Ensure]::Present but property values differ) - $dscRequiredAction = [RequiredAction]::None - switch ($desiredProperties.Ensure) { ([Ensure]::Present) { - # If not already present, or different to expected/desired - return [RequiredAction]::New (i.e. Resource needs creating) - if ($null -eq $currentProperties -or $($currentProperties.Ensure) -ne [Ensure]::Present) - { - $dscRequiredAction = [RequiredAction]::New - Write-Verbose "DscActionRequired='$dscRequiredAction'" - break - } + Write-Verbose "Desired state is Present." - # Changes made by DSC to the following properties are unsupported by the resource (other than when creating a [RequiredAction]::New resource) - if ($dscPropertyNamesWithNoSetSupport.Count -gt 0) + if ($currentProperties.Ensure -eq [Ensure]::Absent) { - $dscPropertyNamesWithNoSetSupport | ForEach-Object { + Write-Verbose "Current state is Absent." - if ($($currentProperties[$_].ToString()) -ne $($desiredProperties[$_].ToString())) - { - $errorMessage = "The '$($this.GetType().Name)', DSC Resource does not support changes for/to the '$_' property." - New-InvalidOperationException -Message $errorMessage - } - } - } - - # Compare all properties ('Current' vs 'Desired') - if ($dscPropertyNamesToCompare.Count -gt 0) - { - $dscPropertyNamesToCompare | ForEach-Object { - - if ($($currentProperties."$_") -ne $($desiredProperties."$_")) - { - Write-Verbose "DscPropertyValueMismatch='$_'" + switch ($currentProperties.LookupResult.Status) + { + ([DSCGetSummaryState]::NotFound) { + $dscRequiredAction = [RequiredAction]::New + Write-Verbose "Resource not found. Setting action to New." + }([DSCGetSummaryState]::Changed) { $dscRequiredAction = [RequiredAction]::Set + Write-Verbose "Resource Changed. Setting action to Set." + }([DSCGetSummaryState]::Renamed) { + $dscRequiredAction = [RequiredAction]::Set + Write-Verbose "Resource Renamed. Setting action to Set." + } + ([DSCGetSummaryState]::Missing) { + $dscRequiredAction = [RequiredAction]::Remove + Write-Verbose "Resource missing. Setting action to Remove." } } - if ($dscRequiredAction -eq [RequiredAction]::Set) - { - Write-Verbose "DscActionRequired='$dscRequiredAction'" - break - } + Write-Verbose "DscActionRequired='$dscRequiredAction'" + return $dscRequiredAction } - # Otherwise, no changes to make (i.e. The desired state is already achieved) + Write-Verbose "No changes required. Desired state already achieved." return $dscRequiredAction - break } + ([Ensure]::Absent) { - # If currently/already present - return $false (i.e. state is incorrect) - if ($null -ne $currentProperties -and $currentProperties.Ensure -ne [Ensure]::Absent) - { - $dscRequiredAction = [RequiredAction]::Remove - Write-Verbose "DscActionRequired='$dscRequiredAction'" - break - } + Write-Verbose "Desired state is Absent." + + $dscRequiredAction = ($currentProperties.LookupResult.Status -eq [DSCGetSummaryState]::NotFound) ? [RequiredAction]::None : [RequiredAction]::Remove - # Otherwise, no changes to make (i.e. The desired state is already achieved) Write-Verbose "DscActionRequired='$dscRequiredAction'" return $dscRequiredAction - break } + default { $errorMessage = "Could not obtain a valid 'Ensure' value within '$($this.GetResourceName())' Test() function. Value was '$($desiredProperties.Ensure)'." + Write-Verbose $errorMessage New-InvalidOperationException -Message $errorMessage } } @@ -195,13 +260,11 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase return $dscRequiredAction } - hidden [Hashtable]GetDesiredStateParameters([Hashtable]$CurrentStateProperties, [Hashtable]$DesiredStateProperties, [RequiredAction]$RequiredAction) { [Hashtable]$desiredStateParameters = $DesiredStateProperties [System.String]$IdPropertyName = $this.GetResourceIdPropertyName() - # If actions required are 'None' or 'Error', return a $null value if ($RequiredAction -in @([RequiredAction]::None, [RequiredAction]::Error)) { @@ -210,10 +273,12 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase # If the desired state/action is to remove the resource, generate/return a minimal set of parameters required to remove the resource elseif ($RequiredAction -eq [RequiredAction]::Remove) { - return @{ + + return $desiredStateParameters + + return @{ ApiUri = $DesiredStateProperties.ApiUri Pat = $DesiredStateProperties.Pat - Force = $true # Set this from the 'Current' state as we would expect this to have an existing key/ID value to use @@ -223,6 +288,7 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase # If the desired state/action is to add/new or update/set the resource, start with the values in the $DesiredStateProperties variable, and amend elseif ($RequiredAction -in @([RequiredAction]::New, [RequiredAction]::Set)) { + # Set $desiredParameters."$IdPropertyName" to $CurrentStateProperties."$IdPropertyName" if it's known and can be recovered from existing resource if ([System.String]::IsNullOrWhiteSpace($desiredStateParameters."$IdPropertyName") -and ![System.String]::IsNullOrWhiteSpace($CurrentStateProperties."$IdPropertyName")) @@ -293,6 +359,7 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase [void] SetToDesiredState() { [RequiredAction]$dscRequiredAction = $this.GetDscRequiredAction() + $cacheProperties = $false if ($dscRequiredAction -in @([RequiredAction]::'New', [RequiredAction]::'Set', [RequiredAction]::'Remove')) { @@ -302,6 +369,12 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase $dscRequiredActionFunctionName = $this.GetResourceFunctionName($dscRequiredAction) $dscDesiredStateParameters = $this.GetDesiredStateParameters($dscCurrentStateProperties, $dscDesiredStateProperties, $dscRequiredAction) + # Set the lookup properties on the desired state object. Since it will be used to set the resource. + if ($null -ne $dscCurrentStateProperties.LookupResult) + { + $dscDesiredStateParameters.LookupResult = $dscCurrentStateProperties.LookupResult + } + & $dscRequiredActionFunctionName @dscDesiredStateParameters | Out-Null Start-Sleep -Milliseconds $($this.GetPostSetWaitTimeMs()) } diff --git a/source/Classes/005.APIRateLimit.ps1 b/source/Classes/007.APIRateLimit.ps1 similarity index 93% rename from source/Classes/005.APIRateLimit.ps1 rename to source/Classes/007.APIRateLimit.ps1 index 5887f9be0..ddbd06a37 100644 --- a/source/Classes/005.APIRateLimit.ps1 +++ b/source/Classes/007.APIRateLimit.ps1 @@ -36,7 +36,7 @@ Class APIRateLimit { # Check if all expected keys exist in the hashtable foreach ($key in $expectedKeys) { if (-not $APIRateLimitObj.ContainsKey($key)) { - Write-Error "[APIRateLimit] The hashtable does not contain the expected key: $key" + Write-Warning "[APIRateLimit] The hashtable does not contain the expected key: $key" return $false } } diff --git a/source/Classes/009.xAzDoGroupPermission.ps1 b/source/Classes/009.xAzDoGroupPermission.ps1 new file mode 100644 index 000000000..3a1a48b50 --- /dev/null +++ b/source/Classes/009.xAzDoGroupPermission.ps1 @@ -0,0 +1,105 @@ +<# +.SYNOPSIS + This class represents a DSC resource for managing Azure DevOps project group permissions. + +.DESCRIPTION + The xAzDoGroupPermission class is a DSC resource that allows you to manage permissions for a group in an Azure DevOps project. + +.NOTES + Author: Your Name + Date: Current Date + +.LINK + GitHub Repository: + +.PARAMETER GroupName + The name of the group for which the permissions are being managed. + +.PARAMETER ProjectName + The name of the Azure DevOps project. + +.PARAMETER isInherited + Specifies whether the permissions are inherited from a parent group. Default value is $true. + +.PARAMETER Permissions + Specifies the permissions to be assigned to the group. This should be an array of hashtables, where each hashtable represents a permission. + +.EXAMPLE + This example shows how to use the xAzDoGroupPermission resource to manage permissions for a group in an Azure DevOps project. + + Configuration Example { + Import-DscResource -ModuleName AzDevOpsDsc + + Node localhost { + xAzDoGroupPermission GroupPermission { + GroupName = 'MyGroup' + ProjectName = 'MyProject' + Permissions = @( + @{ + Permission = 'Read' + Allow = $true + }, + @{ + Permission = 'Write' + Allow = $false + } + ) + } + } + } + +#> + +# RESOURCE IS CURRENTLY DISABLED +#[DscResource()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] +class xAzDoGroupPermission : AzDevOpsDscResourceBase +{ + [DscProperty(Key, Mandatory)] + [Alias('Name')] + [System.String]$GroupName + + [DscProperty()] + [Alias('Inherited')] + [System.Boolean]$isInherited=$true + + [DscProperty()] + [HashTable[]]$Permissions + + xAzDoGroupPermission() + { + $this.Construct() + } + + [xAzDoGroupPermission] Get() + { + return [xAzDoGroupPermission]$($this.GetDscCurrentStateProperties()) + } + + hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() + { + return @() + } + + + hidden [Hashtable]GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) + { + $properties = @{ + Ensure = [Ensure]::Absent + } + + # If the resource object is null, return the properties + if ($null -eq $CurrentResourceObject) { return $properties } + + $properties.GroupName = $CurrentResourceObject.GroupName + $properties.isInherited = $CurrentResourceObject.isInherited + $properties.Permissions = $CurrentResourceObject.Permissions + $properties.lookupResult = $CurrentResourceObject.lookupResult + $properties.Ensure = $CurrentResourceObject.Ensure + + Write-Verbose "[xAzDoGroupPermission] Current state properties: $($properties | Out-String)" + + return $properties + } + +} diff --git a/source/Classes/010.AzDevOpsProject.ps1 b/source/Classes/010.AzDevOpsProject.ps1 deleted file mode 100644 index 4dea38bbc..000000000 --- a/source/Classes/010.AzDevOpsProject.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -<# - .SYNOPSIS - A DSC Resource for Azure DevOps that represents the 'Project' resource. - - .DESCRIPTION - A DSC Resource for Azure DevOps that represents the 'Project' resource. - - .PARAMETER ProjectId - The 'Id' of the Azure DevOps, 'Project' resource. - - .PARAMETER ProjectName - The 'Name' of the Azure DevOps, 'Project' resource. - - .PARAMETER ProjectDescription - The 'Description' of the Azure DevOps, 'Project' resource. - - .PARAMETER SourceControlType - The 'SourceControlType' of the Azure DevOps, 'Project' resource. - - If the 'Project' resource already exists in Azure DevOps, the parameter - `SourceControlType` cannot be used to change to another type. -#> -[DscResource()] -[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] -class AzDevOpsProject : AzDevOpsDscResourceBase -{ - [DscProperty()] - [Alias('Id')] - [System.String]$ProjectId - - [DscProperty(Key, Mandatory)] - [Alias('Name')] - [System.String]$ProjectName - - [DscProperty()] - [Alias('Description')] - [System.String]$ProjectDescription - - [DscProperty()] - [ValidateSet('Git', 'Tfvc')] - [System.String]$SourceControlType - - - [AzDevOpsProject] Get() - { - return [AzDevOpsProject]$($this.GetDscCurrentStateProperties()) - } - - - hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() - { - return @('SourceControlType') - } - - hidden [Hashtable]GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) - { - $properties = @{ - Pat = $this.Pat - ApiUri = $this.ApiUri - Ensure = [Ensure]::Absent - } - - if ($null -ne $CurrentResourceObject) - { - if (![System.String]::IsNullOrWhiteSpace($CurrentResourceObject.id)) - { - $properties.Ensure = [Ensure]::Present - } - $properties.ProjectId = $CurrentResourceObject.id - $properties.ProjectName = $CurrentResourceObject.name - $properties.ProjectDescription = $CurrentResourceObject.description - $properties.SourceControlType = $CurrentResourceObject.capabilities.versioncontrol.sourceControlType - } - - return $properties - } - -} diff --git a/source/Classes/011.xAzDoOrganizationGroup.ps1 b/source/Classes/011.xAzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..2cb018d90 --- /dev/null +++ b/source/Classes/011.xAzDoOrganizationGroup.ps1 @@ -0,0 +1,99 @@ +<# +.SYNOPSIS + This class represents an Azure DevOps organization group. + +.DESCRIPTION + The xAzDoOrganizationGroup class is a DSC resource that allows you to manage Azure DevOps organization groups. + It provides properties to specify the group name, display name, and description. + +.NOTES + Author: Michael Zanatta + Date: 04/19/2024 + +.LINK + GitHub Repository: + +.PARAMETER GroupName + The name of the organization group. + This property is mandatory and serves as the key property for the resource. + +.PARAMETER GroupDescription + The description of the organization group. + +.INPUTS + None. + +.OUTPUTS + None. + +.EXAMPLE + This example shows how to create an instance of the xAzDoOrganizationGroup class: + + $organizationGroup = [xAzDoOrganizationGroup]::new() + $organizationGroup.GroupName = "MyGroup" + $organizationGroup.GroupDescription = "This is my group." + + $organizationGroup.Get() + +#> + +[DscResource()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] +class xAzDoOrganizationGroup : AzDevOpsDscResourceBase +{ + [DscProperty(Key, Mandatory)] + [Alias('Name')] + [System.String]$GroupName + + [DscProperty()] + [Alias('Description')] + [System.String]$GroupDescription + + xAzDoOrganizationGroup() + { + $this.Construct() + } + + [xAzDoOrganizationGroup] Get() + { + return [xAzDoOrganizationGroup]$($this.GetDscCurrentStateProperties()) + } + + hidden [HashTable] getDscCurrentAPIState() + { + # Get the current state of the resource + $params = @{ + GroupName = $this.GroupName + GroupDescription = $this.GroupDescription + } + + return Get-xAzDoOrganizationGroup @params + + } + + hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() + { + return @() + } + + hidden [Hashtable]GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) + { + $properties = @{ + Ensure = [Ensure]::Absent + } + + # If the resource object is null, return the properties + if ($null -eq $CurrentResourceObject) { return $properties } + + $properties.GroupName = $CurrentResourceObject.GroupName + $properties.GroupDescription = $CurrentResourceObject.GroupDescription + $properties.Ensure = $CurrentResourceObject.Ensure + $properties.LookupResult = $CurrentResourceObject.LookupResult + #$properties.Reasons = $CurrentResourceObject.LookupResult.Reasons + + Write-Verbose "[xAzDoOrganizationGroup] Current state properties: $($properties | Out-String)" + + return $properties + } + +} diff --git a/source/Classes/020.xAzDoProject.ps1 b/source/Classes/020.xAzDoProject.ps1 new file mode 100644 index 000000000..7a7fd147a --- /dev/null +++ b/source/Classes/020.xAzDoProject.ps1 @@ -0,0 +1,110 @@ +<# +.SYNOPSIS + This class represents an Azure DevOps project. + +.DESCRIPTION + The xAzDoProject class is used to define and manage Azure DevOps projects. It inherits from the AzDevOpsDscResourceBase class. + +.NOTES + Author: Your Name + Date: Current Date + +.LINK + GitHub Repository: + +.PARAMETER ProjectName + The name of the Azure DevOps project. + +.PARAMETER ProjectDescription + The description of the Azure DevOps project. + +.PARAMETER SourceControlType + The type of source control for the project. Valid values are 'Git' and 'Tfvc'. + +.PARAMETER ProcessTemplate + The process template for the project. Valid values are 'Agile', 'Scrum', 'CMMI', and 'Basic'. + +.PARAMETER Visibility + The visibility of the project. Valid values are 'Public' and 'Private'. + +.INPUTS + None + +.OUTPUTS + None + +.EXAMPLE + $project = [xAzDoProject]::Get() + $project.ProjectName = 'MyProject' + $project.ProjectDescription = 'This is a sample project' + $project.SourceControlType = 'Git' + $project.ProcessTemplate = 'Agile' + $project.Visibility = 'Private' + $project | Set-AzDevOpsProject + +#> + +[DscResource()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] +class xAzDoProject : AzDevOpsDscResourceBase +{ + [DscProperty(Key, Mandatory)] + [Alias('Name')] + [System.String]$ProjectName + + [DscProperty()] + [Alias('Description')] + [System.String]$ProjectDescription + + [DscProperty()] + [ValidateSet('Git', 'Tfvc')] + [System.String]$SourceControlType = 'Git' + + [DscProperty()] + [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] + [System.String]$ProcessTemplate = 'Agile' + + [DscProperty()] + [ValidateSet('Public', 'Private')] + [System.String]$Visibility = 'Private' + + xAzDoProject() + { + $this.Construct() + } + + [xAzDoProject] Get() + { + return [xAzDoProject]$($this.GetDscCurrentStateProperties()) + } + + + hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() + { + return @('SourceControlType','ProcessTemplate') + } + + hidden [Hashtable]GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) + { + $properties = @{ + Ensure = [Ensure]::Absent + } + + # If the resource object is null, return the properties + if ($null -eq $CurrentResourceObject) { return $properties } + + $properties.ProjectName = $CurrentResourceObject.ProjectName + $properties.ProjectDescription = $CurrentResourceObject.ProjectDescription + $properties.SourceControlType = $CurrentResourceObject.SourceControlType + $properties.ProcessTemplate = $CurrentResourceObject.ProcessTemplate + $properties.Visibility = $CurrentResourceObject.Visibility + $properties.LookupResult = $CurrentResourceObject.LookupResult + $properties.Ensure = $CurrentResourceObject.Ensure + + Write-Verbose "[xAzDoGroupPermission] Current state properties: $($properties | Out-String)" + + return $properties + + } + +} diff --git a/source/Classes/021.xAzDoProjectServices.ps1 b/source/Classes/021.xAzDoProjectServices.ps1 new file mode 100644 index 000000000..7b6e99e1b --- /dev/null +++ b/source/Classes/021.xAzDoProjectServices.ps1 @@ -0,0 +1,122 @@ +<# +.SYNOPSIS + This class represents an Azure DevOps project and its associated services. + +.DESCRIPTION + The xAzDoProjectServices class is a DSC resource that allows you to manage the services associated with an Azure DevOps project. It provides properties to enable or disable various services such as Git repositories, work boards, build pipelines, test plans, and Azure artifacts. + +.PARAMETER ProjectName + The name of the Azure DevOps project. + +.PARAMETER GitRepositories + Specifies whether Git repositories are enabled or disabled for the project. Valid values are 'Enabled' and 'Disabled'. The default value is 'Enabled'. + +.PARAMETER WorkBoards + Specifies whether work boards are enabled or disabled for the project. Valid values are 'Enabled' and 'Disabled'. The default value is 'Enabled'. + +.PARAMETER BuildPipelines + Specifies whether build pipelines are enabled or disabled for the project. Valid values are 'Enabled' and 'Disabled'. The default value is 'Enabled'. + +.PARAMETER TestPlans + Specifies whether test plans are enabled or disabled for the project. Valid values are 'Enabled' and 'Disabled'. The default value is 'Enabled'. + +.PARAMETER AzureArtifact + Specifies whether Azure artifacts are enabled or disabled for the project. Valid values are 'Enabled' and 'Disabled'. The default value is 'Enabled'. + +.EXAMPLE + This example shows how to use the xAzDoProjectServices resource to manage the services of an Azure DevOps project. + + Configuration Example { + Import-DscResource -ModuleName xAzDevOpsDSC + + Node localhost { + xAzDoProjectServices ProjectServices { + ProjectName = 'MyProject' + GitRepositories = 'Enabled' + WorkBoards = 'Disabled' + BuildPipelines = 'Enabled' + TestPlans = 'Disabled' + AzureArtifact = 'Enabled' + Ensure = 'Present' + } + } + } + +.NOTES + Version: 1.0 + Author: Your Name + Required Modules: xAzDevOpsDSC +#> + +[DscResource()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] +class xAzDoProjectServices : AzDevOpsDscResourceBase +{ + [DscProperty(Mandatory, Key)] + [Alias('Name')] + [System.String]$ProjectName + + [DscProperty()] + [Alias('Repos')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$GitRepositories = 'Enabled' + + [DscProperty()] + [Alias('Board')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$WorkBoards = 'Enabled' + + [DscProperty()] + [Alias('Pipelines')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$BuildPipelines = 'Enabled' + + [DscProperty()] + [Alias('Tests')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$TestPlans = 'Enabled' + + [DscProperty()] + [Alias('Artifacts')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$AzureArtifact = 'Enabled' + + xAzDoProjectServices() + { + $this.Construct() + } + + [xAzDoProjectServices] Get() + { + return [xAzDoProjectServices]$($this.GetDscCurrentStateProperties()) + } + + hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() + { + return @() + } + + hidden [Hashtable]GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) + { + $properties = @{ + Ensure = [Ensure]::Absent + } + + # If the resource object is null, return the properties + if ($null -eq $CurrentResourceObject) { return $properties } + + $properties.ProjectName = $CurrentResourceObject.ProjectName + $properties.GitRepositories = $CurrentResourceObject.GitRepositories + $properties.WorkBoards = $CurrentResourceObject.WorkBoards + $properties.BuildPipelines = $CurrentResourceObject.BuildPipelines + $properties.TestPlans = $CurrentResourceObject.TestPlans + $properties.AzureArtifact = $CurrentResourceObject.AzureArtifact + $properties.Ensure = $CurrentResourceObject.Ensure + $properties.LookupResult = $CurrentResourceObject.LookupResult + + Write-Verbose "[xAzDoProjectGroup] Current state properties: $($properties | Out-String)" + + return $properties + } + +} diff --git a/source/Classes/022.xAzDoProjectGroup.ps1 b/source/Classes/022.xAzDoProjectGroup.ps1 new file mode 100644 index 000000000..22e90c89e --- /dev/null +++ b/source/Classes/022.xAzDoProjectGroup.ps1 @@ -0,0 +1,109 @@ +<# +.SYNOPSIS + This class represents an Azure DevOps project group. + +.DESCRIPTION + The xAzDoProjectGroup class is a DSC resource that allows you to manage Azure DevOps project groups. + +.NOTES + Author: Michael Zanatta + Date: 04/19/2024 + +.LINK + GitHub Repository: + +.PARAMETER ProjectName + The name of the Azure DevOps project associated with the project group. + +.PARAMETER GroupName + The name of the project group. + +.PARAMETER GroupDescription + The description of the project group. + +.EXAMPLE + This example shows how to create a new project group: + + $projectGroup = [xAzDoProjectGroup]::new() + $projectGroup.ProjectName = "MyProject" + $projectGroup.GroupName = "MyGroup" + $projectGroup.GroupDescription = "This is my project group." + $projectGroup.Ensure = "Present" + $projectGroup.Pat = "**********" + $projectGroup.ApiUri = "https://dev.azure.com/MyOrganization" + + $projectGroup | Set-DscResource + +.INPUTS + None + +.OUTPUTS + None +#> + +[DscResource()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] +class xAzDoProjectGroup : AzDevOpsDscResourceBase +{ + [DscProperty(Key, Mandatory)] + [Alias('Name')] + [System.String]$GroupName + + [DscProperty(Mandatory)] + [Alias('Project')] + [System.String]$ProjectName + + [DscProperty()] + [Alias('Description')] + [System.String]$GroupDescription + + xAzDoProjectGroup() + { + $this.Construct() + } + + [xAzDoProjectGroup] Get() + { + return [xAzDoProjectGroup]$($this.GetDscCurrentStateProperties()) + } + + hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() + { + return @() + } + + hidden [HashTable] getDscCurrentAPIState() + { + # Get the current state of the resource + $params = @{ + GroupName = $this.GroupName + GroupDescription = $this.GroupDescription + ProjectName = $this.ProjectName + } + + return Get-xAzDoProjectGroup @params + + } + + hidden [Hashtable]GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) + { + $properties = @{ + Ensure = [Ensure]::Absent + } + + # If the resource object is null, return the properties + if ($null -eq $CurrentResourceObject) { return $properties } + + $properties.GroupName = $CurrentResourceObject.GroupName + $properties.GroupDescription = $CurrentResourceObject.GroupDescription + $properties.ProjectName = $CurrentResourceObject.ProjectName + $properties.Ensure = $CurrentResourceObject.Ensure + $properties.LookupResult = $CurrentResourceObject.LookupResult + #$properties.Reasons = $CurrentResourceObject.LookupResult.Reasons + + Write-Verbose "[xAzDoProjectGroup] Current state properties: $($properties | Out-String)" + + return $properties + } + +} diff --git a/source/Classes/031.xAzDoGroupMember.ps1 b/source/Classes/031.xAzDoGroupMember.ps1 new file mode 100644 index 000000000..00c918e08 --- /dev/null +++ b/source/Classes/031.xAzDoGroupMember.ps1 @@ -0,0 +1,91 @@ +<# +.SYNOPSIS + This class represents a DSC resource for managing Azure DevOps group members. + +.DESCRIPTION + The xAzDoGroupMember class is a DSC resource that allows you to manage the members of an Azure DevOps group. + It inherits from the AzDevOpsDscResourceBase class. + +.NOTES + Author: Your Name + Date: Current Date + +.LINK + GitHub Repository: + +.PARAMETER GroupName + The name of the Azure DevOps group. + +.PARAMETER GroupMembers + An array of strings representing the members of the Azure DevOps group. + +.EXAMPLE + This example shows how to use the xAzDoGroupMember resource to add members to an Azure DevOps group. + + Configuration Example { + Import-DscResource -ModuleName xAzDoGroupMember + + Node localhost { + xAzDoGroupMember GroupMember { + GroupName = 'MyGroup' + GroupMembers = @('User1', 'User2', 'User3') + Ensure = 'Present' + } + } + } + +.INPUTS + None + +.OUTPUTS + None + +#> + +[DscResource()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] +class xAzDoGroupMember : AzDevOpsDscResourceBase +{ + [DscProperty(Key, Mandatory)] + [Alias('Name')] + [System.String]$GroupName + + [DscProperty(Mandatory)] + [Alias('Members')] + [System.String[]]$GroupMembers + + xAzDoGroupMember() + { + $this.Construct() + } + + [xAzDoGroupMember] Get() + { + return [xAzDoGroupMember]$($this.GetDscCurrentStateProperties()) + } + + hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() + { + return @() + } + + hidden [Hashtable]GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) + { + $properties = @{ + Ensure = [Ensure]::Absent + } + + # If the resource object is null, return the properties + if ($null -eq $CurrentResourceObject) { return $properties } + + $properties.GroupName = $CurrentResourceObject.GroupName + $properties.GroupMembers = $CurrentResourceObject.GroupMembers + $properties.Ensure = $CurrentResourceObject.Ensure + $properties.LookupResult = $CurrentResourceObject.LookupResult + + Write-Verbose "[xAzDoProjectGroup] Current state properties: $($properties | Out-String)" + + return $properties + } + +} diff --git a/source/Classes/040.xAzDoGitRepository.ps1 b/source/Classes/040.xAzDoGitRepository.ps1 new file mode 100644 index 000000000..25fe4003f --- /dev/null +++ b/source/Classes/040.xAzDoGitRepository.ps1 @@ -0,0 +1,97 @@ +<# +.SYNOPSIS + This class represents an Azure DevOps Git repository. + +.DESCRIPTION + The xAzDoGitRepository class is a DSC resource that allows you to manage Azure DevOps Git repositories. + It inherits from the AzDevOpsDscResourceBase class. + +.NOTES + Author: Your Name + Date: Current Date + +.LINK + GitHub Repository: + +.PARAMETER ProjectName + The name of the Azure DevOps project where the Git repository is located. + +.PARAMETER GitRepositoryName + The name of the Git repository. + +.PARAMETER SourceRepository + The source repository URL. + +.EXAMPLE + This example shows how to use the xAzDoGitRepository resource to ensure that a Git repository exists in an Azure DevOps project. + + Configuration Example { + Import-DscResource -ModuleName xAzDoGitRepository + + xAzDoGitRepository MyGitRepository { + ProjectName = 'MyProject' + GitRepositoryName = 'MyRepository' + SourceRepository = 'https://github.com/MyUser/MyRepository.git' + Ensure = 'Present' + } + } + +.INPUTS + None + +.OUTPUTS + None +#> + +[DscResource()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] +class xAzDoGitRepository : AzDevOpsDscResourceBase +{ + [DscProperty(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName + + [DscProperty(Key, Mandatory)] + [Alias('Repository')] + [System.String]$RepositoryName + + [DscProperty()] + [Alias('Source')] + [System.String]$SourceRepository + + xAzDoGitRepository() + { + $this.Construct() + } + + [xAzDoGitRepository] Get() + { + return [xAzDoGitRepository]$($this.GetDscCurrentStateProperties()) + } + + hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() + { + return @('ProjectName', 'RepositoryName', 'SourceRepository') + } + + hidden [Hashtable]GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) + { + $properties = @{ + Ensure = [Ensure]::Absent + } + + # If the resource object is null, return the properties + if ($null -eq $CurrentResourceObject) { return $properties } + + $properties.ProjectName = $CurrentResourceObject.ProjectName + $properties.RepositoryName = $CurrentResourceObject.RepositoryName + $properties.SourceRepository = $CurrentResourceObject.SourceRepository + $properties.Ensure = $CurrentResourceObject.Ensure + $properties.LookupResult = $CurrentResourceObject.LookupResult + + Write-Verbose "[xAzDoProjectGroup] Current state properties: $($properties | Out-String)" + + return $properties + } + +} diff --git a/source/Classes/041.xAzDoGitPermission.ps1 b/source/Classes/041.xAzDoGitPermission.ps1 new file mode 100644 index 000000000..5ef142792 --- /dev/null +++ b/source/Classes/041.xAzDoGitPermission.ps1 @@ -0,0 +1,99 @@ +<# +.SYNOPSIS + This class represents an Azure DevOps DSC resource for managing Git permissions. + +.DESCRIPTION + The xAzDoGitPermission class is a DSC resource that allows you to manage Git permissions in Azure DevOps. It inherits from the AzDevOpsDscResourceBase class and provides properties and methods for managing Git permissions. + +.PARAMETER ProjectName + Specifies the name of the Azure DevOps project. + +.PARAMETER RepositoryName + Specifies the name of the Git repository. + +.PARAMETER isInherited + Specifies whether the permissions are inherited from the parent repository. Default value is $true. + +.PARAMETER PermissionsList + Specifies the list of permissions to be set for the repository. + +.NOTES + This class is part of the AzureDevOpsDSC module. + +.LINK + https://github.com/Azure/AzureDevOpsDSC + +.EXAMPLE + This example shows how to use the xAzDoGitPermission class to manage Git permissions in Azure DevOps. + + Configuration Example { + Import-DscResource -ModuleName AzureDevOpsDSC + + Node localhost { + xAzDoGitPermission GitPermission { + ProjectName = 'MyProject' + RepositoryName = 'MyRepository' + PermissionsList = @('Read', 'Contribute') + Ensure = 'Present' + } + } + } + +#> + +[DscResource()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] +class xAzDoGitPermission : AzDevOpsDscResourceBase +{ + [DscProperty(Key, Mandatory)] + [Alias('Name')] + [System.String]$ProjectName + + [DscProperty(Mandatory)] + [Alias('Repository')] + [System.String]$RepositoryName + + [DscProperty()] + [Alias('Inherited')] + [System.Boolean]$isInherited=$true + + [DscProperty()] + [HashTable[]]$Permissions + + xAzDoGitPermission() + { + $this.Construct() + } + + [xAzDoGitPermission] Get() + { + return [xAzDoGitPermission]$($this.GetDscCurrentStateProperties()) + } + + hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() + { + return @() + } + + hidden [Hashtable]GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) + { + $properties = @{ + Ensure = [Ensure]::Absent + } + + # If the resource object is null, return the properties + if ($null -eq $CurrentResourceObject) { return $properties } + + $properties.ProjectName = $CurrentResourceObject.ProjectName + $properties.RepositoryName = $CurrentResourceObject.RepositoryName + $properties.isInherited = $CurrentResourceObject.isInherited + $properties.Permissions = $CurrentResourceObject.Permissions + $properties.lookupResult = $CurrentResourceObject.lookupResult + $properties.Ensure = $CurrentResourceObject.Ensure + + Write-Verbose "[xAzDoGitPermission] Current state properties: $($properties | Out-String)" + + return $properties + } + +} diff --git a/source/Enum/DSCGetSummaryState.ps1 b/source/Enum/DSCGetSummaryState.ps1 new file mode 100644 index 000000000..8010f7ccc --- /dev/null +++ b/source/Enum/DSCGetSummaryState.ps1 @@ -0,0 +1,13 @@ +enum DSCGetSummaryState +{ + # Changed + Changed = 0 + # Unchanged + Unchanged = 1 + # Not Found + NotFound = 2 + # Renamed + Renamed = 3 + # Missing + Missing = 4 +} diff --git a/source/Enum/DescriptorType.ps1 b/source/Enum/DescriptorType.ps1 new file mode 100644 index 000000000..df9d33a7b --- /dev/null +++ b/source/Enum/DescriptorType.ps1 @@ -0,0 +1,64 @@ +Enum DescriptorType { + + Analytics + AnalyticsViews + ReleaseManagement + AuditLog + Identity + WorkItemTrackingAdministration + DistributedTask + GitRepositories + VersionControlItems2 + EventSubscriber + WorkItemTrackingProvision + ServiceEndpoints + ServiceHooks + Collection + Proxy + Plan + Process + AccountAdminSecurity + Library + Environment + Project + EventSubscription + ProjectAnalysisLanguageMetrics + Tagging + MetaTask + Iteration + WorkItemQueryFolders + Favorites + Registry + Graph + ViewActivityPaneSecurity + Job + EventPublish + WorkItemTracking + StrongBox + Server + TestManagement + SettingEntries + BuildAdministration + Location + Boards + OrganizationLevelData + UtilizationPermissions + WorkItemsHub + WebPlatform + VersionControlPrivileges + Workspaces + CrossProjectWidgetView + WorkItemTrackingConfiguration + DiscussionThreads + BoardsExternalIntegration + DataProvider + Social + Security + IdentityPicker + ServicingOrchestration + Build + DashboardsPrivileges + CSS + VersionControlItems + +} diff --git a/source/Enum/TokenType.ps1 b/source/Enum/TokenType.ps1 new file mode 100644 index 000000000..45d522ef5 --- /dev/null +++ b/source/Enum/TokenType.ps1 @@ -0,0 +1,5 @@ +Enum TokenType { + ManagedIdentity + PersonalAccessToken + Certificate +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 new file mode 100644 index 000000000..b908de738 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 @@ -0,0 +1,20 @@ +class CacheItem +{ + [string] $Key + [object] $Value + [datetime] $created + + CacheItem([string] $Key, [object] $Value) + { + # The Key Can't be empty + if (-not $Key) + { + throw "Key cannot be null or empty." + } + + $this.Key = $Key + $this.Value = $Value + $this.created = Get-Date + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.Get-AzDevOpsApiResourceUri.Tests.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACE/Empty.txt similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.Get-AzDevOpsApiResourceUri.Tests.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACE/Empty.txt diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.ps1 new file mode 100644 index 000000000..cc6bf9429 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.ps1 @@ -0,0 +1,37 @@ +function Get-DevOpsACL +{ + param ( + [Parameter(Mandatory)] + [string]$OrganizationName, + + [Parameter(Mandatory)] + [String]$SecurityDescriptorId, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + # + # Construct the URL for the API call + $params = @{ + Uri = "https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}" -f $OrganizationName, $SecurityDescriptorId, $ApiVersion + Method = 'Get' + } + + # Invoke the REST API call + $ACLList = Invoke-AzDevOpsApiRestMethod @params + + if (($null -eq $ACLList.value) -or ($ACLList.count -eq 0)) + { + return $null + } + + # + # Cache the ACL List. Use the SecurityDescriptorId as the key + Add-CacheItem -Key $SecurityDescriptorId -Value $ACLList.value -Type 'LiveACLList' + Export-CacheObject -CacheType 'LiveACLList' -Content $Global:AzDoLiveACLList + + return $ACLList.value + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.ps1 new file mode 100644 index 000000000..51bfb43ab --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.ps1 @@ -0,0 +1,66 @@ +<# +.SYNOPSIS +Retrieves the identity associated with a given subject descriptor in Azure DevOps. + +.DESCRIPTION +The Get-DevOpsDescriptorIdentity function retrieves the identity associated with a given subject descriptor in Azure DevOps. It makes a REST API call to the Azure DevOps API to fetch the identity information. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization. + +.PARAMETER SubjectDescriptor +The subject descriptor of the identity to retrieve. + +.PARAMETER ApiVersion +The version of the Azure DevOps API to use. If not specified, the default API version will be used. + +.EXAMPLE +Get-DevOpsDescriptorIdentity -OrganizationName "MyOrg" -SubjectDescriptor "subject:abcd1234" + +This example retrieves the identity associated with the subject descriptor "subject:abcd1234" in the Azure DevOps organization "MyOrg". + +#> +Function Get-DevOpsDescriptorIdentity { + + [CmdletBinding(DefaultParameterSetName = 'Default')] + param( + [Parameter(Mandatory, ParameterSetName = 'Default')] + [Parameter(Mandatory, ParameterSetName = 'Descriptors')] + [string]$OrganizationName, + + [Parameter(Mandatory, ParameterSetName = 'Default')] + [String]$SubjectDescriptor, + + [Parameter(Mandatory, ParameterSetName = 'Descriptors')] + [String]$Descriptor, + + [Parameter(ParameterSetName = 'Default')] + [Parameter(ParameterSetName = 'Descriptors')] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + # Determine the query parameter based on the parameter set + if ($SubjectDescriptor) { + $query = "subjectDescriptors=$SubjectDescriptor" + } else { + $query = "descriptors=$Descriptor" + } + + # + # Construct the URL for the API call + $params = @{ + Uri = "https://vssps.dev.azure.com/{0}/_apis/identities?{1}&api-version={2}" -f $OrganizationName, $query, $ApiVersion + Method = 'Get' + } + + # Invoke the REST API call + $identity = Invoke-AzDevOpsApiRestMethod @params + + if (($null -eq $identity.value) -or ($identity.count -gt 1)) { + return $null + } + + return $identity.value + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.New-AzDevOpsApiResource.Tests.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ApiResource/Empty.txt similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.New-AzDevOpsApiResource.Tests.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ApiResource/Empty.txt diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.ps1 new file mode 100644 index 000000000..9c4c92b51 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.ps1 @@ -0,0 +1,50 @@ +Function Remove-xAzDoPermission +{ + param( + [Parameter(Mandatory)] + [string]$OrganizationName, + + [Parameter(Mandatory)] + [string]$SecurityNamespaceID, + + [Parameter(Mandatory)] + [string]$TokenName, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + Write-Verbose "[Remove-xAzDoPermission] Started." + + # Define a hashtable to store parameters for the Invoke-AzDevOpsApiRestMethod function. + $params = @{ + # Construct the Uri using string formatting with the -f operator. + # It includes the API endpoint, group identity, member identity, and the API version. + Uri = "https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?tokens={2}&recurse=False&api-version={3}" -f $OrganizationName, + $SecurityNamespaceID, + $TokenName, + $ApiVersion + # Set the method to DELETE. + Method = 'DELETE' + } + + + try { + # Call the Invoke-AzDevOpsApiRestMethod function with the parameters defined above. + # The "@" symbol is used to pass the hashtable as splatting parameters. + Write-Verbose "[Remove-xAzDoPermission] Attempting to invoke REST method to remove ACLs." + $member = Invoke-AzDevOpsApiRestMethod @params + if ($member -ne $true) { + Write-Error "[Remove-xAzDoPermission] Failed to remove ACLs." + } else { + Write-Verbose "[Remove-xAzDoPermission] ACLs removed successfully." + } + + } catch { + # If an exception occurs, write an error message to the console with details about the issue. + Write-Error "[Remove-xAzDoPermission] Failed to add member to group: $($_.Exception.Message)" + } + + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.ps1 new file mode 100644 index 000000000..728ca916a --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.ps1 @@ -0,0 +1,49 @@ + + +Function Set-xAzDoPermission +{ + param( + [Parameter(Mandatory)] + [string]$OrganizationName, + + [Parameter(Mandatory)] + [string]$SecurityNamespaceID, + + [Parameter(Mandatory)] + [Object]$SerializedACLs, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + Write-Verbose "[Set-xAzDoPermission] Started." + + # Define a hashtable to store parameters for the Invoke-AzDevOpsApiRestMethod function. + + $params = @{ + # Construct the Uri using string formatting with the -f operator. + # It includes the API endpoint, group identity, member identity, and the API version. + Uri = "https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}" -f $OrganizationName, + $SecurityNamespaceID, + $ApiVersion + # Set the method to PUT. + Method = 'POST' + # Set the body of the request to the serialized ACLs. + Body = $SerializedACLs | ConvertTo-Json -Depth 4 + } + + Write-Verbose "[Set-xAzDoPermission] Body: $($params.Body)" + + try { + # Call the Invoke-AzDevOpsApiRestMethod function with the parameters defined above. + # The "@" symbol is used to pass the hashtable as splatting parameters. + Write-Verbose "[Set-xAzDoPermission] Attempting to invoke REST method to set ACLs." + $null = Invoke-AzDevOpsApiRestMethod @params + + } catch { + # If an exception occurs, write an error message to the console with details about the issue. + Write-Error "[Set-xAzDoPermission] Failed to set ACLs: $($_.Exception.Message)" + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.ps1 new file mode 100644 index 000000000..33afa9d93 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.ps1 @@ -0,0 +1,35 @@ + +function List-DevOpsGitRepository +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$OrganizationName, + + [Parameter(Mandatory)] + [String]$ProjectName, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + $params = @{ + Uri = "https://dev.azure.com/$OrganizationName/$ProjectName/_apis/git/repositories" + Method = 'Get' + } + + # + # Invoke the Rest API to get the groups + $repositories = Invoke-AzDevOpsApiRestMethod @params + + if ($null -eq $repositories.value) + { + return $null + } + + # + # Return the groups from the cache + return $repositories.Value + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.ps1 new file mode 100644 index 000000000..60ba0a056 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.ps1 @@ -0,0 +1,33 @@ +function List-DevOpsGroupMembers +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string] + $Organization, + [Parameter(Mandatory)] + [String] + $GroupDescriptor, + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + $params = @{ + Uri = "https://vssps.dev.azure.com/{0}/_apis/graph/Memberships/{1}?direction=down" -f $Organization, $GroupDescriptor + Method = 'Get' + } + + # + # Invoke the Rest API to get the groups + $membership = Invoke-AzDevOpsApiRestMethod @params + + if ($null -eq $membership.value) { + return $null + } + + # + # Return the groups from the cache + return $membership.Value + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.ps1 new file mode 100644 index 000000000..6fa91d6d6 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.ps1 @@ -0,0 +1,34 @@ +Function List-DevOpsGroups { + [CmdletBinding()] + [OutputType([System.Object])] + Param + ( + [Parameter(Mandatory)] + [string] + $Organization, + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + $params = @{ + Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/groups" + Method = 'Get' + } + + # + # Invoke the Rest API to get the groups + $groups = Invoke-AzDevOpsApiRestMethod @params + + if ($null -eq $groups.value) { + return $null + } + + # + # Perform a lookup to get the group + + # + # Return the groups from the cache + return $groups.Value + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.ps1 new file mode 100644 index 000000000..9c812057d --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.ps1 @@ -0,0 +1,31 @@ +Function List-DevOpsProcess { + [CmdletBinding()] + [OutputType([System.Object])] + Param + ( + [Parameter(Mandatory)] + [string] + $Organization, + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + $params = @{ + Uri = "https://dev.azure.com/{0}/_apis/process/processes?api-version={1}" -f $Organization, $ApiVersion + Method = 'Get' + } + + # + # Invoke the Rest API to get the groups + $groups = Invoke-AzDevOpsApiRestMethod @params + + if ($null -eq $groups.value) { + return $null + } + + # + # Return the groups from the cache + return $groups.Value + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.ps1 new file mode 100644 index 000000000..325f17873 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.ps1 @@ -0,0 +1,32 @@ + +function List-DevOpsProjects +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$OrganizationName, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + $params = @{ + Uri = "https://dev.azure.com/$OrganizationName/_apis/projects" + Method = 'Get' + } + + # + # Invoke the Rest API to get the groups + $groups = Invoke-AzDevOpsApiRestMethod @params + + if ($null -eq $groups.value) + { + return $null + } + + # + # Return the groups from the cache + return $groups.Value + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.ps1 new file mode 100644 index 000000000..798971086 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.ps1 @@ -0,0 +1,30 @@ +Function List-DevOpsSecurityNamespaces { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [String]$OrganizationName + ) + + # Use a verbose statement to indicate the start of the function. + Write-Verbose "[List-DevOpsSecurityNamespaces] Started." + + # Params + $params = @{ + Uri = "https://dev.azure.com/$OrganizationName/_apis/securitynamespaces/" + Method = 'Get' + } + + # + # Invoke the Rest API to get the groups + $namespaces = Invoke-AzDevOpsApiRestMethod @params + + if ($null -eq $namespaces.value) + { + return $null + } + + # + # Return the groups from the cache + return $namespaces.Value + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.ps1 new file mode 100644 index 000000000..d6a437e16 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.ps1 @@ -0,0 +1,31 @@ +Function List-DevOpsServicePrinciples { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$OrganizationName, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + $params = @{ + Uri = "https://vssps.dev.azure.com/$OrganizationName/_apis/graph/serviceprincipals" + Method = 'Get' + } + + # + # Invoke the Rest API to get the groups + $serviceprincipals = Invoke-AzDevOpsApiRestMethod @params + + if ($null -eq $serviceprincipals.value) + { + return $null + } + + # + # Return the groups from the cache + return $serviceprincipals.Value + + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.ps1 new file mode 100644 index 000000000..cd1f9ecdb --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.ps1 @@ -0,0 +1,30 @@ +function List-UserCache +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$OrganizationName, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + $params = @{ + Uri = "https://vssps.dev.azure.com/$OrganizationName/_apis/graph/users" + Method = 'Get' + } + + # + # Invoke the Rest API to get the groups + $users = Invoke-AzDevOpsApiRestMethod @params + + if ($null -eq $users.value) { + return $null + } + + # + # Return the groups from the cache + return $users.Value + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.ps1 new file mode 100644 index 000000000..a1fa38139 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.ps1 @@ -0,0 +1,56 @@ +Function New-GitRepository { + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + + [Parameter(Mandatory)] + [Alias('URI')] + [System.String]$ApiUri, + + [Parameter(Mandatory)] + [Alias('Name')] + [Object]$Project, + + [Parameter(Mandatory)] + [Alias('Repository')] + [System.String]$RepositoryName, + + [Parameter()] + [Alias('Source')] + [System.String]$SourceRepository, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + + ) + + Write-Verbose "[New-GitRepository] Creating new repository '$($RepositoryName)' in project '$($Project.name)'" + + # Define parameters for creating a new DevOps group + $params = @{ + ApiUri = "{0}/{1}/_apis/git/repositories?api-version={2}" -f $ApiUri, $Project.name, $ApiVersion + Method = 'POST' + ContentType = 'application/json' + Body = @{ + name = $RepositoryName + project = @{ + id = $Project.id + } + } | ConvertTo-Json + } + + # Try to invoke the REST method to create the group and return the result + try { + $repo = Invoke-AzDevOpsApiRestMethod @params + Write-Verbose "[New-GitRepository] Repository Created: '$($repo.name)'" + return $repo + } + # Catch any exceptions and write an error message + catch { + Write-Error "[New-GitRepository] Failed to Create Repository: $_" + } + + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.ps1 new file mode 100644 index 000000000..42ce64043 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.ps1 @@ -0,0 +1,44 @@ +Function Remove-GitRepository { + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + + [Parameter(Mandatory)] + [Alias('URI')] + [System.String]$ApiUri, + + [Parameter(Mandatory)] + [Alias('Name')] + [Object]$Project, + + [Parameter(Mandatory)] + [Alias('Repo')] + [Object]$Repository, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + + ) + + Write-Verbose "[Remove-GitRepository] Removing repository '$($Repository.Name)' in project '$($Project.name)'" + + # Define parameters for creating a new DevOps group + $params = @{ + ApiUri = "{0}/{1}/_apis/git/repositories/{2}?api-version={3}" -f $ApiUri, $Project.name, $Repository.id , $ApiVersion + Method = 'Delete' + } + + # Try to invoke the REST method to create the group and return the result + try { + $null = Invoke-AzDevOpsApiRestMethod @params + return + } + # Catch any exceptions and write an error message + catch { + Write-Error "[Remove-GitRepository] Failed to Create Repository: $_" + } + + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.ps1 new file mode 100644 index 000000000..e7b173f55 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.ps1 @@ -0,0 +1,94 @@ +<# +.SYNOPSIS +Creates a new group in Azure DevOps. + +.DESCRIPTION +The New-DevOpsGroup function creates a new group in Azure DevOps using the specified parameters. + +.PARAMETER ApiUri +The URI of the Azure DevOps API. + +.PARAMETER GroupName +The name of the group to create. + +.PARAMETER ApiVersion +The version of the Azure DevOps API to use. If not specified, the default version will be used. + +.PARAMETER ProjectScopeDescriptor +The scope descriptor of the project. If specified, the group will be created within the specified project scope. + +.OUTPUTS +System.Management.Automation.PSObject + +.EXAMPLE +New-DevOpsGroup -ApiUri "https://dev.azure.com/myorganization" -GroupName "MyGroup" + +Creates a new group named "MyGroup" in Azure DevOps using the specified API URI. + +.EXAMPLE +New-DevOpsGroup -ApiUri "https://dev.azure.com/myorganization" -GroupName "MyGroup" -ProjectScopeDescriptor "vstfs:///Classification/TeamProject/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + +Creates a new group named "MyGroup" in Azure DevOps within the specified project scope. + +#> +# Define a function to create a new Azure DevOps Group +Function New-DevOpsGroup { + # CmdletBinding attribute specifies that this function is written as an advanced function + [CmdletBinding()] + # OutputType attribute defines the output type of the function which is PSObject in this case + [OutputType([System.Management.Automation.PSObject])] + # Parameters block defining the parameters accepted by the function + param + ( + # Parameter attribute marks this as a mandatory parameter that the user must supply when calling the function. + [Parameter(Mandatory)] + [string] + $ApiUri, # The URI for the Azure DevOps API. + + # Mandatory parameter for the group name + [Parameter(Mandatory)] + [string] + $GroupName, + + # Optional parameter for the group description with a default value of null + [Parameter()] + [String] + $GroupDescription = $null, + + # Optional parameter for the API version with a default value obtained from the Get-AzDevOpsApiVersion function + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default), + + # Optional parameter for the project scope descriptor + [Parameter()] + [String] + $ProjectScopeDescriptor + ) + + # Hashtable to hold parameters for the API request + $params = @{ + Uri = "{0}/_apis/graph/groups?api-version={1}" -f $ApiUri, $ApiVersion + Method = 'Post' + ContentType = 'application/json' + Body = @{ + displayName = $GroupName + description = $GroupDescription + } | ConvertTo-Json + } + + # If ProjectScopeDescriptor is provided, modify the URI to include it + if ($ProjectScopeDescriptor) { + $params.Uri = "{0}/_apis/graph/groups?scopeDescriptor={1}&api-version={2}" -f $ApiUri, $ProjectScopeDescriptor, $ApiVersion + } + + # Try to invoke the REST method to create the group and return the result + try { + $group = Invoke-AzDevOpsApiRestMethod @params + return $group + } + # Catch any exceptions and write an error message + catch { + Write-Error "Failed to create group: $_" + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.ps1 new file mode 100644 index 000000000..beb2dcef8 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.ps1 @@ -0,0 +1,57 @@ +<# +.SYNOPSIS +Removes a group from Azure DevOps. + +.DESCRIPTION +The Remove-DevOpsGroup function is used to remove a group from Azure DevOps using the Azure DevOps REST API. + +.PARAMETER ApiUri +The mandatory parameter for the API URI. + +.PARAMETER ApiVersion +The optional parameter for the API version with a default value obtained from the Get-AzDevOpsApiVersion function. + +.PARAMETER GroupDescriptor +The optional parameter for the project scope descriptor. + +.OUTPUTS +System.Management.Automation.PSObject + +.EXAMPLE +Remove-DevOpsGroup -ApiUri "https://dev.azure.com/myorganization" -GroupDescriptor "MyGroup" + +This example removes the group with the specified group descriptor from Azure DevOps. + +#> + +Function Remove-DevOpsGroup { + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject])] + param + ( + [Parameter(Mandatory)] + [string] + $ApiUri, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default), + + [Parameter()] + [String] + $GroupDescriptor + ) + + $params = @{ + Uri = "{0}/_apis/graph/groups/{1}?api-version={2}" -f $ApiUri, $GroupDescriptor, $ApiVersion + Method = 'Delete' + ContentType = 'application/json' + } + + try { + return (Invoke-AzDevOpsApiRestMethod @params) + } + catch { + Write-Error "Failed to remove group: $_" + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.ps1 new file mode 100644 index 000000000..433eddcc6 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.ps1 @@ -0,0 +1,109 @@ +<# +.SYNOPSIS +Updates an Azure DevOps group. + +.DESCRIPTION +The Set-DevOpsGroup function is used to update an Azure DevOps group by sending a PATCH request to the Azure DevOps REST API. + +.PARAMETER ApiUri +The mandatory parameter for the API URI. This should be the base URI of the Azure DevOps organization. + +.PARAMETER GroupName +The mandatory parameter for the group name. This specifies the name of the group to be updated. + +.PARAMETER GroupDescription +The optional parameter for the group description. This specifies the new description for the group. If not provided, the description will not be modified. + +.PARAMETER ApiVersion +The optional parameter for the API version. This specifies the version of the Azure DevOps REST API to use. If not provided, the default API version will be obtained from the Get-AzDevOpsApiVersion function. + +.PARAMETER ProjectScopeDescriptor +The optional parameter for the project scope descriptor. This specifies the scope descriptor for the project. If provided, the group will be updated within the specified project scope. + +.OUTPUTS +The function returns a PSObject representing the updated group. + +.EXAMPLE +Set-DevOpsGroup -ApiUri "https://dev.azure.com/contoso" -GroupName "MyGroup" -GroupDescription "Updated group description" + +This example updates the group named "MyGroup" in the Azure DevOps organization "https://dev.azure.com/contoso" with the new description "Updated group description". + +#> + +# This function is designed to update the description of a group in Azure DevOps. +Function Set-DevOpsGroup { + # CmdletBinding attribute allows the function to use cmdlet parameters and supports advanced functionality like ShouldProcess. + [CmdletBinding(DefaultParameterSetName = 'Default')] + # OutputType attribute specifies the type of object that the function returns. + [OutputType([System.Management.Automation.PSObject])] + param + ( + # Parameter attribute marks this as a mandatory parameter that the user must supply when calling the function. + [Parameter(Mandatory, ParameterSetName = 'ProjectScope')] + [Parameter(Mandatory, ParameterSetName = 'Default')] + [string] + $ApiUri, # The URI for the Azure DevOps API. + + [Parameter(Mandatory, ParameterSetName = 'ProjectScope')] + [Parameter(Mandatory, ParameterSetName = 'Default')] + [string] + $GroupName, # The name of the group to be updated. + + # Optional parameter with a default value of $null if not specified by the user. + [Parameter(ParameterSetName = 'ProjectScope')] + [Parameter(ParameterSetName = 'Default')] + [String] + $GroupDescription = $null, # The new description for the group. + + # Optional parameter that gets the default API version if not specified. + [Parameter(ParameterSetName = 'ProjectScope')] + [Parameter(ParameterSetName = 'Default')] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default), # The API version to use for the request. + + # Group Descriptor for the project within which the group exists. + [Parameter(Mandatory, ParameterSetName = 'Default')] + [String] + $GroupDescriptor, + + # Optional parameter without a default value. + [Parameter(Mandatory, ParameterSetName = 'ProjectScope')] + [String] + $ProjectScopeDescriptor # Scope descriptor for the project within which the group exists. + ) + + # A hashtable is created to hold parameters that will be used in the REST method invocation. + $params = @{ + Uri = "{0}/_apis/graph/groups/{1}?api-version={2}" -f $ApiUri, $GroupDescriptor, $ApiVersion # The API endpoint, formatted with the base URI and API version. + Method = 'Patch' # The HTTP method used for the request, indicating an update operation. + ContentType = 'application/json-patch+json' # The content type of the request body. + Body = @( + @{ + op = "replace" # Operation type in JSON Patch format, here adding a new value. + path = "/displayName" # The path in the target object to add the new value. + value = $GroupName # The value to add, which is the new group display name. + } + @{ + op = "replace" # Operation type in JSON Patch format, here adding a new value. + path = "/description" # The path in the target object to add the new value. + value = $GroupDescription # The value to add, which is the new group description. + } + ) | ConvertTo-Json # Convert the hashtable to JSON format for the request body. + } + + # If ProjectScopeDescriptor is provided, modify the URI to include it in the query parameters. + if ($ProjectScopeDescriptor) { + $params.Uri = "{0}/_apis/graph/groups?scopeDescriptor={1}&api-version={2}" -f $ApiUri, $ProjectScopeDescriptor, $ApiVersion + } + + try { + # Invoke the REST method with the parameters and store the result in $group. + $group = Invoke-AzDevOpsApiRestMethod @params + return $group # Return the result of the REST method call. + } + catch { + # Write an error message to the console if the REST method call fails. + Write-Error "Failed to create group: $_" + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.ps1 new file mode 100644 index 000000000..f784266a2 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.ps1 @@ -0,0 +1,62 @@ +Function New-DevOpsGroupMember { + [CmdletBinding()] + param + ( + # The group Identity + [Parameter(Mandatory)] + [Alias('Group')] + [Object]$GroupIdentity, + + # The group member + [Parameter(Mandatory)] + [Alias('Member')] + [Object]$MemberIdentity, + + # Optional parameter for the API version with a default value obtained from the Get-AzDevOpsApiVersion function + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default), + + # The URI for the Azure DevOps API. + [Parameter(Mandatory)] + [string] + $ApiUri + + ) + + # Define a hashtable to store parameters for the Invoke-AzDevOpsApiRestMethod function. + $params = @{ + # Construct the Uri using string formatting with the -f operator. + # It includes the API endpoint, group identity, member identity, and the API version. + Uri = "{0}/_apis/graph/memberships/{1}/{2}?api-version={3}" -f $ApiUri, + $MemberIdentity.descriptor, + $GroupIdentity.descriptor, + $ApiVersion + # Specifies the HTTP method to be used in the REST call, in this case 'PUT'. + Method = 'PUT' + } + + Write-Verbose "[Add-DevOpsGroupMember] Constructed URI for REST call: $($params.Uri)" + + # Try to invoke the REST method to create the group and return the result + + try { + # Call the Invoke-AzDevOpsApiRestMethod function with the parameters defined above. + # The "@" symbol is used to pass the hashtable as splatting parameters. + Write-Verbose "[Add-DevOpsGroupMember] Attempting to invoke REST method to add group member." + $member = Invoke-AzDevOpsApiRestMethod @params + Write-Verbose "[Add-DevOpsGroupMember] Member added successfully." + + } catch { + # If an exception occurs, write an error message to the console with details about the issue. + Write-Error "[Add-DevOpsGroupMember] Failed to add member to group: $($_.Exception.Message)" + } + + Write-Verbose "[Add-DevOpsGroupMember] Result $($member | ConvertTo-Json)." + + # Return the result of the REST method invocation, which is stored in $member. + Write-Verbose "[Add-DevOpsGroupMember] Returning result from REST method invocation." + return $member + + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.ps1 new file mode 100644 index 000000000..2d1438a22 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.ps1 @@ -0,0 +1,62 @@ +Function Remove-DevOpsGroupMember { + [CmdletBinding()] + param + ( + # The group Identity + [Parameter(Mandatory)] + [Alias('Group')] + [Object]$GroupIdentity, + + # The group member + [Parameter(Mandatory)] + [Alias('Member')] + [Object]$MemberIdentity, + + # Optional parameter for the API version with a default value obtained from the Get-AzDevOpsApiVersion function + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default), + + # The URI for the Azure DevOps API. + [Parameter(Mandatory)] + [string] + $ApiUri + + ) + + # Define a hashtable to store parameters for the Invoke-AzDevOpsApiRestMethod function. + $params = @{ + # Construct the Uri using string formatting with the -f operator. + # It includes the API endpoint, group identity, member identity, and the API version. + Uri = "{0}/_apis/graph/memberships/{1}/{2}?api-version={3}" -f $ApiUri, + $MemberIdentity.descriptor, + $GroupIdentity.descriptor, + $ApiVersion + # Specifies the HTTP method to be used in the REST call, in this case 'PUT'. + Method = 'DELETE' + } + + Write-Verbose "[Remove-DevOpsGroupMember] Constructed URI for REST call: $($params.Uri)" + + # Try to invoke the REST method to create the group and return the result + + try { + # Call the Invoke-AzDevOpsApiRestMethod function with the parameters defined above. + # The "@" symbol is used to pass the hashtable as splatting parameters. + Write-Verbose "[Remove-DevOpsGroupMember] Attempting to invoke REST method to remove group member." + $member = Invoke-AzDevOpsApiRestMethod @params + Write-Verbose "[Remove-DevOpsGroupMember] Member removed successfully." + + } catch { + # If an exception occurs, write an error message to the console with details about the issue. + Write-Error "[Remove-DevOpsGroupMember] Failed to add member to group: $($_.Exception.Message)" + } + + #Write-Verbose "[Remove-DevOpsGroupMember] Result $($member | ConvertTo-Json)." + + # Return the result of the REST method invocation, which is stored in $member. + #Write-Verbose "[Remove-DevOpsGroupMember] Returning result from REST method invocation." + #return $member + + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.Remove-AzDevOpsApiResource.Tests.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Permission/Empty.txt similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.Remove-AzDevOpsApiResource.Tests.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Permission/Empty.txt diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.ps1 new file mode 100644 index 000000000..c625eb183 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.ps1 @@ -0,0 +1,102 @@ +<# +.SYNOPSIS +Creates a new Azure DevOps project. + +.DESCRIPTION +This function creates a new Azure DevOps project using the Azure DevOps REST API. It requires the organization name, project name, description, visibility (either "private" or "public"), and a personal access token for authentication. + +.PARAMETER Organization +The name of the Azure DevOps organization. + +.PARAMETER ProjectName +The name of the project to be created. + +.PARAMETER Description +A brief description of the project. + +.PARAMETER Visibility +The visibility of the project. Valid values are "private" or "public". + +.PARAMETER PersonalAccessToken +The personal access token used for authentication. + +.EXAMPLE +New-DevOpsProject -Organization "myorg" -ProjectName "MyProject" -Description "This is a new project" -Visibility "private" -PersonalAccessToken "mytoken" + +This example creates a new private Azure DevOps project named "MyProject" with the description "This is a new project" in the organization "myorg" using the specified personal access token. + +#> +function New-DevOpsProject +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$Organization, + + [Parameter()] + [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] + [Alias('Name')] + [System.String] + $ProjectName, + + [Parameter()] + [Alias('Description')] + [System.String] + $ProjectDescription, + + [Parameter()] + [System.String] + $SourceControlType, + + [Parameter()] + [System.String]$ProcessTemplateId, + + [Parameter()] + [System.String]$Visibility, + + # Get the latest API version. 7.1 is not supported by the API endpoint. + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion | Select-Object -Last 1) + + ) + + # Validate the parameters + $params = @{ + Uri = "https://dev.azure.com/{0}/_apis/projects?api-version={1}" -f $Organization, $ApiVersion + Method = "POST" + Body = @{ + name = $ProjectName + description = $ProjectDescription + visibility = $Visibility + capabilities = @{ + versioncontrol = @{ + sourceControlType = $SourceControlType + } + processTemplate = @{ + templateTypeId = $ProcessTemplateId + } + } + } + } + + # Seralize the Body to JSON + $params.Body = $params.Body | ConvertTo-Json + + try + { + # Invoke the Azure DevOps REST API to create the project + $response = Invoke-AzDevOpsApiRestMethod @params + + if ($null -eq $response) { + Throw "[New-DevOpsProject] Failed to create the Azure DevOps project: No response returned" + } + + # Output the response which contains the created project details + return $response + } catch + { + Write-Error "[New-DevOpsProject] Failed to create the Azure DevOps project: $_" + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.ps1 new file mode 100644 index 000000000..26baffdef --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.ps1 @@ -0,0 +1,60 @@ +<# +.SYNOPSIS +Removes an Azure DevOps project. + +.DESCRIPTION +The Remove-DevOpsProject function is used to remove an Azure DevOps project from the specified organization. + +.PARAMETER Organization +The name or URL of the Azure DevOps organization. + +.PARAMETER ProjectId +The ID or name of the project to be removed. + +.EXAMPLE +Remove-DevOpsProject -Organization "MyOrganization" -ProjectId "MyProject" + +This example removes the Azure DevOps project with the ID "MyProject" from the organization "MyOrganization". + +#> + +function Remove-DevOpsProject +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$Organization, + + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String] + $ProjectId, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion | Select-Object -Last 1) + + ) + + Write-Verbose "[Remove-DevOpsProject] Started." + + # Define the API version to use + $params = @{ + Uri = "https://dev.azure.com/{0}/_apis/projects/{1}?api-version={2}" -f $Organization, $ProjectId, $ApiVersion + Method = "DELETE" + } + + Write-Verbose "[Remove-DevOpsProject] Removing project $ProjectId from Azure DevOps organization $Organization" + + try + { + # Invoke the Azure DevOps REST API to create the project + $response = Invoke-AzDevOpsApiRestMethod @params + # Output the response which contains the created project details + return $response + } catch + { + Write-Error "[Remove-DevOpsProject] Failed to create the Azure DevOps project: $_" + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 new file mode 100644 index 000000000..6648ec2c8 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 @@ -0,0 +1,102 @@ +<# +.SYNOPSIS +Updates an Azure DevOps project. + +.DESCRIPTION +This function updates an Azure DevOps project with the specified parameters. It allows you to change the project name, description, visibility, and personal access token. + +.PARAMETER Organization +The name or ID of the Azure DevOps organization. + +.PARAMETER ProjectId +The ID or name of the project to update. + +.PARAMETER NewName +The new name for the project. + +.PARAMETER Description +The new description for the project. + +.PARAMETER Visibility +The visibility of the project. Valid values are 'private' and 'public'. + +.PARAMETER PersonalAccessToken +The personal access token (PAT) used for authentication. + +.EXAMPLE +Update-DevOpsProject -Organization "contoso" -ProjectId "MyProject" -NewName "NewProjectName" -Description "Updated project description" -Visibility "public" -PersonalAccessToken "PAT" + +This example updates the project named "MyProject" in the "contoso" organization. It changes the project name to "NewProjectName", updates the description, sets the visibility to "public", and uses the specified personal access token for authentication. + +#> +function Update-DevOpsProject +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$Organization, + + [Parameter()] + [Alias('Name')] + [System.String] + $ProjectId, + + [Parameter()] + [Alias('Description')] + [System.String] + $ProjectDescription, + + [Parameter()] + [System.String]$ProcessTemplateId, + + [Parameter()] + [System.String]$Visibility, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion | Select-Object -Last 1) + + ) + + Write-Verbose "[Update-DevOpsProject] Updating project '$ProjectId' in organization '$Organization'" + + # Construct the body of the request + $body = @{ + name = $ProjectName + visibility = $Visibility + <# + TODO: ISSUE with updating the ProcessTemplateId. + capabilities = @{ + processTemplate = @{ + templateTypeId = $ProcessTemplateId + } + } + #> + } + + # Add the description if provided + if ($ProjectDescription) + { + $body.description = $ProjectDescription + } + + # Construct the Paramters for the Invoke-AzDevOpsApiRestMethod function + $params = @{ + Uri = "https://dev.azure.com/{0}/_apis/projects/{1}?api-version={2}" -f $Organization, $ProjectId, $ApiVersion + Body = $body | ConvertTo-Json + Method = 'PATCH' + } + + # Invoke the Azure DevOps REST API to update the project + try + { + $response = Invoke-AzDevOpsApiRestMethod @params + } catch + { + Write-Error "Failed to update the Azure DevOps project: $_" + } + + # Output the response which contains the updated project details + return $response + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 new file mode 100644 index 000000000..be809a163 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 @@ -0,0 +1,86 @@ +<# +.SYNOPSIS + Waits for a project to be created in Azure DevOps. + +.DESCRIPTION + The Wait-DevOpsProject function waits for a project to be created in Azure DevOps. It checks the status of the project creation and waits until the project is either created successfully or fails to be created. + +.PARAMETER OrganizationName + The name of the Azure DevOps organization. + +.PARAMETER ProjectURL + The URL of the project to wait for. + +.PARAMETER ApiVersion + The version of the Azure DevOps API to use. If not specified, the default API version will be used. + +.EXAMPLE + Wait-DevOpsProject -OrganizationName "MyOrg" -ProjectURL "https://dev.azure.com/MyOrg/MyProject" + +.NOTES + Author: Your Name + Date: Current Date +#> + +Function Wait-DevOpsProject { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$OrganizationName, + + [Parameter(Mandatory = $true)] + [string]$ProjectURL, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + $params = @{ + Uri = "{0}" -f $ProjectURL + Method = "GET" + } + + Write-Verbose "[Wait-DevOpsProject] URI: $($params.URI)" + + # Loop until the project is created + $counter = 0 + do { + Write-Verbose "[Wait-DevOpsProject] Sending request to check project status..." + $response = Invoke-AzDevOpsApiRestMethod @params + $project = $response + + # Check the status of the project + switch ($response.status) { + 'creating' { + Write-Verbose "[Wait-DevOpsProject] Project is still being created..." + Start-Sleep -Seconds 5 + } + 'wellFormed' { + Write-Verbose "[Wait-DevOpsProject] Project has been created successfully." + break + } + 'failed' { + Write-Error "[Wait-DevOpsProject] Project creation failed: $response" + break + } + 'notSet' { + Write-Error "[Wait-DevOpsProject] Project creation status is not set: $response" + break + } + default { + # Still creating + Write-Verbose "[Wait-DevOpsProject] Project is still being created (default case)..." + Start-Sleep -Seconds 5 + } + } + + # Increment the counter + $counter++ + + } while ($counter -lt 10) + + if ($counter -ge 10) { + Write-Error "[Wait-DevOpsProject] Timed out waiting for project to be created." + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.ps1 new file mode 100644 index 000000000..a10a1cf1e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.ps1 @@ -0,0 +1,43 @@ +function Get-ProjectServiceStatus +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$Organization, + + [Parameter(Mandatory = $true)] + [string]$ProjectId, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + # Get the project + # Construct the URI with optional state filter + $params = @{ + Uri = 'https://dev.azure.com/{0}/_apis/FeatureManagement/FeatureStates/host/project/{1}/{2}?api-version={3}' -f $Organization, $ProjectId, $ServiceName, $ApiVersion + Method = 'Get' + } + + try + { + $response = Invoke-AzDevOpsApiRestMethod @params + # If the service is 'undefined' then treat it as 'enabled' + if ($response.state -eq 'undefined') + { + $response.state = 'enabled' + } + + # Output the state of the service + return $response + } + catch + { + Write-Error "Failed to get Security Descriptor: $_" + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.ps1 new file mode 100644 index 000000000..f47b6a32e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.ps1 @@ -0,0 +1,41 @@ +function Set-ProjectServiceStatus +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$Organization, + + [Parameter(Mandatory = $true)] + [string]$ProjectId, + + [Parameter(Mandatory = $true)] + [string]$ServiceName, + + [Parameter(Mandatory = $true)] + [Object]$Body, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + # Get the project + # Construct the URI with optional state filter + $params = @{ + Uri = 'https://dev.azure.com/{0}/_apis/FeatureManagement/FeatureStates/host/project/{1}/{2}?api-version={3}' -f $Organization, $ProjectId, $ServiceName, $ApiVersion + Method = 'PATCH' + Body = $Body | ConvertTo-Json + } + + try + { + $response = Invoke-AzDevOpsApiRestMethod @params + # Output the state of the service + return $response.state + } + catch + { + Write-Error "Failed to set Security Descriptor: $_" + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.Set-AzDevOpsApiResource.Tests.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Resource/Empty.txt similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/_TODO2.Set-AzDevOpsApiResource.Tests.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Resource/Empty.txt diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/_TODO2.New-AzDevOpsProject.Tests.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/RoleAssignments/Empty.txt similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/_TODO2.New-AzDevOpsProject.Tests.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/RoleAssignments/Empty.txt diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/_TODO2.Remove-AzDevOpsProject.Tests.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/RoleDefinition/empty.txt similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/_TODO2.Remove-AzDevOpsProject.Tests.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/RoleDefinition/empty.txt diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.ps1 new file mode 100644 index 000000000..f6703b926 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.ps1 @@ -0,0 +1,57 @@ +<# +.SYNOPSIS +Retrieves the security descriptor for a project in Azure DevOps. + +.DESCRIPTION +The Get-DevOpsSecurityDescriptor function retrieves the security descriptor for a project in Azure DevOps. +It uses the Azure DevOps REST API to perform a lookup and retrieve the descriptor. + +.PARAMETER ProjectName +The name of the project. + +.PARAMETER Organization +The name of the Azure DevOps organization. + +.PARAMETER ApiVersion +The version of the Azure DevOps REST API to use. If not specified, the default version will be used. + +.EXAMPLE +Get-DevOpsSecurityDescriptor -ProjectId "ProjectID" -Organization "MyOrganization" + +This example retrieves the security descriptor for the project named "MyProject" in the Azure DevOps organization "MyOrganization". + +#> +function Get-DevOpsSecurityDescriptor +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string] + $ProjectId, + [Parameter(Mandatory)] + [string] + $Organization, + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + # Get the project + # Construct the URI with optional state filter + $params = @{ + Uri = "https://vssps.dev.azure.com/{0}/_apis/graph/descriptors/{1}?api-version={2}" -f $Organization, $ProjectId, $ApiVersion + Method = 'Get' + } + + try + { + $response = Invoke-AzDevOpsApiRestMethod @params + # Output the security descriptor + return $response.value + } + catch + { + Write-Error "Failed to get Security Descriptor: $_" + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/_TODO2.Set-AzDevOpsProject.Tests.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ServiceEndpoint/empty.txt similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/_TODO2.Set-AzDevOpsProject.Tests.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ServiceEndpoint/empty.txt diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ServicePrincipal/empty.txt b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ServicePrincipal/empty.txt new file mode 100644 index 000000000..e69de29bb diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.ps1 new file mode 100644 index 000000000..2fb3d997e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.ps1 @@ -0,0 +1,53 @@ +Function Add-AuthenticationHTTPHeader { + + # Dertimine the type of token. + + $headerValue = "" + + switch ($Global:DSCAZDO_AuthenticationToken.tokenType) { + + # If the token is null + {[String]::IsNullOrEmpty($_)} { + throw "[Add-AuthenticationHTTPHeader] Error. The authentication token is null. Please ensure that the authentication token is set." + } + {$_ -eq 'PersonalAccessToken'} { + + # + # Personal Access Token + + # Add the Personal Access Token to the header + $headerValue = "Authorization: Basic {0}" -f $Global:DSCAZDO_AuthenticationToken.Get() + break + } + {$_ -eq 'ManagedIdentity'} { + + # + # Managed Identity Token + + Write-Verbose "[Add-AuthenticationHTTPHeader] Adding Managed Identity Token to the HTTP Headers." + + # Test if the Managed Identity Token has expired + if ($Global:DSCAZDO_AuthenticationToken.isExpired()) + { + Write-Verbose "[Add-AuthenticationHTTPHeader] Managed Identity Token has expired. Obtaining a new token." + # If so, get a new token + $Global:DSCAZDO_AuthenticationToken = Update-AzManagedIdentity -OrganizationName $Global:DSCAZDO_OrganizationName + } + + # Add the Managed Identity Token to the header + $headerValue = 'Bearer {0}' -f $Global:DSCAZDO_AuthenticationToken.Get() + break + + } + default { + throw "[Add-AuthenticationHTTPHeader] Error. The authentication token type is not supported." + } + + } + + Write-Verbose "[Add-AuthenticationHTTPHeader] Adding Header" + + # Return the header value + return $headerValue + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 new file mode 100644 index 000000000..f9fd7ae71 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 @@ -0,0 +1,115 @@ +<# +.SYNOPSIS +Obtains a managed identity token from Azure AD. + +.DESCRIPTION +The Get-AzManagedIdentityToken function is used to obtain an access token from Azure AD using a managed identity. It can only be called from the New-AzManagedIdentity or Update-AzManagedIdentity functions. + +.PARAMETER OrganizationName +Specifies the name of the organization. + +.PARAMETER Verify +Specifies whether to verify the connection. If this switch is not set, the function returns the managed identity token. If the switch is set, the function tests the connection and returns the access token. + +.EXAMPLE +Get-AzManagedIdentityToken -OrganizationName "Contoso" -Verify +Obtains the access token for the managed identity associated with the organization "Contoso" and verifies the connection. + +.NOTES +This function does not require the Azure PowerShell module. +#> + +Function Get-AzManagedIdentityToken { + [CmdletBinding()] + param ( + # Organization Name + [Parameter(Mandatory)] + [String] + $OrganizationName, + + # Verify the Connection + [Parameter()] + [Switch] + $Verify + ) + + Write-Verbose "[Get-AzManagedIdentityToken] Getting the managed identity token for the organization $OrganizationName." + + # Import the parameters + $ManagedIdentityParams = @{ + # Define the Azure instance metadata endpoint to get the access token + Uri = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=499b84ac-1321-427f-aa17-267ca6975798" + Method = 'Get' + Headers = @{ Metadata="true" } + ContentType = 'Application/json' + NoAuthentication = $true + } + + # Dertimine if the machine is an arc machine + if ($env:IDENTITY_ENDPOINT) { + + # Test if console is being run as Administrator + if (-not([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + throw "[Get-AzManagedIdentityToken] Error: Authentication to Azure Arc requires Administrator privileges." + } + + Write-Verbose "[Get-AzManagedIdentityToken] The machine is an Azure Arc machine. The Uri needs to be updated to $($env:IDENTITY_ENDPOINT):" + $ManagedIdentityParams.Uri = "{0}?api-version=2020-06-01&resource=499b84ac-1321-427f-aa17-267ca6975798" -f $env:IDENTITY_ENDPOINT + $ManagedIdentityParams.AzureArcAuthentication = $true + + } else { + Write-Verbose "[Get-AzManagedIdentityToken] The machine is not an Azure Arc machine. No changes are required." + } + + # Obtain the access token from Azure AD using the Managed Identity + Write-Verbose "[Get-AzManagedIdentityToken] Invoking the Azure Instance Metadata Service to get the access token." + + # Invoke the RestAPI + try { + $response = Invoke-AzDevOpsApiRestMethod @ManagedIdentityParams + } catch { + + # If there is an error it could be because it's an arc machine, and we need to use the secret file: + $wwwAuthHeader = $_.Exception.Response.Headers.WwwAuthenticate + if ($wwwAuthHeader -notmatch "Basic realm=.+") + { + Throw "[Get-AzManagedIdentityToken] {0}" -f $_ + } + + Write-Verbose "[Get-AzManagedIdentityToken] Managed Identity Token Retrival Failed. Retrying with secret file." + + # Extract the secret file path from the WWW-Authenticate header + $secretFile = ($wwwAuthHeader -split "Basic realm=")[1] + # Read the secret file to get the token + $token = Get-Content -LiteralPath $secretFile -Raw + # Add the token to the headers + $ManagedIdentityParams.Headers.Authorization = "Basic $token" + + # Retry the request. Silently continue to suppress the error message, since we will handle it below. + $response = Invoke-AzDevOpsApiRestMethod @ManagedIdentityParams -ErrorAction SilentlyContinue + + } + # Test the response + if ($null -eq $response.access_token) { throw "Error. Access token not returned from Azure Instance Metadata Service. Please ensure that the Azure Instance Metadata Service is available." } + + Write-Verbose "[Get-AzManagedIdentityToken] Managed Identity Token Retrival Successful." + + # TypeCast the response to a ManagedIdentityToken object + $ManagedIdentity = New-ManagedIdentityToken $response + # Null the response + $null = $response + + # Return the token if the verify switch is not set + if (-not($verify)) { return $ManagedIdentity } + + Write-Verbose "[Get-AzManagedIdentityToken] Verifying the connection to the Azure DevOps API." + + # Test the Connection + if (-not(Test-AzToken $ManagedIdentity)) { throw "Error. Failed to call the Azure DevOps API." } + + Write-Verbose "[Get-AzManagedIdentityToken] Connection Verified." + + # Return the AccessToken + return ($ManagedIdentity) + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Update-AzManagedIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 similarity index 81% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Update-AzManagedIdentity.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 index 4a7ae4333..fe74db190 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Update-AzManagedIdentity.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 @@ -23,9 +23,9 @@ Function Update-AzManagedIdentity { } # Clear the existing token. - $Global:DSCAZDO_ManagedIdentityToken = $null + $Global:DSCAZDO_AuthenticationToken = $null # Refresh the Token. - $Global:DSCAZDO_ManagedIdentityToken = Get-AzManagedIdentityToken -OrganizationName $Global:DSCAZDO_OrganizationName + $Global:DSCAZDO_AuthenticationToken = Get-AzManagedIdentityToken -OrganizationName $Global:DSCAZDO_OrganizationName } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.ps1 new file mode 100644 index 000000000..342b82081 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.ps1 @@ -0,0 +1,56 @@ +Function Set-AzPersonalAccessToken { + [CmdletBinding(DefaultParameterSetName = 'PersonalAccessToken')] + param ( + # Organization Name + [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] + [Parameter(Mandatory, ParameterSetName = 'SecureStringPersonalAccessToken')] + [Alias('OrgName')] + [String] + $OrganizationName, + + # Personal Access Token + [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] + [Alias("PAT")] + [String] + $PersonalAccessToken, + + # Secure String Personal Access Token + [Parameter(Mandatory, ParameterSetName = 'SecureStringPersonalAccessToken')] + [Alias("SecureStringPAT")] + [SecureString] + $SecureStringPersonalAccessToken, + + # Verify the Connection + [Parameter(ParameterSetName = 'SecureStringPersonalAccessToken')] + [Parameter(ParameterSetName = 'PersonalAccessToken')] + [Switch] + $Verify + ) + + Write-Verbose "[Set-PersonalAccessToken] Setting the Personal Access Token for the organization $OrganizationName." + + # If a SecureString Personal Access Token is provided, parse it and set as the Token + if ($SecureStringPersonalAccessToken) { + $Token = New-PersonalAccessToken -SecureStringPersonalAccessToken $SecureStringPersonalAccessToken + } elseif ($PersonalAccessToken) { + # TypeCast the response to a PersonalAccessToken object + $Token = New-PersonalAccessToken -PersonalAccessToken $PersonalAccessToken + } else { + throw "Error. A Personal Access Token or SecureString Personal Access Token must be provided." + } + + # + # Return the token if the verify switch is not set + if (-not($verify)) { return $Token } + + Write-Verbose "[Set-PersonalAccessToken] Verifying the connection to the Azure DevOps API." + + # Test the Connection + if (-not(Test-AzToken $Token)) { throw "Error. Failed to call the Azure DevOps API." } + + Write-Verbose "[Set-PersonalAccessToken] Connection Verified." + + # Return the AccessToken + return ($Token) + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzManagedIdentityToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.ps1 similarity index 56% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzManagedIdentityToken.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.ps1 index 79c97b483..7743e6665 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzManagedIdentityToken.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.ps1 @@ -3,40 +3,35 @@ Tests the Azure Managed Identity token for accessing Azure DevOps REST API. .DESCRIPTION - The Test-AzManagedIdentityToken function is used to test the Azure Managed Identity token for accessing the Azure DevOps REST API. + The Test-AzToken function is used to test the Azure Managed Identity token for accessing the Azure DevOps REST API. It calls the Azure DevOps REST API with the provided Managed Identity token and returns true if the token is valid, otherwise returns false. -.PARAMETER ManagedIdentity +.PARAMETER Token Specifies the Managed Identity token to be tested. .EXAMPLE - $token = Get-AzManagedIdentityToken -ResourceId 'https://management.azure.com' - $isValid = Test-AzManagedIdentityToken -ManagedIdentity $token - if ($isValid) { - Write-Host "Token is valid." - } else { - Write-Host "Token is invalid." - } + #> -Function Test-AzManagedIdentityToken { +Function Test-AzToken { [CmdletBinding()] param ( [Parameter(Mandatory)] [Object] - $ManagedIdentity + $Token ) # Define the Azure DevOps REST API endpoint to get the list of projects - $AZDOProjectUrl = $AzManagedIdentityLocalizedData.Global_Url_AZDO_Project -f $GLOBAL:DSCAZDO_OrganizationName + $AZDOProjectUrl = "https://dev.azure.com/{0}/_apis/projects" -f $GLOBAL:DSCAZDO_OrganizationName $FormattedUrl = "{0}?api-version=7.2-preview.4" -f $AZDOProjectUrl $params = @{ Uri = $FormattedUrl Method = 'Get' Headers = @{ - Authorization ="Bearer {0}" -f $ManagedIdentity.Get() + Authorization ="Bearer {0}" -f $Token.Get() } + NoAuthentication = $true } # Call the Azure DevOps REST API with the Managed Identity Bearer token diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.ps1 new file mode 100644 index 000000000..01917c09e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.ps1 @@ -0,0 +1,93 @@ +Function Add-CacheItem { + <# + .SYNOPSIS + Add a cache item to the cache. + + .DESCRIPTION + Adds a cache item to the cache with a specified key, value, and type. + + .PARAMETER Key + The key of the cache item to add. + + .PARAMETER Value + The value of the cache item to add. + + .PARAMETER Type + The type of the cache item to add. Valid values are 'Project', 'Team', 'Group', 'SecurityDescriptor'. + + .EXAMPLE + Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project' + + .NOTES + This function is private and should not be used directly. + #> + [CmdletBinding()] + param ( + # The key of the cache item to add + [Parameter(Mandatory)] + [string] + $Key, + + # The value of the cache item to add + [Parameter(Mandatory)] + [object] + $Value, + + # The type of the cache item to add + [Parameter(Mandatory)] + [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] + [string] + $Type, + + # Suppress warning messages + [switch] + $SuppressWarning + ) + + Write-Verbose "[Add-CacheItem] Retrieving the current cache." + [System.Collections.Generic.List[CacheItem]]$cache = Get-CacheObject -CacheType $Type + #Get-AzDevOpsCache -CacheType $Type + + # If the cache is empty, create a new cache + if ($cache.count -eq 0) { + Write-Verbose "[Add-CacheItem] Cache is empty. Creating new cache." + $cache = [System.Collections.Generic.List[CacheItem]]::New() + } + + Write-Verbose "[Add-CacheItem] Creating new cache item with key: '$Key'." + $cacheItem = [CacheItem]::New($Key, $Value) + + Write-Verbose "[Add-CacheItem] Checking if the cache already contains the key: '$Key'." + $existingItem = $cache | Where-Object { $_.Key -eq $Key } + + if ($existingItem) { + + # If the cache already contains the key, remove the existing item + if ($SuppressWarning.IsPresent) { + Write-Verbose "[Add-CacheItem] A cache item with the key '$Key' already exists. Flushing key from the cache." + } else { + Write-Warning "[Add-CacheItem] A cache item with the key '$Key' already exists. Flushing key from the cache." + } + + # Remove the existing cache item + Remove-CacheItem -Key $Key -Type $Type + + # Refresh the cache + [System.Collections.Generic.List[CacheItem]]$cache = Get-CacheObject -CacheType $Type + + # If the cache is empty, create a new cache + if ($cache.count -eq 0) { + Write-Verbose "[Add-CacheItem] Cache is empty. Creating new cache." + $cache = [System.Collections.Generic.List[CacheItem]]::New() + } + + } + + Write-Verbose "[Add-CacheItem] Adding new cache item with key: '$Key'." + $cache.Add($cacheItem) + + # Update the memory cache + Set-Variable -Name "AzDo$Type" -Value $cache -Scope Global + + Write-Verbose "[Add-CacheItem] Cache item with key: '$Key' successfully added." +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 new file mode 100644 index 000000000..5ec4ef35e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 @@ -0,0 +1,86 @@ +<# +.SYNOPSIS +Sets the Azure DevOps API project cache. + +.DESCRIPTION +This function sets the Azure DevOps API project cache by making an API request to get the projects and adding them to the cache. + +.PARAMETER OrganizationName +Specifies the name of the organization. If not provided, the function uses a global variable as a fallback. + +.EXAMPLE +AzDoAPI_0_ProjectCache -OrganizationName "MyOrganization" +This example sets the Azure DevOps API project cache for the organization "MyOrganization". + +.INPUTS +None. + +.OUTPUTS +None. + +.NOTES +Author: [Author Name] +Date: [Date] +#> + +Function AzDoAPI_0_ProjectCache { + [CmdletBinding()] + param( + [string]$OrganizationName + ) + + # Use a verbose statement to indicate the start of the function. + Write-Verbose "Starting 'AzDoAPI_0_ProjectCache' function." + + if (-not $OrganizationName) { + # If no organization name is provided, use a global variable as fallback + Write-Verbose "No organization name provided as parameter; using global variable." + $OrganizationName = $Global:DSCAZDO_OrganizationName + } + + # Create a hashtable to store parameters for API call + $params = @{ + Organization = $OrganizationName + } + + try { + # Inform about the API call being made with the parameters + Write-Verbose "Calling 'List-DevOpsProjects' with parameters: $($params | Out-String)" + + # Perform an Azure DevOps API request to get the projects + $projects = List-DevOpsProjects @params + $projectsArr = [System.Collections.ArrayList]::new() + + # Iterate through each project and get the security descriptors + foreach ($project in $projects) { + # Add the Project + $securityDescriptor = Get-DevOpsSecurityDescriptor -ProjectId $project.Id -Organization $OrganizationName + # Add the security descriptor to the project object + $projectsArr.Add(($project | Select-Object *, @{Name='ProjectDescriptor'; Expression={$securityDescriptor}})) + } + + # Log the total number of projects returned by the API call + Write-Verbose "'List-DevOpsProjects' returned a total of $($projects.Count) projects." + + # Iterate through each project in the response and add them to the cache + foreach ($project in $projectsArr) { + # Log the addition of each project to the cache + Write-Verbose "Adding Project '$($project.Name)' to the cache." + # Add the project to the cache with its name as the key + Add-CacheItem -Key $project.Name -Value $project -Type 'LiveProjects' + } + + # Export the cache to a file + Export-CacheObject -CacheType 'LiveProjects' -Content $AzDoLiveProjects + + # Indicate completion of adding projects to cache + Write-Verbose "Completed adding projects to cache." + + } catch { + # Handle any exceptions that occur during the try block + Write-Error "An error occurred: $_" + } + + # Signal the end of the function execution + Write-Verbose "Function 'AzDoAPI_0_ProjectCache' completed." +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.ps1 new file mode 100644 index 000000000..13462fbf8 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.ps1 @@ -0,0 +1,62 @@ +<# +.SYNOPSIS +Sets the Azure DevOps API group cache. + +.DESCRIPTION +This function sets the cache for Azure DevOps API groups. It retrieves the groups using the List-AzDevOpsGroup function and adds them to the cache. + +.PARAMETER OrganizationName +The name of the organization. If not provided as a parameter, it uses the global variable $Global:DSCAZDO_OrganizationName. + +.EXAMPLE +AzDoAPI_1_GroupCache -OrganizationName "MyOrganization" +#> + +Function AzDoAPI_1_GroupCache { + [CmdletBinding()] + param( + [string]$OrganizationName + ) + + # Use a verbose statement to indicate the start of the function. + Write-Verbose "Starting 'Set-GroupCache' function." + + if (-not $OrganizationName) { + Write-Verbose "No organization name provided as parameter; using global variable." + $OrganizationName = $Global:DSCAZDO_OrganizationName + } + + $params = @{ + Organization = $OrganizationName + } + + try { + + Write-Verbose "Calling 'List-DevOpsGroups' with parameters: $($params | Out-String)" + # Perform an Azure DevOps API request to get the groups + + $groups = List-DevOpsGroups @params + + Write-Verbose "'List-DevOpsGroups' returned a total of $($groups.Count) groups." + + # Iterate through each of the responses and add them to the cache + foreach ($group in $groups) { + Write-Verbose "Adding group '$($group.PrincipalName)' to cache." + # Add the group to the cache + Add-CacheItem -Key $group.PrincipalName -Value $group -Type 'LiveGroups' + } + + # Export the cache to a file + Export-CacheObject -CacheType 'LiveGroups' -Content $AzDoLiveGroups + + Write-Verbose "Completed adding groups to cache." + + } catch { + + Write-Error "An error occurred: $_" + + } + + Write-Verbose "Function 'AzDoAPI_1_GroupCache' completed." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.ps1 new file mode 100644 index 000000000..dd729793b --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.ps1 @@ -0,0 +1,50 @@ +Function AzDoAPI_2_UserCache { + + [CmdletBinding()] + param( + [string]$OrganizationName + ) + + # Use a verbose statement to indicate the start of the function. + Write-Verbose "[AzDoAPI_2_UserCache] Starting 'AzDoAPI_2_UserCache' function." + + if (-not $OrganizationName) + { + Write-Verbose "[AzDoAPI_2_UserCache] No organization name provided as parameter; using global variable." + $OrganizationName = $Global:DSCAZDO_OrganizationName + } + + $params = @{ + Organization = $OrganizationName + } + + try + { + Write-Verbose "[AzDoAPI_2_UserCache] Calling 'AzDoAPI_2_UserCache' with parameters: $($params | Out-String)" + # Perform an Azure DevOps API request to get the groups + + $users = List-UserCache @params + + Write-Verbose "[AzDoAPI_2_UserCache] 'AzDoAPI_2_UserCache' returned a total of $($users.Count) users." + + # Iterate through each of the responses and add them to the cache + foreach ($user in $users) { + Write-Verbose "[AzDoAPI_2_UserCache] Adding user '$($user.PrincipalName)' to cache." + # Add the group to the cache + Add-CacheItem -Key $user.PrincipalName -Value $user -Type 'LiveUsers' + } + + # Export the cache to a file + Export-CacheObject -CacheType 'LiveUsers' -Content $AzDoLiveUsers + + Write-Verbose "[AzDoAPI_2_UserCache] Completed adding users to cache." + + } + catch + { + Write-Error "[AzDoAPI_2_UserCache] An error occurred: $_" + } + + Write-Verbose "[AzDoAPI_2_UserCache] Function 'Set-AzDoAPICacheGroup' completed." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.ps1 new file mode 100644 index 000000000..77dc34e91 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.ps1 @@ -0,0 +1,78 @@ +function AzDoAPI_3_GroupMemberCache +{ + [CmdletBinding()] + param( + [string]$OrganizationName + ) + + # Use a verbose statement to indicate the start of the function. + Write-Verbose "Starting 'Set-GroupCache' function." + + if (-not $OrganizationName) + { + Write-Verbose "No organization name provided as parameter; using global variable." + $OrganizationName = $Global:DSCAZDO_OrganizationName + } + + $params = @{ + Organization = $OrganizationName + } + + # Enumerate the live group cache + $AzDoLiveGroups = Get-CacheObject -CacheType 'LiveGroups' + # Enumerate the live users cache + $AzDoLiveUsers = Get-CacheObject -CacheType 'LiveUsers' + + try + { + ForEach ($AzDoLiveGroup in $AzDoLiveGroups) + { + # Update the Group ID in the parameters + $GroupDescriptor = $AzDoLiveGroup.Value.descriptor + + Write-Verbose "Calling 'AzDoAPI_2_GroupMemberCache' with parameters: $($params | Out-String)" + + # Perform an Azure DevOps API request to get the groups + $groupMembers = List-DevOpsGroupMembers -Organization $OrganizationName -GroupDescriptor $GroupDescriptor + + # If there are no members, skip to the next group + if ($null -eq $groupMembers.memberDescriptor) + { + Write-Verbose "No members found for group '$($AzDoLiveGroup.Key)'; skipping." + continue + } + + # Members + $members = [System.Collections.Generic.List[object]]::new() + + # Iterate through each of the users and groups and add them to the cache + $azdoUserMembers = $AzDoLiveUsers.value | Where-Object { $_.descriptor -in $groupMembers.memberDescriptor } + $azdoGroupMembers = $AzDoLiveGroups.value | Where-Object { $_.descriptor -in $groupMembers.memberDescriptor } + + $azdoUserMembers | Select-Object *,@{Name="Type";Exp={"user"}} | Where-Object { $_.descriptor -in $groupMembers.memberDescriptor } | ForEach-Object { + $null = $members.Add($_) + } + + $azdoGroupMembers | Select-Object *,@{Name="Type";Exp={"group"}} | Where-Object { $_.descriptor -in $groupMembers.memberDescriptor } | ForEach-Object { + $null = $members.Add($_) + } + + # Add the group to the cache + Add-CacheItem -Key $AzDoLiveGroup.value.PrincipalName -Value $members -Type 'LiveGroupMembers' + + } + + # Export the cache to a file + Export-CacheObject -CacheType 'LiveGroupMembers' -Content $AzdoLiveGroupMembers + + Write-Verbose "Completed adding groups to cache." + + } + catch + { + Write-Error "An error occurred: $_" + } + + Write-Verbose "Function 'Set-AzDoAPICacheGroup' completed." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.ps1 new file mode 100644 index 000000000..ec551da51 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.ps1 @@ -0,0 +1,57 @@ +function AzDoAPI_4_GitRepositoryCache +{ + [CmdletBinding()] + param( + [string]$OrganizationName + ) + + # Use a verbose statement to indicate the start of the function. + Write-Verbose "[AzDoAPI_4_GitRepositoryCache] Started." + + if (-not $OrganizationName) + { + Write-Verbose "[AzDoAPI_4_GitRepositoryCache] No organization name provided as parameter; using global variable." + $OrganizationName = $Global:DSCAZDO_OrganizationName + } + + # Enumerate the live projects cache + $AzDoLiveProjects = Get-CacheObject -CacheType 'LiveProjects' + + try + { + foreach ($AzDoLiveProject in $AzDoLiveProjects) + { + # Log the project being processed + Write-Verbose "[AzDoAPI_4_GitRepositoryCache] Processing Project '$($AzDoLiveProject.Value.Name)'." + $ProjectName = $AzDoLiveProject.Value.Name + + # Call the API to get the repositories for the project + $enumeratedRepositories = List-DevOpsGitRepository -ProjectName $ProjectName -OrganizationName $OrganizationName + + # Log the the git repositories returned by the API call + Write-Verbose "[AzDoAPI_4_GitRepositoryCache] 'List-DevOpsGitRepository' returned a total of $($enumeratedRepositories.Count) repositories." + + # Iterate through each repository in the response and add them to the cache + foreach ($repository in $enumeratedRepositories) + { + # Log the addition of each repository to the cache + Write-Verbose "[AzDoAPI_4_GitRepositoryCache] Adding Repository '$($repository.Name)' to the cache." + # Add the repository to the cache with its name as the key + Add-CacheItem -Key "$ProjectName\$($repository.Name)" -Value $repository -Type 'LiveRepositories' + } + + } + + # Export the cache to a file + Export-CacheObject -CacheType 'LiveRepositories' -Content $AzDoLiveRepositories + Write-Verbose "[AzDoAPI_4_GitRepositoryCache] Completed adding groups to cache." + + } + catch + { + Write-Error "[AzDoAPI_4_GitRepositoryCache] An error occurred: $_" + } + + Write-Verbose "[AzDoAPI_4_GitRepositoryCache] completed." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.ps1 new file mode 100644 index 000000000..3f459967e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.ps1 @@ -0,0 +1,40 @@ +function AzDoAPI_5_PermissionsCache +{ + [CmdletBinding()] + param( + [string]$OrganizationName + ) + + # + # Use a verbose statement to indicate the start of the function. + + Write-Verbose "[AzDoAPI_5_PermissionsCache] Started." + + if (-not $OrganizationName) + { + Write-Verbose "[AzDoAPI_5_PermissionsCache] No organization name provided as parameter; using global variable." + $OrganizationName = $Global:DSCAZDO_OrganizationName + } + + # + # List the security namespaces + + $securityNamespaces = List-DevOpsSecurityNamespaces -OrganizationName $OrganizationName + + # + # Iterate through each security namespace and export the permissions to the cache + + foreach ($securityNamespace in $securityNamespaces) + { + $securityNamespaceName = $securityNamespace.name + $value = $securityNamespace | Select-Object namespaceId, name, displayName, writePermission, readPermision, dataspaceCategory, actions + + # Add the project to the cache with its name as the key + Add-CacheItem -Key $securityNamespaceName -Value $value -Type 'SecurityNamespaces' + + } + + # Export the cache to a file + Export-CacheObject -CacheType 'SecurityNameSpaces' -Content $AzDoSecurityNameSpaces -Depth 5 + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.ps1 new file mode 100644 index 000000000..1725c2dd6 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.ps1 @@ -0,0 +1,50 @@ +function AzDoAPI_6_ServicePrinciple +{ + [CmdletBinding()] + param( + [string]$OrganizationName + ) + + # Use a verbose statement to indicate the start of the function. + Write-Verbose "Starting [AzDoAPI_6_ServicePrinciple] function." + + if (-not $OrganizationName) + { + Write-Verbose "No organization name provided as parameter; using global variable." + $OrganizationName = $Global:DSCAZDO_OrganizationName + } + + $params = @{ + Organization = $OrganizationName + } + + try + { + Write-Verbose "[AzDoAPI_6_ServicePrinciple] with parameters: $($params | Out-String)" + # Perform an Azure DevOps API request to get the groups + + $serviceprincipals = List-DevOpsServicePrinciples @params + + Write-Verbose "[AzDoAPI_6_ServicePrinciple] returned a total of $($serviceprincipals.Count) serviceprincipals." + + # Iterate through each of the responses and add them to the cache + foreach ($serviceprincipal in $serviceprincipals) + { + Write-Verbose "[AzDoAPI_6_ServicePrinciple] Adding serviceprincipal '$($serviceprincipal.displayName)' to cache." + # Add the group to the cache + Add-CacheItem -Key $serviceprincipal.DisplayName -Value $serviceprincipal -Type 'LiveServicePrinciples' + } + + # Export the cache to a file + Export-CacheObject -CacheType 'LiveServicePrinciples' -Content $AzDoLiveServicePrinciples + + Write-Verbose "[AzDoAPI_6_ServicePrinciple] Completed adding serviceprincipals to cache." + + } + catch + { + Write-Error "An error occurred: $_" + } + + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.ps1 new file mode 100644 index 000000000..634aebd2f --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.ps1 @@ -0,0 +1,131 @@ +function AzDoAPI_7_IdentitySubjectDescriptors +{ + [CmdletBinding()] + param( + [string]$OrganizationName + ) + + # + # Use a verbose statement to indicate the start of the function. + + Write-Verbose "[AzDoAPI_5_PermissionsCache] Started." + + if (-not $OrganizationName) + { + Write-Verbose "[AzDoAPI_5_PermissionsCache] No organization name provided as parameter; using global variable." + $OrganizationName = $Global:DSCAZDO_OrganizationName + } + + # Enumerate the live group cache + $AzDoLiveGroups = Get-CacheObject -CacheType 'LiveGroups' + # Enumerate the live users cache + $AzDoLiveUsers = Get-CacheObject -CacheType 'LiveUsers' + # Enumerate the live service principals cache + $AzDoLiveServicePrinciples = Get-CacheObject -CacheType 'LiveServicePrinciples' + + # + # Iterate through each of the groups and query the Identity and add to the cache + + $params = @{ + OrganizationName = $OrganizationName + } + + # Iterate through each of the groups and query the Identity and add to the cache + ForEach ($AzDoLiveGroup in $AzDoLiveGroups) { + + $identity = Get-DevOpsDescriptorIdentity @params -SubjectDescriptor $AzDoLiveGroup.value.descriptor + + $ACLIdentity = [PSCustomObject]@{ + id = $identity.id + descriptor = $identity.descriptor + subjectDescriptor = $identity.subjectDescriptor + providerDisplayName = $identity.providerDisplayName + isActive = $identity.isActive + isContainer = $identity.isContainer + } + + $AzDoLiveGroup.value | Add-Member -MemberType NoteProperty -Name 'ACLIdentity' -Value $ACLIdentity + + $cacheParams = @{ + Key = $AzDoLiveGroup.Key + Value = $AzDoLiveGroup + Type = 'LiveGroups' + SuppressWarning = $true + } + + # Add to the cache + Add-CacheItem @cacheParams + + } + + # Update the cache + Export-CacheObject -CacheType 'LiveGroups' -Content $AzDoLiveGroups + + # + # Iterate through each of the users and query the Identity and add to the cache + + ForEach ($AzDoLiveUser in $AzDoLiveUsers) { + + $identity = Get-DevOpsDescriptorIdentity @params -SubjectDescriptor $AzDoLiveUser.value.descriptor + + $ACLIdentity = [PSCustomObject]@{ + id = $identity.id + descriptor = $identity.descriptor + subjectDescriptor = $identity.subjectDescriptor + providerDisplayName = $identity.providerDisplayName + isActive = $identity.isActive + isContainer = $identity.isContainer + } + + $AzDoLiveUser.value | Add-Member -MemberType NoteProperty -Name 'ACLIdentity' -Value $ACLIdentity + + $cacheParams = @{ + Key = $AzDoLiveUser.Key + Value = $AzDoLiveUser + Type = 'LiveUsers' + SuppressWarning = $true + } + + # Add to the cache + Add-CacheItem @cacheParams + + } + + # Update the cache + Export-CacheObject -CacheType 'LiveUsers' -Content $AzDoLiveUsers + + # + # Iterate through each of the service principals and query the Identity and add to the cache + + ForEach ($AzDoLiveServicePrinciple in $AzDoLiveServicePrinciples) { + + $identity = Get-DevOpsDescriptorIdentity @params -SubjectDescriptor $AzDoLiveServicePrinciple.value.descriptor + + $ACLIdentity = [PSCustomObject]@{ + id = $identity.id + descriptor = $identity.descriptor + subjectDescriptor = $identity.subjectDescriptor + providerDisplayName = $identity.providerDisplayName + isActive = $identity.isActive + isContainer = $identity.isContainer + } + + $AzDoLiveServicePrinciple.value | Add-Member -MemberType NoteProperty -Name 'ACLIdentity' -Value $ACLIdentity + + $cacheParams = @{ + Key = $AzDoLiveServicePrinciple.Key + Value = $AzDoLiveServicePrinciple + Type = 'LiveServicePrinciples' + SuppressWarning = $true + } + + # Add to the cache + Add-CacheItem @cacheParams + + } + + # Update the cache + Export-CacheObject -CacheType 'LiveServicePrinciples' -Content $AzDoLiveServicePrinciples + + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.ps1 new file mode 100644 index 000000000..8ea37b5bf --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.ps1 @@ -0,0 +1,50 @@ +function AzDoAPI_8_ProjectProcessTemplates +{ + [CmdletBinding()] + param( + [string]$OrganizationName + ) + + # Use a verbose statement to indicate the start of the function. + Write-Verbose "[AzDoAPI_8_ProjectProcessTemplates] Starting 'AzDoAPI_8_ProjectProcessTemplates' function." + + if (-not $OrganizationName) + { + Write-Verbose "[AzDoAPI_8_ProjectProcessTemplates] No organization name provided as parameter; using global variable." + $OrganizationName = $Global:DSCAZDO_OrganizationName + } + + # Construct the parameters for the API request + $params = @{ + Organization = $OrganizationName + } + + try + { + Write-Verbose "[AzDoAPI_8_ProjectProcessTemplates] Calling 'List-DevOpsProcess' with parameters: $($params | Out-String)" + # Perform an Azure DevOps API request to get the groups + + $processes = List-DevOpsProcess @params + + # Iterate through each of the responses and add them to the cache + foreach ($process in $processes) + { + Write-Verbose "[AzDoAPI_8_ProjectProcessTemplates] Adding process '$($process.name)' to cache." + # Add the group to the cache + Add-CacheItem -Key $process.name -Value $process -Type 'LiveProcesses' + } + + # Export the cache to a file + Export-CacheObject -CacheType 'LiveProcesses' -Content $AzDoLiveProcesses + + Write-Verbose "[AzDoAPI_8_ProjectProcessTemplates] Completed adding processes to cache." + + } + catch + { + Write-Error "[AzDoAPI_8_ProjectProcessTemplates] An error occurred: $_" + } + + Write-Verbose "[AzDoAPI_8_ProjectProcessTemplates] Function completed." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.ps1 new file mode 100644 index 000000000..ec6d933ec --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.ps1 @@ -0,0 +1,79 @@ +<# +.SYNOPSIS +Exports content to a cache file and saves it to a global variable. + +.DESCRIPTION +The Export-CacheObject function exports content to a cache file and saves it to a global variable. It is used in the AzureDevOpsDsc module for caching Azure DevOps API responses. + +.PARAMETER CacheType +Specifies the type of cache. Valid values are 'Project', 'Team', 'Group', and 'SecurityDescriptor'. + +.PARAMETER Content +Specifies the content to be exported to the cache file. + +.PARAMETER Depth +Specifies the depth of the object to be exported. Default value is 3. + +.PARAMETER CacheRootPath +Specifies the root path where the cache directory will be created. Default value is the script root path. + +.EXAMPLE +Export-CacheObject -CacheType 'Project' -Content $projectData +Exports the $projectData content to a cache file and saves it to the global variable 'AzDoProject'. + +.INPUTS +None. + +.OUTPUTS +None. + +.NOTES +This function is part of the AzureDevOpsDsc module and is used for caching Azure DevOps API responses. +#> +Function Export-CacheObject { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] + [string]$CacheType, + + [Parameter()] + [AllowEmptyCollection()] + [Object[]]$Content, + + [Parameter()] + [int]$Depth = 3 + ) + + # Write initial verbose message + Write-Verbose "[Export-ObjectCache] Starting export process for cache type: $CacheType" + + # Use the Enviroment Variables to set the Cache Directory Path + if ($ENV:AZDODSC_CACHE_DIRECTORY) { + $CacheDirectoryPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "Cache" + } else { + Throw "The environment variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the variable to the path of the cache directory." + } + + try { + + $cacheFilePath = Join-Path -Path $CacheDirectoryPath -ChildPath "$CacheType.clixml" + + # Create cache directory if it does not exist + if (-not (Test-Path -Path $CacheDirectoryPath)) { + Write-Verbose "[Export-ObjectCache] Creating cache directory at path: $CacheDirectoryPath" + New-Item -Path $CacheDirectoryPath -ItemType Directory | Out-Null + } + + # Save content to cache file + Write-Verbose "[Export-ObjectCache] Saving content to cache file: $cacheFilePath" + $Content | Export-Clixml -Depth $Depth -LiteralPath $cacheFilePath + + # Confirm completion of export process + Write-Verbose "[Export-ObjectCache] Export process completed successfully for cache type: $CacheType" + + } catch { + Write-Error "[Export-ObjectCache] Failed to create cache for Azure DevOps API: $_" + throw + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 new file mode 100644 index 000000000..efd791b24 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 @@ -0,0 +1,55 @@ +<# +.SYNOPSIS +Searches for a CacheItem in a given list of cache items based on a filter. + +.DESCRIPTION +The Find-CacheItem function searches for a CacheItem in a given list of cache items based on a filter. It returns the matching CacheItem. + +.PARAMETER CacheList +The list of cache items to search in. This parameter is mandatory and accepts pipeline input. + +.PARAMETER Filter +The filter to apply when searching for the CacheItem. This parameter is mandatory and accepts a script block. + +.OUTPUTS +System.Management.Automation.PSObject +The matching CacheItem. + +.EXAMPLE +$cacheItems = Get-CacheItems +$filteredCacheItem = $cacheItems | Find-CacheItem -Filter { $_.Name -eq 'MyCacheItem' } +$filteredCacheItem +# Returns the CacheItem with the name 'MyCacheItem' from the list of cache items. + +.NOTES +Author: Your Name +Date: Today's Date +#> + +Function Find-CacheItem +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject])] + param + ( + [Parameter(Mandatory, ValueFromPipeline)] + [Alias('Cache')] + [Object[]]$CacheList, + + [Parameter(Mandatory)] + [ScriptBlock]$Filter + ) + + # + # Logging + Write-Verbose "[Find-CacheItem] Searching for the CacheItem with filter '$Filter'." + + # + # Get the CacheItem + $cacheItem = $null + $cacheItem = $CacheList | Where-Object -FilterScript $Filter + + # + # Return the CacheItem + return $cacheItem +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.ps1 new file mode 100644 index 000000000..1d7c3f6a1 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.ps1 @@ -0,0 +1,57 @@ + + +function Get-CacheItem +{ + <# + .SYNOPSIS + Get a cache item from the cache. + + .DESCRIPTION + Get a cache item from the cache. + + .PARAMETER Key + The key of the cache item to get. + + .EXAMPLE + Get-CacheItem -Key 'MyKey' + + .NOTES + This function is private and should not be used directly. + #> + [CmdletBinding()] + [OutputType([CacheItem])] + param ( + [Parameter(Mandatory)] + [string] + $Key, + + [Parameter(Mandatory)] + [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] + [string] + $Type, + + [Parameter()] + [scriptblock] + $Filter + ) + + try + { + [System.Collections.Generic.List[CacheItem]]$cache = Get-CacheObject -CacheType $Type + $cacheItem = $cache.Where({$_.Key -eq $Key}) + } catch + { + $cacheItem = $null + Write-Verbose $_ + } + + if ($null -eq $cacheItem) { return $null } + + if ($Filter -ne $null) + { + $cacheItem = $cacheItem | Where-Object $Filter + } + + return $cacheItem.Value + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 new file mode 100644 index 000000000..d32dbc286 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 @@ -0,0 +1,74 @@ +<# +.SYNOPSIS +Retrieves a cache object of a specified type. + +.DESCRIPTION +The Get-CacheObject function is used to retrieve a cache object of a specified type. It first checks if the cache object is available in memory, and if not, it attempts to import it. The function supports different cache types such as Project, Team, Group, and SecurityDescriptor. + +.PARAMETER CacheType +Specifies the type of cache object to retrieve. Valid values are 'Project', 'Team', 'Group', and 'SecurityDescriptor'. + +.PARAMETER CacheRootPath +Specifies the root path of the cache. By default, it uses the path of the current script. + +.EXAMPLE +Get-CacheObject -CacheType Project +Retrieves the cache object of type 'Project'. + +.EXAMPLE + +Retrieves the cache object of type 'Team' from the specified root path. + +.INPUTS +None. + +.OUTPUTS +The cache object of the specified type. + +.NOTES +This function is part of the AzureDevOpsDsc module. + +.LINK +https://github.com/Azure/AzureDevOpsDsc + +#> +function Get-CacheObject { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] + [string]$CacheType + ) + + # Write initial verbose message + Write-Verbose "[Get-ObjectCache] Attempting to retrieve cache object for type: $CacheType" + + # Use the Enviroment Variables to set the Cache Directory Path + if ($ENV:AZDODSC_CACHE_DIRECTORY) { + $CacheDirectoryPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "Cache" + } else { + Throw "The environment variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the variable to the path of the cache directory." + } + + try { + # Attempt to get the variable from the global scope + $var = Get-Variable -Name "AzDo$CacheType" -Scope Global -ErrorAction SilentlyContinue + + if ($var) { + Write-Verbose "[Get-ObjectCache] Cache object found in memory for type: $CacheType" + # If the variable is found, return the content of the cache. Dont use $var here, since it will a different object type. + $var = Get-Variable -Name "AzDo$CacheType" -ValueOnly -Scope Global + } else { + Write-Verbose "[Get-ObjectCache] Cache object not found in memory, attempting to import for type: $CacheType" + $var = Import-CacheObject -CacheType $CacheType + } + + # Return the content of the cache after importing it + Write-Verbose "[Get-ObjectCache] Returning imported cache object for type: $CacheDirectoryPath" + return $var + + } catch { + Write-Error "[Get-ObjectCache] Failed to get cache for Azure DevOps API: $_" + throw + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.ps1 new file mode 100644 index 000000000..2edc9b096 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.ps1 @@ -0,0 +1,92 @@ +<# +.SYNOPSIS +Imports a cache object for Azure DevOps API. + +.DESCRIPTION +The Import-CacheObject function is used to import a cache object for Azure DevOps API. It checks if the cache file exists and imports its content if found. The cache object is then stored in a global variable. + +.PARAMETER CacheType +Specifies the type of cache object to import. Valid values are 'Project', 'Team', 'Group', and 'SecurityDescriptor'. + +.PARAMETER CacheRootPath +Specifies the root path where the cache directory is located. By default, it uses the current script's root path. + +.EXAMPLE + + +This example imports the cache object for the 'Project' type from the cache directory located at "C:\Cache". + +.INPUTS +None. + +.OUTPUTS +None. + +.NOTES +#> + +function Import-CacheObject +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] + [string]$CacheType + + ) + + # Write initial verbose message + Write-Verbose "[Import-CacheObject] Starting to import cache object for type: $CacheType" + + # Use the Enviroment Variables to set the Cache Directory Path + if ($ENV:AZDODSC_CACHE_DIRECTORY) { + $CacheDirectoryPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "Cache" + } else { + Throw "The environment variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the variable to the path of the cache directory." + } + + Write-Verbose "[Import-CacheObject] Cache root path: $CacheDirectoryPath" + + try + { + # Determine cache file path + $cacheFile = Join-Path -Path $CacheDirectoryPath -ChildPath "$CacheType.clixml" + + # Check if cache file exists + if (-not (Test-Path -Path $cacheFile)) + { + Write-Warning "[Import-CacheObject] Cache file not found at path: $cacheFile" + } + + Write-Verbose "[Import-CacheObject] Importing content from cache file at path: $cacheFile" + + $Content = Import-Clixml -Path $cacheFile + + #Set-Variable -Name "AzDo$CacheType" -Value $Content -Scope Global -Force + Write-Verbose "[Import-CacheObject] Successfully imported cache object for type: $cacheFile" + + # Convert the imported cache object to a list of CacheItem objects + $newCache = [System.Collections.Generic.List[CacheItem]]::New() + + # If the content is null, skip! + if ($null -ne $Content) + { + $Content | ForEach-Object { + # If the key is empty, skip the item + if ([string]::IsNullOrEmpty($_.Key)) { return } + + # Create a new CacheItem object and add it to the list + $newCache.Add([CacheItem]::New($_.Key, $_.Value)) + } + } + + # Update the new cache object + Set-Variable -Name "AzDo$CacheType" -Value $newCache -Scope Global -Force + Write-Verbose "[Import-CacheObject] Cache object imported successfully for '$CacheType'." + + } catch + { + Write-Error "[Import-CacheObject] Failed to import cache for Azure DevOps API: $_" + throw $_ + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.ps1 new file mode 100644 index 000000000..c437c470b --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.ps1 @@ -0,0 +1,86 @@ +<# +.SYNOPSIS + Initializes the cache object for Azure DevOps API. + +.DESCRIPTION + This function is used to initialize the cache object for Azure DevOps API. It checks if the cache file exists and imports the cache object if it does. If the cache file does not exist, it creates a new cache object. + +.PARAMETER CacheType + Specifies the type of cache to initialize. Valid values are 'Project', 'Team', 'Group', and 'SecurityDescriptor'. + +.EXAMPLE + Initialize-CacheObject -CacheType Project + Initializes the cache object for the 'Project' cache type. + +.NOTES +#> +Function Initialize-CacheObject { + [CmdletBinding()] + param( + # Specifies the type of cache to initialize. Valid values are 'Project', 'Team', 'Group', and 'SecurityDescriptor'. + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] + [string]$CacheType, + # Used to bypass the file deletion check for live caches. Needed for DSC Resources to import the cache. + [Parameter()] + [Switch]$BypassFileCheck + ) + + try { + + # Use the Enviroment Variables to set the Cache Directory Path + if ($ENV:AZDODSC_CACHE_DIRECTORY) { + $CacheDirectoryPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "Cache" + } else { + Throw "The environment variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the variable to the path of the cache directory." + } + + $cacheFilePath = Join-Path -Path $CacheDirectoryPath -ChildPath "$CacheType.clixml" + Write-Verbose "[Initialize-CacheObject] Cache file path: $cacheFilePath" + + # If the cache group is LiveGroups or LiveProjects, set the cache file path to the temporary directory + if (-not($BypassFileCheck.IsPresent) -and ($CacheType -match '^Live')) { + + # Flush the cache if it is a live cache + if (Test-Path -LiteralPath $cacheFilePath -ErrorAction SilentlyContinue) { + Write-Verbose "[Initialize-CacheObject] Cache file found. Removing cache file for '$CacheType'." + Remove-Item -LiteralPath $cacheFilePath -Force + } + + } else { + # Test if the Cache File exists. If it exists, import the cache object + Write-Verbose "[Initialize-CacheObject] Cache file path: $cacheFilePath" + } + + # Test if the Cache File exists. If it exists, import the cache object + if (Test-Path -Path $cacheFilePath) { + + # If the cache file exists, import the cache object + Write-Verbose "[Initialize-CacheObject] Cache file found. Importing cache object for '$CacheType'." + Import-CacheObject -CacheType $CacheType + + } else { + + # If the cache file does not exist, create a new cache object + Write-Verbose "[Initialize-CacheObject] Cache file not found. Creating new cache object for '$CacheType'." + + # Create the cache directory if it does not exist + if (-not (Test-Path -Path $CacheDirectoryPath)) { + Write-Verbose "[Initialize-CacheObject] Cache directory not found. Creating cache directory." + New-Item -Path $CacheDirectoryPath -ItemType Directory | Out-Null + } + + # Create the content + $content = [System.Collections.Generic.List[CacheItem]]::New() + + # Create a new cache object + Set-CacheObject -CacheType $CacheType -Content $content + + } + + } catch { + Write-Verbose "An error occurred: $_" + throw "[Initialize-CacheObject] Failed to import cache for Azure DevOps API: $_" + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.ps1 new file mode 100644 index 000000000..0919e036d --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.ps1 @@ -0,0 +1,21 @@ +Function Refresh-AzDoCache { + param( + [Parameter(Mandatory)] + [string]$OrganizationName + ) + + # Clear the live cache + Get-Variable Azdo* -Scope Global | Remove-Variable -Scope Global + + # Iterate through Each of the Caching Commands and initalize the Cache. + Get-Command "AzDoAPI_*" | Where-Object Source -eq 'AzureDevOpsDsc.Common' | ForEach-Object { + . $_.Name -OrganizationName $OrganizationName + } + + # ReImport the Cache + Get-AzDoCacheObjects | ForEach-Object { + Import-CacheObject -CacheType $_ + } + + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.ps1 new file mode 100644 index 000000000..97c7f541e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.ps1 @@ -0,0 +1,51 @@ +Function Refresh-CacheIdentity { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [Object]$Identity, + [Parameter(Mandatory)] + [String]$Key, + [Parameter(Mandatory)] + [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] + [String]$CacheType + ) + + # + # Perform a lookup to get the ACL Descriptor + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SubjectDescriptor = $Identity.descriptor + } + + $descriptorIdentity = Get-DevOpsDescriptorIdentity @params + + # Add the ACLIdentity to the object + $ACLIdentity = [PSCustomObject]@{ + id = $descriptorIdentity.id + descriptor = $descriptorIdentity.descriptor + subjectDescriptor = $descriptorIdentity.subjectDescriptor + providerDisplayName = $descriptorIdentity.providerDisplayName + isActive = $descriptorIdentity.isActive + isContainer = $descriptorIdentity.isContainer + } + + $Identity | Add-Member -MemberType NoteProperty -Name 'ACLIdentity' -Value $ACLIdentity -Force + + # + # Add the object to the cache + $cacheParams = @{ + Key = $Key + Value = $Identity + Type = $CacheType + SuppressWarning = $true + } + + # Add to the cache + Add-CacheItem @cacheParams + + # Update the cache object + $currentCache = Get-CacheObject -CacheType $CacheType + Set-CacheObject -Content $currentCache -CacheType $CacheType + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.ps1 new file mode 100644 index 000000000..5c8d01fb4 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.ps1 @@ -0,0 +1,21 @@ +# Unloads and reloads the cache object of the specified type. +Function Refresh-CacheObject { + param ( + [Parameter(Mandatory)] + [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] + [string] + $CacheType + ) + + Write-Verbose "[Refresh-CacheObject] Unloading the cache object of type '$CacheType'." + + # Unload the current cache object + Remove-Variable -Name "AzDo$CacheType" -Scope Global -ErrorAction SilentlyContinue + + Write-Verbose "[Refresh-CacheObject] Reloading the cache object of type '$CacheType'." + + # Reload the cache object + Import-CacheObject -CacheType $CacheType + +} + diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.ps1 new file mode 100644 index 000000000..999588c50 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.ps1 @@ -0,0 +1,54 @@ +<# +.SYNOPSIS +Removes an item from the Azure DevOps cache. + +.DESCRIPTION +The Remove-CacheItem function is used to remove an item from the Azure DevOps cache. It takes a key and a type as parameters. The key is the identifier of the item to be removed, and the type specifies the type of cache (Project, Team, Group, or SecurityDescriptor) from which the item should be removed. + +.PARAMETER Key +The key of the item to be removed from the cache. + +.PARAMETER Type +The type of cache from which the item should be removed. Valid values are 'Project', 'Team', 'Group', and 'SecurityDescriptor'. + +.EXAMPLE +Remove-CacheItem -Key "myKey" -Type "Project" +Removes the item with the key "myKey" from the Project cache. + +.EXAMPLE +Remove-CacheItem -Key "anotherKey" -Type "Group" +Removes the item with the key "anotherKey" from the Group cache. +#> + +Function Remove-CacheItem { + param ( + [Parameter(Mandatory)] + [string] + $Key, + + [Parameter(Mandatory)] + [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] + [string] + $Type + ) + + Write-Verbose "[Remove-CacheItem] Retrieving the current cache." + #$cache = Get-AzDevOpsCache -CacheType $Type + [System.Collections.Generic.List[CacheItem]]$cache = Get-CacheObject -CacheType $Type + + Write-Verbose "[Remove-CacheItem] Removing the cache item with the key: '$Key'." + + # If the cache has a length of 1, and the key matches, remove the cache + if ($cache.Count -eq 1 -and $cache[0].Key -eq $Key) { + Write-Verbose "[Remove-CacheItem] Cache has a length of 1 and the key matches. Removing the cache." + Set-Variable -Name "AzDo$Type" -Value ([System.Collections.Generic.List[CacheItem]]::New()) -Scope Global + return + } + + # Remove the item from the cache + 0 .. $cache.Count | Where-Object { $cache[$_].Key -eq $Key } | ForEach-Object { $cache.RemoveAt($_) } + + # Update the memory cache + Set-Variable -Name "AzDo$Type" -Value $cache -Scope Global + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 new file mode 100644 index 000000000..1dec5370d --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 @@ -0,0 +1,74 @@ +<# +.SYNOPSIS +Sets a cache object for Azure DevOps API. + +.DESCRIPTION +The Set-CacheObject function is used to set a cache object for Azure DevOps API. It creates a cache directory if it does not exist, saves the content to a cache file, and sets the content to a global variable. + +.PARAMETER CacheType +Specifies the type of cache object. Valid values are 'Project', 'Team', 'Group', and 'SecurityDescriptor'. + +.PARAMETER Content +Specifies the content to be cached. This should be an array of objects. + +.PARAMETER Depth +Specifies the depth of the object to be serialized. Default value is 3. + +.PARAMETER CacheRootPath +Specifies the root path for the cache directory. Default value is the script root path. + +.EXAMPLE +Set-CacheObject -CacheType 'Project' -Content $projectData -Depth 2 + +This example sets a cache object for the 'Project' type with the provided project data, using a serialization depth of 2. + +.INPUTS +None. + +.OUTPUTS +None. + +.NOTES +Author: Your Name +Date: MM/DD/YYYY +#> + +function Set-CacheObject +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] + [string]$CacheType, + + [Parameter()] + [AllowEmptyCollection()] + [Object[]]$Content, + + [Parameter()] + [int]$Depth = 3 + ) + + # Write initial verbose message + Write-Verbose "[Set-ObjectCache] Starting to set cache object for type: $CacheType" + + try + { + # Save content to cache file + Write-Verbose "[Set-ObjectCache] Exporting content to cache file for type: $CacheType" + Export-CacheObject -CacheType $CacheType -Content $Content -Depth $Depth + + # Save content to global variable + Write-Verbose "[Set-ObjectCache] Setting global variable AzDo$CacheType with the provided content" + Set-Variable -Name "AzDo$CacheType" -Value $Content -Scope Global -Force + + Write-Verbose "[Set-ObjectCache] Successfully set cache object for type: $CacheType" + + } + catch + { + Write-Error "[Set-ObjectCache] Failed to create cache for Azure DevOps API: $_" + throw + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResource.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResource.ps1 deleted file mode 100644 index 95f93cde1..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResource.ps1 +++ /dev/null @@ -1,107 +0,0 @@ -<# - .SYNOPSIS - Returns an array of resources returned from the Azure DevOps API. The type of resource - returned is generic to make this function reusable across all resources from the API. - - The resource type requested from the API is determined by the 'ResourceName' parameter. - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER ApiVersion - The version of the Azure DevOps API to use in the call/execution to/against the API. - - .PARAMETER Pat - The 'Personal Access Token' (PAT) to be used by any subsequent requests/operations - against the Azure DevOps API. This PAT must have the relevant permissions assigned - for the subsequent operations being performed. - - .PARAMETER ResourceName - The name of the resource being obtained from the Azure DevOps API (e.g. 'Project' or 'Operation') - - .PARAMETER ResourceId - The 'id' of the resource type being obtained. For example, if the 'ResourceName' parameter value - was 'Project', the 'ResourceId' value would be assumed to be the 'id' of a 'Project'. - - .EXAMPLE - Get-AzDevOpsApiResource -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ResourceName 'Project' - - Returns all 'Project' resources from the Azure DevOps API related to the Organization/ApiUri - value provided. - - .EXAMPLE - Get-AzDevOpsApiResource -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ResourceName 'Project' -ResourceId 'YourProjectId' - - Returns the 'Project' resource from the Azure DevOps API related to the Organization/ApiUri - value provided (where the 'id' of the 'Project' is equal to 'YourProjectId'). -#> -function Get-AzDevOpsApiResource -{ - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory=$true)] - [ValidateScript( { Test-AzDevOpsApiUri -ApiUri $_ -IsValid })] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter()] - [ValidateScript( { Test-AzDevOpsApiVersion -ApiVersion $_ -IsValid })] - [System.String] - $ApiVersion = $(Get-AzDevOpsApiVersion -Default), - - [Parameter(Mandatory=$true)] - [ValidateScript({ Test-AzDevOpsPat -Pat $_ -IsValid })] - [Alias('PersonalAccessToken')] - [System.String] - $Pat, - - [Parameter(Mandatory=$true)] - [ValidateScript({ Test-AzDevOpsApiResourceName -ResourceName $_ -IsValid })] - [System.String] - $ResourceName, - - [Parameter()] - [ValidateScript({ Test-AzDevOpsApiResourceId -ResourceId $_ -IsValid })] - [System.String] - $ResourceId - ) - - - # Prepare 'Get-AzDevOpsApiResourceUri' method parameters - $apiResourceUriParameters = @{ - ApiUri = $ApiUri - ApiVersion = $ApiVersion - ResourceName = $ResourceName - } - - if (![System.String]::IsNullOrWhiteSpace($ResourceId)) - { - $apiResourceUriParameters.ResourceId = $ResourceId - } - - - # Prepare 'Invoke-AzDevOpsApiRestMethod' method parameters - $invokeRestMethodParameters = @{ - Uri = $(Get-AzDevOpsApiResourceUri @apiResourceUriParameters) - Method = 'Get' - Headers = $(Get-AzDevOpsApiHttpRequestHeader -Pat $Pat) - } - - - [System.Management.Automation.PSObject]$apiResources = Invoke-AzDevOpsApiRestMethod @invokeRestMethodParameters - - - # If not a single, resource request, set from the resource(s) in the 'value' property within the response - if ($null -ne $apiResources.value) - { - [System.Management.Automation.PSObject[]]$apiResources = $apiResources.value - } - - - return $apiResources -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzManagedIdentityToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzManagedIdentityToken.ps1 deleted file mode 100644 index 3f60ccd20..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzManagedIdentityToken.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -<# -.SYNOPSIS -Obtains a managed identity token from Azure AD. - -.DESCRIPTION -The Get-AzManagedIdentityToken function is used to obtain an access token from Azure AD using a managed identity. It can only be called from the New-AzManagedIdentity or Update-AzManagedIdentity functions. - -.PARAMETER OrganizationName -Specifies the name of the organization. - -.PARAMETER Verify -Specifies whether to verify the connection. If this switch is not set, the function returns the managed identity token. If the switch is set, the function tests the connection and returns the access token. - -.EXAMPLE -Get-AzManagedIdentityToken -OrganizationName "Contoso" -Verify -Obtains the access token for the managed identity associated with the organization "Contoso" and verifies the connection. - -.NOTES -This function does not require the Azure PowerShell module. -#> - -Function Get-AzManagedIdentityToken { - [CmdletBinding()] - param ( - # Organization Name - [Parameter(Mandatory)] - [String] - $OrganizationName, - - # Verify the Connection - [Parameter()] - [Switch] - $Verify - ) - - # Obtain the access token from Azure AD using the Managed Identity - - $ManagedIdentityParams = @{ - # Define the Azure instance metadata endpoint to get the access token - Uri = $AzManagedIdentityLocalizedData.Global_Url_Azure_Instance_Metadata_Url -f $AzManagedIdentityLocalizedData.Global_AzureDevOps_Resource_Id - Method = 'Get' - Headers = @{Metadata="true"} - ContentType = 'Application/json' - } - - # Invoke the RestAPI - try { $response = Invoke-AzDevOpsApiRestMethod @ManagedIdentityParams } catch { Throw $_ } - # Test the response - if ($null -eq $response.access_token) { throw $AzManagedIdentityLocalizedData.Error_Azure_Instance_Metadata_Service_Missing_Token } - - # TypeCast the response to a ManagedIdentityToken object - $ManagedIdentity = New-ManagedIdentityToken -ManagedIdentityTokenObj $response - # Null the response - $null = $response - - # Return the token if the verify switch is not set - if (-not($verify)) { return $ManagedIdentity } - - # Test the Connection - if (-not(Test-AzManagedIdentityToken $ManagedIdentity)) { throw $AzManagedIdentityLocalizedData.Error_Azure_API_Call_Generic } - - # Return the AccessToken - return ($ManagedIdentity) - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 new file mode 100644 index 000000000..7c0be4dc8 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 @@ -0,0 +1,98 @@ +<# +.SYNOPSIS +Converts permissions to an Access Control Entry (ACE) token. + +.DESCRIPTION +The ConvertTo-ACEList function converts permissions to an Access Control Entry (ACE) token. It takes the security namespace, identity, an array of permissions objects, and the organization name as mandatory parameters. It constructs the ACE token for each permission and adds it to the list of ACEs. + +.PARAMETER SecurityNamespace +The security namespace as a string. This parameter is mandatory. + +.PARAMETER Identity +The identity associated with the ACE. This parameter is mandatory. + +.PARAMETER Permissions +An array of permissions objects. This parameter is mandatory. + +.PARAMETER OrganizationName +The organization name as a string. This parameter is mandatory. + +.EXAMPLE +ConvertTo-ACEList -SecurityNamespace "Namespace" -Identity "User1" -Permissions @("Read", "Write") -OrganizationName "MyOrg" + +This example converts the permissions "Read" and "Write" for the identity "User1" in the specified security namespace and organization name to an ACE token. + +.NOTES +Author: Your Name +Date: Today's Date +#> + +Function ConvertTo-ACEList { + [CmdletBinding()] + param ( + # Mandatory parameter: the security namespace as a string. + [Parameter(Mandatory)] + [string]$SecurityNamespace, + + # Mandatory parameter: an array of permissions objects. + [Parameter()] + [Object[]]$Permissions = @(), + + # Mandatory parameter: the organization name as a string. + [Parameter(Mandatory)] + [string]$OrganizationName + ) + + # Log the start of the function. + Write-Verbose "[ConvertTo-ACEList] Started." + Write-Verbose "[ConvertTo-ACEList] Security Namespace: $SecurityNamespace" + Write-Verbose "[ConvertTo-ACEList] Organization Name: $OrganizationName" + Write-Verbose "[ConvertTo-ACEList] Permissions: $($Permissions | ConvertTo-Json)" + + # Initialize an empty list to hold the ACEs (Access Control Entries). + $ACEs = [System.Collections.Generic.List[HashTable]]::new() + + # Iterate through each of the permissions and construct the ACE token. + ForEach ($Permission in $Permissions) { + + Write-Verbose "[ConvertTo-ACEList] Constructing ACE for $($Permission.Identity)." + + # Define the parameters for the Find-Identity function. + $identityParams = @{ + # The name of the identity to search for. Remove any square brackets (e.g., [TEAM FOUNDATION]\Project Collection Administrators). + Name = $Permission.Identity.Replace('[', '').Replace(']', '') + OrganizationName = $OrganizationName + SearchType = 'principalName' + } + + # Define the parameters for the ACE Token. + $aceTokenParams = @{ + SecurityNamespace = $SecurityNamespace + ACEPermissions = $Permission.Permission + } + + # Convert the Permission to an ACE. + $ht = @{ + Identity = Find-Identity @identityParams + Permissions = ConvertTo-ACETokenList @aceTokenParams + } + + # If the Identity is not found, log a warning. + if (-not $ht.Identity) { + Write-Warning "[ConvertTo-ACEList] Identity $($Permission.Identity) was not found. This will not be added to the ACEs list." + continue + } + # If the Permissions are not found, log a warning. + if (-not $ht.Permissions) { + Write-Warning "[ConvertTo-ACEList] Permissions for $($Permission.Identity) were not found. This will not be added to the ACEs list." + continue + } + + # Add the constructed ACE to the ACEs list. + $ACEs.Add($ht) + + } + + return $ACEs + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.ps1 new file mode 100644 index 000000000..039cc9cba --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.ps1 @@ -0,0 +1,68 @@ + + +Function ConvertTo-ACETokenList { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$SecurityNamespace, + + [Parameter(Mandatory)] + [Object[]]$ACEPermissions + ) + + Write-Verbose "[ConvertTo-ACETokenList] Initializing the ACL Token." + $hashTableArray = [System.Collections.Generic.List[HashTable]]::new() + + Write-Verbose "[ConvertTo-ACETokenList] Performing a Lookup for the Security Descriptor." + Write-Verbose "[ConvertTo-ACETokenList] Security Namespace: $SecurityNamespace" + + $SecurityDescriptor = Get-CacheItem -Key $SecurityNamespace -Type 'SecurityNamespaces' + + # Check if the Security Descriptor was found + if (-not $SecurityDescriptor) { + Write-Error "Security Descriptor not found for namespace: $SecurityNamespace" + return + } + + # Iterate through each of the ACEs and construct the ACE Object + Write-Verbose "[ConvertTo-ACETokenList] Iterating through each of the ACE Permissions." + + ForEach ($ACEPermission in $ACEPermissions) { + + # Check to see if there are any permissions that are not found in the Security Descriptor + $missingPermissions = $ACEPermission.Keys | Where-Object { + ($_ -notin $SecurityDescriptor.actions.displayName) -and + ($_ -notin $SecurityDescriptor.actions.name) + } | ForEach-Object { + Write-Verbose "[ConvertTo-ACETokenList] Permission '$_' not found in the Security Descriptor for namespace: $SecurityNamespace" + } + + # Filter the Allow and Deny permissions + Write-Verbose "[ConvertTo-ACETokenList] ACEPermission: $($ACEPermission | ConvertTo-Json)" + Write-Verbose "[ConvertTo-ACETokenList] Filtering Allow and Deny permissions." + + $AllowPermissions = $ACEPermission.Keys | Where-Object { $ACEPermission."$_" -eq 'Allow' } + $DenyPermissions = $ACEPermission.Keys | Where-Object { $ACEPermission."$_" -eq 'Deny' } + + Write-Verbose "[ConvertTo-ACETokenList] Iterating through the Allow and Deny Permissions and computing actions." + $AllowBits = $SecurityDescriptor.actions | Where-Object { ($_.displayName -in $AllowPermissions) -or ($_.name -in $AllowPermissions) } + $DenyBits = $SecurityDescriptor.actions | Where-Object { ($_.displayName -in $DenyPermissions) -or ($_.name -in $DenyPermissions) } + + # Compute the bitwise OR for the permissions + $hashTable = @{ + DescriptorType = $SecurityNamespace + Allow = $AllowBits + Deny = $DenyBits + } + + Write-Verbose "[ConvertTo-ACETokenList] Adding computed hash table to the array" + Write-Verbose "[ConvertTo-ACETokenList] Hash Table: $($hashTable | ConvertTo-Json)" + $hashTableArray.Add($hashTable) + } + + Write-Verbose "[ConvertTo-ACETokenList] Completed processing ACE Permissions" + + # Return the hashtable array + return $hashTableArray + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 new file mode 100644 index 000000000..2b3fe3536 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 @@ -0,0 +1,111 @@ +<# +.SYNOPSIS +Converts an array of hash tables containing permissions into a list of Access Control Lists (ACLs). + +.DESCRIPTION +The ConvertTo-ACL function takes an array of hash tables containing permissions and converts them into a list of Access Control Lists (ACLs). Each permission in the array must contain 'Identity' and 'Permissions' keys. The function creates an ACL token and ACE (Access Control Entry) for each permission, and then adds them to the ACL list. The ACL list is returned as the output of the function. + +.PARAMETER Permissions +Mandatory parameter. An array of hash tables containing permissions. Each permission must contain 'Identity' and 'Permissions' keys. + +.PARAMETER SecurityNamespace +Mandatory parameter. The security namespace as a string. + +.PARAMETER isInherited +Mandatory parameter. Boolean indicating if the ACL is inherited. + +.PARAMETER OrganizationName +Mandatory parameter. The organization name as a string. + +.EXAMPLE +$permissions = @( + @{ + Identity = 'User1' + Permissions = 'Read' + }, + @{ + Identity = 'User2' + Permissions = 'Read', 'Write' + } +) + +ConvertTo-ACL -Permissions $permissions -SecurityNamespace 'Namespace1' -isInherited $true -OrganizationName 'Org1' + +This example converts an array of permissions into ACLs for a specific security namespace and organization. + +.OUTPUTS +System.Collections.Generic.List[HashTable] +A list of Access Control Lists (ACLs) created from the provided permissions. + +.NOTES +Author: Your Name +Date: Today's Date +#> +Function ConvertTo-ACL { + [CmdletBinding()] + param ( + # Mandatory parameter: an array of hash tables containing permissions. + [Parameter()] + [HashTable[]]$Permissions=@(), + + # Mandatory parameter: the security namespace as a string. + [Parameter(Mandatory = $true)] + [string]$SecurityNamespace, + + # Mandatory parameter: boolean indicating if the ACL is inherited. + [Parameter(Mandatory = $true)] + [bool]$isInherited, + + # Mandatory parameter: the organization name as a string. + [Parameter(Mandatory = $true)] + [string]$OrganizationName, + + # Mandatory parameter: the token name as a string. + [Parameter(Mandatory = $true)] + [string]$TokenName + ) + + # Verbose output indicating the start of the function. + Write-Verbose "[ConvertTo-ACL] Started." + + # Create a hash table for ACL token parameters. + $ACLTokenParams = @{ + SecurityNamespace = $SecurityNamespace + TokenName = $TokenName + } + + Write-Verbose "[ConvertTo-ACL] ACL Token Params: $($ACLTokenParams | Out-String)" + + # Create a hash table for ACE parameters. + $ACEParams = @{ + SecurityNamespace = $SecurityNamespace + Permissions = $Permissions + OrganizationName = $OrganizationName + } + + Write-Verbose "[ConvertTo-ACL] ACE Params: $($ACEParams | Out-String)" + + # Convert the Permission to an ACL Token and create the ACL hash table. + $ACL = @{ + token = New-ACLToken @ACLTokenParams + aces = ConvertTo-ACEList @ACEParams + inherited = $isInherited + } + + # If the ACEs are empty, write a warning and return. + if ($ACL.aces.Count -eq 0) { + Write-Warning "[ConvertTo-ACL] No ACEs were created. Returning." + return $ACL + } + + # Group the ACEs by the identity removing any duplicates. + $ACL.aces = Group-ACEs -ACEs $ACL.aces + + Write-Verbose "[ConvertTo-ACL] Created ACL: $($ACL | Out-String)" + + # Verbose output indicating the completion of the function. + Write-Verbose "[ConvertTo-ACL] Completed. Returning ACLs." + + # Return the list of ACLs. + return $ACL +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.ps1 new file mode 100644 index 000000000..1c7a30216 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.ps1 @@ -0,0 +1,165 @@ +<# +.SYNOPSIS +Serializes the ACL list based on the provided reference ACLs, descriptor ACL list, and descriptor match token. + +.DESCRIPTION +The ConvertTo-ACLHashtable function captures the ACLs that are relevant to the Git Repository by matching the descriptor match token. If the descriptor ACL list is empty, it falls back to using the reference ACLs. It then constructs an ACL object for each ACL in the ACL list, including properties like inheritPermissions, token, and acesDictionary. The acesDictionary contains ACE objects with properties like allow, deny, and descriptor. Finally, it returns the constructed ACLs hashtable. + +.PARAMETER ReferenceACLs +The reference ACLs to be used as a fallback if the descriptor ACL list is empty. + +.PARAMETER DescriptorACLList +The descriptor ACL list containing the ACLs relevant to the Git Repository. + +.PARAMETER DescriptorMatchToken +The descriptor match token used to filter the ACLs relevant to the Git Repository. + +.EXAMPLE +$referenceACLs = @( + [PSCustomObject]@{ + token = "token1" + inheritPermissions = $true + aces = @( + [PSCustomObject]@{ + permissions = @{ + allow = @{ + bit = 1 + } + deny = @{ + bit = 0 + } + } + Identity = @{ + value = @{ + ACLIdentity = @{ + descriptor = "descriptor1" + } + } + } + } + ) + } +) + +$descriptorACLList = @( + [PSCustomObject]@{ + token = "token2" + inheritPermissions = $false + aces = @( + [PSCustomObject]@{ + permissions = @{ + allow = @{ + bit = 0 + } + deny = @{ + bit = 1 + } + } + Identity = @{ + value = @{ + ACLIdentity = @{ + descriptor = "descriptor2" + } + } + } + } + ) + } +) + +$descriptorMatchToken = "token2" + +ConvertTo-ACLHashtable -ReferenceACLs $referenceACLs -DescriptorACLList $descriptorACLList -DescriptorMatchToken $descriptorMatchToken + +.OUTPUTS +System.Collections.Hashtable +The constructed ACLs hashtable containing the serialized ACLs. +#> + +Function ConvertTo-ACLHashtable { + param( + [Parameter()] + [Object[]] + $ReferenceACLs, + + [Parameter(Mandatory)] + [Object[]] + $DescriptorACLList, + + [Parameter(Mandatory)] + [String] + $DescriptorMatchToken + ) + + # Convert the ReferenceACLs to an array if it is not already an array + $ReferenceACLs = [Array]$ReferenceACLs + + Write-Verbose "[ConvertTo-ACLHashtable] Started." + Write-Verbose "[ConvertTo-ACLHashtable] Reference ACLs: $($ReferenceACLs | ConvertTo-Json -Depth 3)" + Write-Verbose "[ConvertTo-ACLHashtable] Descriptor ACL List: $($DescriptorACLList | ConvertTo-Json -Depth 3)" + Write-Verbose "[ConvertTo-ACLHashtable] Descriptor Match Token: $DescriptorMatchToken" + + # Initialize the ACLs hashtable with a count and a list to hold ACL objects + Write-Verbose "[ConvertTo-ACLHashtable] Initializing the ACLs hashtable." + $ACLHashtable = @{ + Count = 0 + value = [System.Collections.Generic.List[Object]]::new() + } + + # Filter out all ACLs that don't match the descriptor match token. These are needed to construct the ACLs object + # Otherwise, the existing ACLs will be removed. + Write-Verbose "[ConvertTo-ACLHashtable] Filtering descriptor ACLs that do not match the descriptor match token." + $FilteredDescriptorACLs = $DescriptorACLList | Where-Object { $_.token -notmatch $DescriptorMatchToken } + + # Iterate through the filtered descriptor ACLs to construct the ACLs object + Write-Verbose "[ConvertTo-ACLHashtable] Iterating through the filtered descriptor ACLs to construct the ACLs object." + ForEach ($DescriptorACL in $FilteredDescriptorACLs) { + Write-Verbose "Adding filtered ACL to the ACLs object." + $ACLHashtable.value.Add($DescriptorACL) + } + + # Construct the ACLs object from the reference ACLs + Write-Verbose "[ConvertTo-ACLHashtable] Constructing the ACLs object from the reference ACLs." + + # Iterate through the ACLs in the ReferenceACLs + ForEach ($ReferenceACL in $ReferenceACLs) { + Write-Verbose "[ConvertTo-ACLHashtable] Processing reference ACL." + Write-Verbose "[ConvertTo-ACLHashtable] Reference ACL: $($ReferenceACL | ConvertTo-Json -Depth 3)" + + # Construct the ACL Object with properties inheritPermissions, token, and acesDictionary + $ACLObject = [PSCustomObject]@{ + inheritPermissions = $ReferenceACL.inherited + token = ConvertTo-FormattedToken -Token $ReferenceACL.token + acesDictionary = @{} + } + + # Iterate through the ACEs in the current ACL to construct the ACEs Dictionary + ForEach ($ACE in $ReferenceACL.aces) { + Write-Verbose "[ConvertTo-ACLHashtable] Constructing ACE Object." + + # Construct the ACE Object with properties allow, deny, and descriptor + $ACEObject = @{ + allow = Get-BitwiseOrResult $ACE.permissions.allow.bit + deny = Get-BitwiseOrResult $ACE.permissions.deny.bit + descriptor = $ACE.Identity.value.ACLIdentity.descriptor + } + # Add the ACE to the ACEs Dictionary using the descriptor as the key + Write-Verbose "[ConvertTo-ACLHashtable] ACEObject $($ACEObject | ConvertTo-Json)." + Write-Verbose "[ConvertTo-ACLHashtable] Adding ACE to the ACEs Dictionary." + + $ACLObject.acesDictionary.Add($ACE.Identity.value.ACLIdentity.descriptor, $ACEObject) + } + + # Add the constructed ACL object (ACLObject) to the ACL List + Write-Verbose "[ConvertTo-ACLHashtable] Adding constructed ACL object to the ACL List." + $ACLHashtable.value.Add($ACLObject) + } + + # Update the ACL Count with the number of ACLs in the list + Write-Verbose "[ConvertTo-ACLHashtable] Updating the ACL Count." + $ACLHashtable.Count = $ACLHashtable.value.Count + + # Return the constructed ACLs hashtable + Write-Verbose "[ConvertTo-ACLHashtable] Completed." + Write-Output $ACLHashtable +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 new file mode 100644 index 000000000..ba0d274b5 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 @@ -0,0 +1,114 @@ +<# +.SYNOPSIS +Formats an Access Control List (ACL) object. + +.DESCRIPTION +The ConvertTo-FormattedACL function takes an ACL object and formats it into a structured format. It matches identities, formats permissions, and creates a formatted ACL object. + +.PARAMETER ACL +The ACL object from the pipeline. + +.PARAMETER SecurityNamespace +The security namespace as a string. + +.PARAMETER OrganizationName +The organization name as a string. + +.EXAMPLE +$myACL = Get-ACL -Path "C:\Temp" +$formattedACL = $myACL | ConvertTo-FormattedACL -SecurityNamespace "MyNamespace" -OrganizationName "MyOrganization" + +This example retrieves an ACL object for a specific path and formats it using the ConvertTo-FormattedACL function. + +.OUTPUTS +[System.Collections.Generic.List[HashTable]] +A list of formatted ACLs. + +.NOTES +Author: Michael Zanatta +Date: 06/26/2024 +#> + +Function ConvertTo-FormattedACL { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [Object]$ACL, + + [Parameter(Mandatory = $true)] + [String]$SecurityNamespace, + + [Parameter(Mandatory = $true)] + [String]$OrganizationName + ) + + begin { + Write-Verbose "[ConvertTo-FormattedACL] Started." + $ACLList = [System.Collections.Generic.List[HashTable]]::new() + } + + process { + # Logging + Write-Verbose "[ConvertTo-FormattedACL] Processing ACL: $($ACL.token)" + Write-Verbose "[ConvertTo-FormattedACL] ACL: $($ACL | ConvertTo-Json)" + + # If the token is empty, skip it. + if (-not $ACL.token) { + Write-Verbose "[ConvertTo-FormattedACL] Current token is empty. Skipping." + Write-Warning "[ConvertTo-FormattedACL] Current token is empty. Skipping. ACL: $($ACL | ConvertTo-Json)" + return + } + + $ACEs = [System.Collections.Generic.List[HashTable]]::new() + $ACEEntries = $ACL.acesDictionary.psObject.properties.name + Write-Verbose "[ConvertTo-FormattedACL] Found ACE entries: $($ACEEntries.Count)" + + # If the ACE entries are empty, skip it. + if ($ACEEntries.Count -eq 0) { + Write-Verbose "[ConvertTo-FormattedACL] Current ACE entries are empty. Skipping." + Write-Warning "[ConvertTo-FormattedACL] Current ACE entries are empty. Skipping. ACL: $($ACL | ConvertTo-Json)" + return + } + + $ACEEntries | ForEach-Object { + Write-Verbose "[ConvertTo-FormattedACL] Processing ACE entry: $_" + $ACEs.Add([HashTable]@{ + Name = $_ + Value = $ACL.acesDictionary."$_" + }) + } + Write-Verbose "[ConvertTo-FormattedACL] Found ACEs: $($ACEs.Count)" + + # If the ACEs are empty, skip it. + if ($ACEs.Count -eq 0) { + Write-Verbose "[ConvertTo-FormattedACL] Current ACEs are empty. Skipping." + Write-Warning "[ConvertTo-FormattedACL] Current ACEs are empty. Skipping. ACL: $($ACL | ConvertTo-Json)" + return + } + + # Create the Formatted ACL Object + foreach ($ACE in $ACEs) { + Write-Verbose "[ConvertTo-FormattedACL] Matching identity for ACE: $($ACE.Name)" + $ACE."Identity" = Find-Identity -Name $ACE.Name -OrganizationName $OrganizationName + Write-Verbose "[ConvertTo-FormattedACL] Formatting ACE: $($ACE.Name) - Allow $($ACE.value.allow) - Deny $($ACE.value.deny)" + $ACE."Permissions" = Format-ACEs -Allow $ACE.value.allow -Deny $ACE.value.deny -SecurityNamespace $SecurityNamespace + } + + Write-Verbose "[ConvertTo-FormattedACL] Adding formatted ACL: $($ACL.token)" + + $formattedACL = [HashTable]@{ + token = Parse-ACLToken -Token $ACL.token -SecurityNamespace $SecurityNamespace + ACL = $ACL + inherited = $ACL.inheritPermissions + aces = $ACEs + } + + $ACLList.Add($formattedACL) + } + + end { + Write-Verbose "[ConvertTo-FormattedACL] Completed." + + return $ACLList + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.ps1 new file mode 100644 index 000000000..ffec9acee --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.ps1 @@ -0,0 +1,62 @@ +<# +.SYNOPSIS +Formats the token based on its type. + +.DESCRIPTION +The ConvertTo-FormattedToken function is used to format a token based on its type. It takes a token as input and returns the formatted token string. + +.PARAMETER Token +The token to format. This parameter is mandatory and accepts an array of objects. + +.EXAMPLE +$token = @{ + type = 'GitProject' + projectId = 'myProject' + repositoryId = 'myRepo' +} +ConvertTo-FormattedToken -Token $token +# Output: "repoV2/myProject/myRepo" + +.NOTES +This function assumes that the token type is either 'GitOrganization', 'GitProject', or 'GitRepository'. If the token type is not one of these, the function will not format the token and will return an empty string. +#> + +Function ConvertTo-FormattedToken { + [CmdletBinding()] + param ( + # Define a mandatory parameter named 'Token' of type Object array + [Parameter(Mandatory)] + [Object[]]$Token + ) + + # Output verbose message indicating the function has started + Write-Verbose "[ConvertTo-FormattedToken] Started." + + # Initialize variable to store formatted token string + $string = "" + + # Determine the type of the token and format accordingly + switch ($Token) { + # If the token type is 'GitOrganization' + {$_.type -eq 'GitOrganization'} { + $string = 'repoV2' + break + } + # If the token type is 'GitProject' + {$_.type -eq 'GitProject'} { + $string = 'repoV2/{0}' -f $Token.projectId + break + } + # If the token type is 'GitRepository' + {$_.type -eq 'GitRepository'} { + $string = 'repoV2/{0}/{1}' -f $Token.projectId, $Token.RepoId + break + } + } + + # Output verbose message with the token value + Write-Verbose "[ConvertTo-FormattedToken] Token: $string" + + # Return the formatted token string + return $string +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.ps1 new file mode 100644 index 000000000..622359687 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.ps1 @@ -0,0 +1,60 @@ +<# +.SYNOPSIS +Formats the Access Control Entries (ACEs) based on the specified parameters. + +.DESCRIPTION +The Format-ACEs function is used to format the Access Control Entries (ACEs) based on the specified parameters. It performs a lookup of the security namespace and creates a new ACE object with the allow and deny actions. + +.PARAMETER Allow +Specifies whether to include ACEs with the "Allow" permission. Default value is 0 (false). + +.PARAMETER Deny +Specifies whether to include ACEs with the "Deny" permission. Default value is 0 (false). + +.PARAMETER SecurityNamespace +Specifies the security namespace to perform the lookup. This parameter is mandatory. + +.EXAMPLE +Format-ACEs -Allow $true -Deny $false -SecurityNamespace "MySecurityNamespace" +Returns the ACE object with the "Allow" actions from the specified security namespace. + +.EXAMPLE +Format-ACEs -Allow $false -Deny $true -SecurityNamespace "MySecurityNamespace" +Returns the ACE object with the "Deny" actions from the specified security namespace. + +.NOTES +This function requires the Get-CacheItem cmdlet from the AzureDevOpsDsc.Common module to perform the security namespace lookup. +#> + +Function Format-ACEs +{ + [CmdletBinding()] + param + ( + [Parameter()] + [Int]$Allow=0, + [Parameter()] + [Int]$Deny=0, + [Parameter(Mandatory)] + [string]$SecurityNamespace + ) + + # + # Logging + Write-Verbose "[Format-ACEs] Started." + + # + # Perform a Lookup of the Security Namespace + $namespace = Get-CacheItem -Key $SecurityNamespace -Type 'SecurityNamespaces' + + # Create a new ACE Object + $ACE = @{ + Allow = $namespace.actions | Where-Object { $_.bit -band $Allow } + Deny = $namespace.actions | Where-Object { $_.bit -band $Deny } + DescriptorType = $SecurityNamespace + } + + # + # Return the ACE + return $ACE +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 new file mode 100644 index 000000000..5f9aba1e2 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 @@ -0,0 +1,55 @@ +<# +.SYNOPSIS + Performs a bitwise OR operation on an array of integers. + +.DESCRIPTION + The Get-BitwiseOrResult function takes an array of integers as input and performs a bitwise OR operation on them. It returns the result of the operation. + +.PARAMETER integers + Specifies the array of integers on which the bitwise OR operation is performed. + + Required? true + Position? 1 + Default value + Accept pipeline input? false + Accept wildcard characters? false + +.EXAMPLE + $inputArray = 1, 2, 4, 8 + $result = Get-BitwiseOrResult -integers $inputArray + $result + # Output: 15 + +.NOTES + Author: Your Name + Date: Current Date +#> +Function Get-BitwiseOrResult { + [CmdletBinding()] + param ( + [int[]]$integers + ) + + Write-Verbose "[Get-BitwiseOrResult] Started." + Write-Verbose "[Get-BitwiseOrResult] Integers: $integers" + + $result = 0 + + if ($integers.Count -eq 0) { + return 0 + } + + foreach ($integer in $integers) { + if (-not [int]::TryParse($integer.ToString(), [ref]$null)) { + Write-Error "Invalid integer value: $integer" + return 0 + } + $result = $result -bor $integer + } + + if ([String]::IsNullOrEmpty($result)) { + return 0 + } + + return $result +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.ps1 new file mode 100644 index 000000000..b033f0ed6 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.ps1 @@ -0,0 +1,64 @@ +Function Group-ACEs { + param( + # Mandatory parameter: an array of ACE objects. + [Parameter()] + [Object[]]$ACEs + ) + + Write-Verbose "[Group-ACE] Started." + + # Check if the ACEs are not found. + if (-not $ACEs) { + Write-Verbose "[Group-ACE] ACEs not found." + return + } + + Write-Verbose "[Group-ACE] Initializing empty list to hold the ACEs." + + # Initialize an empty list to hold the ACEs (Access Control Entries). + $ACEList = [System.Collections.Generic.List[HashTable]]::new() + + Write-Verbose "[Group-ACE] Grouping the ACEs by identity." + + # Group the ACEs by the identity. + $GroupedIdentities = $ACEs | Group-Object -Property { $_.Identity.value.originId } + + Write-Verbose "[Group-ACE] Filtering groups based on count." + + # Filter by the count + $Single, $Multiple = $GroupedIdentities.Where({ $_.Count -eq 1 }, 'Split') + + Write-Verbose "[Group-ACE] Adding single identities to the ACE list." + + # Add the single identities to the ACE list + $Single | ForEach-Object { + Write-Verbose "[Group-ACE] Adding single identity: $($_.Group[0].Identity)" + $ACEList.Add($_.Group[0]) + } + + Write-Verbose "[Group-ACE] Grouping multiple identities by permissions." + + # Group the multiple identities by the permissions + ForEach ($item in $Multiple) { + + Write-Verbose "[Group-ACE] Processing multiple identity: $($item.Group[0].Identity)" + + # Create a new hash table for the group + $ht = @{ + Identity = $item.Group[0].Identity + Permissions = @{ + Deny = $item.Group.Permissions.Deny | Sort-Object -Unique Bit + Allow = $item.Group.Permissions.Allow | Sort-Object -Unique Bit + DescriptorType = $item.Group[0].Permissions.DescriptorType + } + } + + Write-Verbose "[Group-ACE] Adding grouped identity with permissions." + + $ACEList.Add($ht) + } + + Write-Verbose "[Group-ACE] Completed." + $ACEList + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.ps1 new file mode 100644 index 000000000..0ec4b09f9 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.ps1 @@ -0,0 +1,113 @@ +<# +.SYNOPSIS +Converts a security TokenName to an ACL token based on the security namespace. + +.DESCRIPTION +The New-ACLToken function converts a security TokenName to an ACL token based on the specified security namespace. It is used in the Azure DevOps DSC module to derive the token type and other relevant information for Git repositories. + +.PARAMETER SecurityNamespace +Specifies the security namespace for which the ACL token needs to be generated. + +.PARAMETER TokenName +Specifies the security TokenName that needs to be converted to an ACL token. + +.OUTPUTS +The function returns a hashtable containing the following properties: +- type: The type of the ACL token (e.g., GitOrganization, GitProject, GitRepository, GitUnknown, UnknownSecurityNamespace). +- inherited: Indicates whether the security TokenName is inherited or not. +- projectId: The ID of the project associated with the ACL token (applicable for GitProject and GitRepository types). +- RepoId: The ID of the repository associated with the ACL token (applicable for GitRepository type). + +.EXAMPLE +New-ACLToken -SecurityNamespace 'Git Repositories' -TokenName 'Contoso/Org/Project' + +This example converts the security TokenName 'Contoso/Org/Project' to an ACL token for the 'Git Repositories' security namespace. The resulting ACL token will have the type 'GitProject' and the project ID will be retrieved from the cache. + +.NOTES +This function is part of the AzureDevOpsDsc.Common module and is used internally by other functions in the module. +#> + +Function New-ACLToken { + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [string]$SecurityNamespace, + + [Parameter(Mandatory)] + [string]$TokenName + + ) + + $TokenName = $TokenName.Replace('[', '').Replace(']', '') + + Write-Verbose "[New-ACLToken] Started." + Write-Verbose "[New-ACLToken] Security Namespace: $SecurityNamespace" + Write-Verbose "[New-ACLToken] Token Name: $TokenName" + + + $result = @{} + + # Create a new ACL Object + switch ($SecurityNamespace) { + + # Git Repositories + 'Git Repositories' { + + # Derive the Token Type GitOrganization, GitProject or GitRepository + if ($TokenName -match $LocalizedDataAzResourceTokenPatten.OrganizationGit) { + # Derive the Token Type GitOrganization + $result.type = 'GitOrganization' + + } elseif ($TokenName -match $LocalizedDataAzResourceTokenPatten.GitProject) { + # Derive the Token Type GitProject + $result.type = 'GitProject' + $result.projectId = (Get-CacheItem -Key $matches.ProjectName.Trim() -Type 'LiveProjects').id + + } elseif ($TokenName -match $LocalizedDataAzResourceTokenPatten.GitRepository) { + # Derive the Token Type GitRepository + $result.type = 'GitRepository' + $result.projectId = (Get-CacheItem -Key $matches.ProjectName.Trim() -Type 'LiveProjects').id + $result.RepoId = (Get-CacheItem -Key $TokenName -Type 'LiveRepositories').id + + } else { + # Derive the Token Type GitUnknown + $result.type = 'GitUnknown' + Write-Warning "[New-ACLToken] TokenName '$TokenName' does not match any known Git ACL Token Patterns." + } + break; + } + + # Identity + 'Identity' { + + # Derive the Token Type Identity + if ($TokenName -match $LocalizedDataAzResourceTokenPatten.GroupPermission) { + # Derive the Token Type Identity + $result.type = 'GitGroupPermission' + $result.projectId = $matches.ProjectId + $result.groupId = $matches.GroupId + + } else { + # Derive the Token Type Identity + $result.type = 'GroupUnknown' + Write-Warning "[New-ACLToken] TokenName '$TokenName' does not match any known Identity ACL Token Patterns." + } + + $result.type = 'Identity' + break; + } + + default { + Write-Warning "[New-ACLToken] SecurityNamespace '$SecurityNamespace' is not recognized." + $result.type = 'UnknownSecurityNamespace' + } + + } + + Write-Verbose "[New-ACLToken] ACL Token: $($result | Out-String)" + + # Return the ACL Token + return $result + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.ps1 new file mode 100644 index 000000000..79a461f42 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.ps1 @@ -0,0 +1,78 @@ +Function Parse-ACLToken { + param( + [Parameter(Mandatory)] + [String]$Token, + + [Parameter(Mandatory)] + [ValidateSet('Identity', 'Git Repositories')] + [String]$SecurityNamespace + ) + + $result = @{} + + Write-Verbose "[Parse-ACLToken] Started." + Write-Verbose "[Parse-ACLToken] Token: $Token" + Write-Verbose "[Parse-ACLToken] Security Namespace: $SecurityNamespace" + + # + # Git Repositories + if ($SecurityNamespace -eq 'Git Repositories') { + # Match the Token with the Regex Patterns + switch -regex ($Token.Trim()) { + $LocalizedDataAzACLTokenPatten.OrganizationGit { + $result.type = 'OrganizationGit' + break; + } + + $LocalizedDataAzACLTokenPatten.GitProject { + $result.type = 'GitProject' + break; + } + + $LocalizedDataAzACLTokenPatten.GitRepository { + $result.type = 'GitRepository' + break; + } + + $LocalizedDataAzACLTokenPatten.GitBranch { + $result.type = 'GitBranch' + break; + } + + default { + throw "Token '$Token' is not recognized." + } + } + + # + # Identity + } elseif ($SecurityNamespace -eq 'Identity') { + + # Match the Token with the Regex Patterns + switch -regex ($Token.Trim()) { + + $LocalizedDataAzACLTokenPatten.ResourcePermission { + $result.type = 'ResourcePermission' + break; + } + + $LocalizedDataAzACLTokenPatten.GroupPermission { + $result.type = 'GroupPermission' + break; + } + + default { + throw "Token '$Token' is not recognized." + } + } + } + + # Get all Capture Groups and add them into a hashtable + $matches.keys | Where-Object { $_.Length -gt 1 } | ForEach-Object { + $result."$_" = $matches."$_" + } + + $result._token = $Token + + return $result +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.ps1 new file mode 100644 index 000000000..df4a23050 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.ps1 @@ -0,0 +1,26 @@ +function Resolve-ACLToken +{ + param ( + # Reference ACL + [Parameter()] + [Object[]] + $ReferenceObject, + + # Difference ACL + [Parameter()] + [Object[]] + $DifferenceObject + ) + + Write-Verbose "[Resolve-ACLToken] Started." + + # Prefer the Difference ACL if it is not null. This is because the Difference ACL contains the most recent information. + if ($null -ne $DifferenceObject) + { + Write-Verbose "[Resolve-ACLToken] Difference ACL is not null." + return $DifferenceObject.token._token + } + + Write-Verbose "[Resolve-ACLToken] Difference ACL is null." + return $ReferenceObject.token._token +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.ps1 new file mode 100644 index 000000000..e28c464de --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.ps1 @@ -0,0 +1,226 @@ + +# The Azure Devops ACL API is different to other ACL APIs where it only provides a means to get, remove and set ACLs. +# This means that if there is a change to the ACL's then the entire ACL must be set again. +# This function captures the differences between two ACLs and if there is a different the properties changed will contain the new reference ACL. +# + +Function Test-ACLListforChanges +{ + [CmdletBinding()] + param ( + # The Reference ACL to compare against. + [Parameter()] + [Object[]] + $ReferenceACLs, + + # The Difference ACL to compare against. + [Parameter()] + [Object[]] + $DifferenceACLs + ) + + Write-Verbose "[Test-ACLListforChanges] Started." + + $result = @{ + status = "Unchanged" + reason = @( + @{ + Value = "No changes detected." + Reason = "No changes detected." + } + ) + propertiesChanged = @() + } + + # + # Test if the Reference and Difference ACLs are null. + + if (($ReferenceACLs.aces -eq $null) -and ($DifferenceACLs -eq $null)) + { + Write-Verbose "[Test-ACLListforChanges] ACLs are null." + return $result + } + + # Get the Token + #$Token = Get-ACLToken $ReferenceACLs $DifferenceACLs + + # If the Reference ACL is null, set the status to changed. + if (($null -eq $ReferenceACLs) -or ($null -eq $ReferenceACLs.aces)) + { + Write-Verbose "[Test-ACLListforChanges] Reference ACL is null." + $result.status = "Missing" + $result.propertiesChanged = $DifferenceACLs + $result.reason += @{ + Value = $DifferenceACLs + Reason = "Reference ACL is null." + } + return $result + } + + # If the Difference ACL is null, set the status to changed. + if ($null -eq $DifferenceACLs) + { + Write-Verbose "[Test-ACLListforChanges] Difference ACL is null." + $result.status = "NotFound" + $result.propertiesChanged = $ReferenceACLs + $result.reason += @{ + Value = $ReferenceACLs + Reason = "Difference ACL is null." + } + return $result + } + + # Set the flag to be false + $isChanged = $false + + # + # Test if the Reference and Difference ACLs count is not equal. + + if ($ReferenceACLs.ACEs.Count -ne $DifferenceACLs.ACEs.Count) + { + Write-Verbose "[Test-ACLListforChanges] ACLs count is not equal." + $result.status = "Changed" + $result.reason += @{ + Value = $ReferenceACLs + Reason = "ACLs count is not equal." + } + $result.propertiesChanged = $ReferenceACLs + return $result + } + + # + # Test if the inherited flag is not equal. + + if ($ReferenceACLs.inherited -ne $DifferenceACLs.inherited) + { + Write-Verbose "[Test-ACLListforChanges] Inherited flag is not equal." + $result.status = "Changed" + $result.reason += @{ + Value = $ReferenceACLs + Reason = "Inherited flag is not equal." + } + $result.propertiesChanged = $ReferenceACLs + return $result + } + + # + # Test each of the Reference ACLs + ForEach ($ReferenceACL in $ReferenceACLs) { + + $acl = $DifferenceACLs | Where-Object { $_.Identity.value.originId -eq $ReferenceACL.Identity.value.originId } + + # Test if the ACL is not found in the Difference ACL. + if ($null -eq $acl) { + $result.status = "Changed" + $result.propertiesChanged = $ReferenceACLs + $result.reason += @{ + Value = $ReferenceACL + Reason = "ACL not found in Difference ACL." + } + return $result + } + + # Test the inherited flag. + if ($ReferenceACL.isInherited -ne $acl.isInherited) { + $result.status = "Changed" + $result.propertiesChanged = $ReferenceACLs + $result.reason += @{ + Value = $ReferenceACL + Reason = "Inherited flag is not equal." + } + return $result + } + + # Iterate through the ACEs and compare them. + + ForEach ($ReferenceACE in $ReferenceACL.ACEs) { + + # Check if the ACE is found in the Difference ACL. + $ace = $DifferenceACLs.ACEs | Where-Object { $_.Identity.value.originId -eq $ReferenceACE.Identity.value.originId } + + # Check if the ACE is not found in the Difference ACL. + if ($null -eq $ace) { + $result.status = "Changed" + $result.propertiesChanged = $ReferenceACLs + $result.reason += @{ + Value = $ReferenceACE + Reason = "ACE not found in Difference ACL." + } + return $result + } + + # + # From this point on, we know that the ACE is found in both ACLs. + + # + # Compare the Allow ACEs + + $ReferenceAllow = Get-BitwiseOrResult $ReferenceACE.Permissions.Allow.Bit + $DifferenceAllow = Get-BitwiseOrResult $ace.Permissions.Allow.Bit + + # Test if the integers are not equal. + if ($ReferenceAllow -ne $DifferenceAllow) + { + Write-Verbose "[Test-ACLListforChanges] Allow ACEs are not equal." + $result.propertiesChanged = $ReferenceACLs + $result.reason += @{ + Value = @{ + ReferenceAllow = $ReferenceAllow + DifferenceAllow = $DifferenceAllow + } + Reason = "Allow ACEs are not equal." + } + $result.status = "Changed" + } + + # + # Compare the Deny ACEs + + $ReferenceDeny = Get-BitwiseOrResult $ReferenceACE.Permissions.Deny.Bit + $DifferenceDeny = Get-BitwiseOrResult $ace.Permissions.Deny.Bit + + # Test if the integers are not equal. + if ($ReferenceDeny -ne $DifferenceDeny) + { + Write-Verbose "[Test-ACLListforChanges] Deny ACEs are not equal." + $result.propertiesChanged = $ReferenceACLs + $result.reason += @{ + Value = @{ + ReferenceDeny = $ReferenceDeny + DifferenceDeny = $DifferenceDeny + } + Reason = "Deny ACEs are not equal." + } + $result.status = "Changed" + } + + } + + } + + # + # Test each of the Difference ACLs + + foreach ($DifferenceACL in $DifferenceACLs) { + + $acl = $ReferenceACLs | Where-Object { $_.Identity.value.originId -eq $DifferenceACL.Identity.value.originId } + + # Test if the ACL is not found in the Reference ACL. + if ($null -eq $acl) { + $result.status = "Changed" + $result.reason += @{ + Value = $DifferenceACL + Reason = "ACL not found in Reference ACL." + } + $result.propertiesChanged = $ReferenceACLs + return $result + } + + # No other tests are required as the Reference ACL has already been tested. + + } + + # Result the result hash table. + return $result + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.ps1 similarity index 77% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.ps1 index 531c39f37..c3ce7a41c 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.ps1 @@ -28,11 +28,11 @@ function Test-AzDevOpsApiHttpRequestHeader [OutputType([System.Boolean])] param ( - [Parameter(Mandatory = $true)] + [Parameter()] [Hashtable] $HttpRequestHeader, - [Parameter(Mandatory = $true)] + [Parameter()] [ValidateSet($true)] [System.Management.Automation.SwitchParameter] $IsValid @@ -44,8 +44,17 @@ function Test-AzDevOpsApiHttpRequestHeader if ($HttpRequestHeader.Metadata) { return $true } # Otherwise, if the header is not valid, retrun false + if ($null -eq $HttpRequestHeader) { return $false } + + # If the header is not null, but the Authorization is null, return false + if ($HttpRequestHeader.Authorization) + { + if ($HttpRequestHeader.Authorization -match '^(Basic|Bearer)(:\s|\s).+$') + { + return $true + } + } + + return $false - return !($null -eq $HttpRequestHeader -or - $null -eq $HttpRequestHeader.Authorization -or - $HttpRequestHeader.Authorization -match '^(Basic|Bearer):\s.+$') } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceId.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.ps1 similarity index 96% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceId.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.ps1 index c585b21cc..04463d9e8 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceId.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.ps1 @@ -30,7 +30,7 @@ function Test-AzDevOpsApiResourceId [System.String] $ResourceId, - [Parameter(Mandatory = $true)] + [Parameter()] [ValidateSet($true)] [System.Management.Automation.SwitchParameter] $IsValid diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiTimeoutExceeded.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.ps1 similarity index 100% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiTimeoutExceeded.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.ps1 diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiUri.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.ps1 similarity index 85% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiUri.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.ps1 index 9378da005..7f75451a2 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiUri.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.ps1 @@ -26,16 +26,19 @@ function Test-AzDevOpsApiUri [OutputType([System.Boolean])] param ( - [Parameter(Mandatory = $true)] + [Parameter()] [System.String] $ApiUri, - [Parameter(Mandatory = $true)] + [Parameter()] [ValidateSet($true)] [System.Management.Automation.SwitchParameter] $IsValid ) + # The APIUri is not mandatory. If it is not provided, the function will return $true. + if ([String]::IsNullOrEmpty($ApiUri)) { return $true } + return !(($ApiUri -inotlike 'http://*' -and $ApiUri -inotlike 'https://*') -or $ApiUri -inotlike '*/_apis/') diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiVersion.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.ps1 similarity index 94% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiVersion.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.ps1 index fc30e8857..91f588d29 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiVersion.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.ps1 @@ -27,11 +27,11 @@ function Test-AzDevOpsApiVersion [OutputType([System.Boolean])] param ( - [Parameter(Mandatory = $true)] + [Parameter()] [System.String] $ApiVersion, - [Parameter(Mandatory = $true)] + [Parameter()] [ValidateSet($true)] [System.Management.Automation.SwitchParameter] $IsValid diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Wait-AzDevOpsApiResource.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.ps1 similarity index 100% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Wait-AzDevOpsApiResource.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.ps1 diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 new file mode 100644 index 000000000..0db85f42d --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 @@ -0,0 +1,34 @@ +<# +.SYNOPSIS +Converts a byte array to a Base64 string. + +.DESCRIPTION +The ConvertTo-Base64String function takes a byte array as input and converts it to a Base64 string representation. + +.PARAMETER InputObject +The byte array to be converted to a Base64 string. + +.EXAMPLE +$bytes = [System.Text.Encoding]::UTF8.GetBytes("Hello, World!") +$base64String = ConvertTo-Base64String -InputObject $bytes +$base64String +# Output: SGVsbG8sIFdvcmxkIQ== + +.NOTES +Author: GitHub Copilot +Date: September 2021 +#> +function ConvertTo-Base64String +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [String] + $InputObject + ) + + process { + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($InputObject)) + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.ps1 new file mode 100644 index 000000000..030044040 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.ps1 @@ -0,0 +1,115 @@ +Function Find-AzDoIdentity { + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$Identity + ) + + Write-Verbose "[Find-AzDoIdentity] Starting identity lookup for '$Identity'." + + # Get the Usernames from the cache + $CachedUsers = $Global:AZDOLiveUsers + Write-Verbose "[Find-AzDoIdentity] Retrieved cached users." + + # Get the Groups from the Cache + $CachedGroups = $Global:AZDOLiveGroups + Write-Verbose "[Find-AzDoIdentity] Retrieved cached groups." + + switch ($Identity) { + + # Test if the Username contains an '@' symbol. If it does, it is an email address and should be converted to a UPN + { $Identity -like '*@*' } { + Write-Verbose "[Find-AzDoIdentity] Identity is an email address; converting to UPN." + + # Perform a lookup using the existing username + $cachedItem = Get-CacheItem -Key $Identity -Type 'LiveUsers' + #$cachedItem = $cachedItem | Select-Object *, @{Name = 'Type'; Exp={'Users'}} + + # Test if the user is found + if ($null -eq $cachedItem) { + Write-Warning "[Find-AzDoIdentity] No user found with the UPN '$Identity'." + return + } + + Write-Verbose "[Find-AzDoIdentity] Found user with UPN '$Identity'." + return $cachedItem + } + + # Test if the Username contains a '\' or '/' symbol. If it does, it is a group and needs to be sanitized + { $Identity -match '\\|\/' } { + Write-Verbose "[Find-AzDoIdentity] Identity contains a '\' or '/'; treated as a group." + + # If the Identity 'Project\GroupName' does not contain square brackets, add them around the Project. + if ($Identity -notmatch '\[.*\]') { + $split = $Identity -split ('\\|\/') + $Identity = "[{0}]\{1}" -f $split[0], $split[1] + } + + # Perform a lookup using the existing username + Write-Verbose "[Find-AzDoIdentity] Performing a lookup using the group name '$Identity'." + $cachedItem = Get-CacheItem -Key $Identity -Type 'LiveGroups' + + # Test if the group is found + if ($null -eq $cachedItem) { + Write-Warning "[Find-AzDoIdentity] No group found with the name '$Identity'." + return + } + + #$cachedItem = $cachedItem | Select-Object *, @{Name = 'Type'; Exp={'Group'}} + + Write-Verbose "[Find-AzDoIdentity] cachedItem '$($cachedItem | ConvertTo-Json)'." + Write-Verbose "[Find-AzDoIdentity] Found group with name '$Identity'." + + return $cachedItem + } + + # If all else fails, try and perform a lookup using the display name. + # If multiple users are found, throw an error. + # If no users are found, throw an error. + default { + + Write-Verbose "[Find-AzDoIdentity] Performing a lookup using the display name '$Identity'." + + # Perform a lookup using the existing username + [Array]$User = $CachedUsers | Where-Object { $_.value.displayName -eq $Identity } + [Array]$Group = $CachedGroups | Where-Object { $_.value.displayName -eq $Identity } + + # Write the number of users and groups found + Write-Verbose "[Find-AzDoIdentity] Found $($User.Count) users and $($Group.Count) groups with the display name '$Identity'." + + # Test if the user is found + if ($User.Count -gt 1) { + Write-Warning "[Find-AzDoIdentity] Multiple users found with the display name '$Identity'. Please use the UPN (user@domain.com)" + return + } + + # Test if a group is found + if ($Group.Count -gt 1) { + Write-Warning "[Find-AzDoIdentity] Multiple groups found with the display name '$Identity'. Please use the UPN ([project]\[groupname])" + return + } + + # Test if both a user and a group are found + if ($User.Count -eq 1 -and $Group.Count -eq 1) { + Write-Warning "[Find-AzDoIdentity] Both a user and a group found with the display name '$Identity'. Please use the UPN (user@domain.com or [project]\[groupname])" + return + } + + if ($User.Count -eq 1) { + # If the user is found, add the type and return the user + Write-Verbose "[Find-AzDoIdentity] Single user found with the display name '$Identity'." + return $User.value + } elseif ($Group.Count -eq 1) { + # If the group is found, add the type and return the group + Write-Verbose "[Find-AzDoIdentity] Single group found with the display name '$Identity'." + return $Group.value + } + + Write-Warning "No identity found for '$Identity'." + return + + } + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 new file mode 100644 index 000000000..8cf6a6b3f --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 @@ -0,0 +1,189 @@ +<# +.SYNOPSIS + Finds an identity (user or group) based on the provided name. + +.DESCRIPTION + The Find-Identity function searches for an identity (user or group) based on the provided name. + It first checks the cached groups and users to find a match. + If multiple identities with the same name are found, a warning is issued and null is returned. + +.PARAMETER Name + The name of the identity to search for. + +.PARAMETER OrganizationName + The name of the organization. + +.PARAMETER SearchType + The type of search to perform. Valid values are 'descriptor', 'descriptorId', 'displayName', 'originId', 'key'. + +.OUTPUTS + Returns the ACLIdentity object of the found identity. If no identity is found, null is returned. + +.NOTES + Author: Your Name + Date: Current Date + +.EXAMPLE + Find-Identity -Name "JohnDoe" + Returns the ACLIdentity object of the identity with the name "JohnDoe" if found. Otherwise, returns null. +#> + +Function Find-Identity { + [CmdletBinding()] + param( + # The name of the identity to search for. + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name, + + # The name of the organization. + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$OrganizationName, + + # The type of search to perform. + [Parameter()] + [ValidateSet('descriptor', 'descriptorId', 'displayName', 'originId', 'principalName')] + [string]$SearchType = 'descriptor' + ) + + # Logging + Write-Verbose "[Find-Identity] Started." + Write-Verbose "[Find-Identity] Name: $Name" + Write-Verbose "[Find-Identity] Organization Name: $OrganizationName" + Write-Verbose "[Find-Identity] Search Type: $SearchType" + + try { + $CachedGroups = Get-CacheObject -CacheType 'LiveGroups' + $CachedUsers = Get-CacheObject -CacheType 'LiveUsers' + $CachedServicePrincipals = Get-CacheObject -CacheType 'LiveServicePrinciples' + } catch { + Write-Error "Failed to retrieve cache objects: $_" + return $null + } + + # + # Define the lookup table based on the search type + + switch ($SearchType) { + 'descriptor' { + $lookup = @{ + groupIdentitySB = { $_.value.ACLIdentity.descriptor -eq $Name } + userIdentitySB = { $_.value.ACLIdentity.descriptor -eq $Name } + servicePrincipalIdentitySB = { $_.value.ACLIdentity.descriptor -eq $Name } + } + } + 'descriptorId' { + $lookup = @{ + groupIdentitySB = { $_.value.ACLIdentity.id -eq $Name } + userIdentitySB = { $_.value.ACLIdentity.id -eq $Name } + servicePrincipalIdentitySB = { $_.value.ACLIdentity.id -eq $Name } + } + } + 'originId' { + $lookup = @{ + groupIdentitySB = { $_.value.originId -eq $Name } + userIdentitySB = { $_.value.originId -eq $Name } + servicePrincipalIdentitySB = { $_.value.originId -eq $Name } + } + } + 'principalName' { + $lookup = @{ + groupIdentitySB = { $_.value.principalName.replace('[','').replace(']','') -eq $Name } + userIdentitySB = { $_.value.principalName -eq $Name } + servicePrincipalIdentitySB = { $_.value.principalName -eq $Name } + } + } + 'displayName' { + $lookup = @{ + groupIdentitySB = { $_.value.displayName -eq $Name } + userIdentitySB = { $_.value.displayName -eq $Name } + servicePrincipalIdentitySB = { $_.value.displayName -eq $Name } + } + } + default { + Write-Error "Invalid SearchType: $SearchType" + return $null + } + } + + # + # Find the identity + + $groupIdentity = $CachedGroups | Where-Object $lookup.groupIdentitySB + $userIdentity = $CachedUsers | Where-Object $lookup.userIdentitySB + $servicePrincipalIdentity = $CachedServicePrincipals | Where-Object $lookup.servicePrincipalIdentitySB + + # Check if multiple identities were found. + if ($groupIdentity -or $userIdentity -or $servicePrincipalIdentity) { + if (($groupIdentity -and $userIdentity) -or ($groupIdentity -and $servicePrincipalIdentity) -or ($userIdentity -and $servicePrincipalIdentity)) { + Write-Warning "[Find-Identity] Found multiple identities with the name '$Name'. Returning null." + return $null + } + + if ($groupIdentity) { + Write-Verbose "[Find-Identity] Found group identity for '$Name'." + Write-Verbose "[Find-Identity] $SearchType" + return $groupIdentity + } elseif ($userIdentity) { + Write-Verbose "[Find-Identity] Found user identity for '$Name'." + return $userIdentity + } elseif ($servicePrincipalIdentity) { + Write-Verbose "[Find-Identity] Found service principal identity for '$Name'." + return $servicePrincipalIdentity + } + } else { + Write-Warning "[Find-Identity] No identity found for '$Name'. Performing a lookup via the API." + + # Perform a lookup via the API + $params = @{ + OrganizationName = $OrganizationName + Descriptor = $Name + } + + Write-Verbose "[Find-Identity] Performing a lookup via the API." + Write-Verbose "[Find-Identity] $SearchType" + + try { + # Get the identity + $identity = Get-DevOpsDescriptorIdentity @params + } catch { + Write-Error "Failed to retrieve identity via API: $_" + return $null + } + + # Attempt to match the identity using the ID + $groupIdentity = $CachedGroups | Where-Object { $_.value.ACLIdentity.id -eq $identity.id } + $userIdentity = $CachedUsers | Where-Object { $_.value.ACLIdentity.id -eq $identity.id } + $servicePrincipalIdentity = $CachedServicePrincipals | Where-Object { $_.value.ACLIdentity.id -eq $identity.id } + + # Test if the identity was found + if ($groupIdentity -or $userIdentity -or $servicePrincipalIdentity) { + + # Check if multiple identities were found. + if (($groupIdentity -and $userIdentity) -or ($groupIdentity -and $servicePrincipalIdentity) -or ($userIdentity -and $servicePrincipalIdentity)) { + Write-Warning "[Find-Identity] Found multiple identities with the ID '$($identity.id)'. Returning null." + return $null + } + + if ($groupIdentity) { + Write-Verbose "[Find-Identity] Found group identity for '$Name'." + return $groupIdentity + } elseif ($userIdentity) { + Write-Verbose "[Find-Identity] Found user identity for '$Name'." + return $userIdentity + } elseif ($servicePrincipalIdentity) { + Write-Verbose "[Find-Identity] Found service principal identity for '$Name'." + return $servicePrincipalIdentity + } + } + + # If no identity was found, write a warning and return null + Write-Warning "[Find-Identity] No identity found for '$Name'." + return $null + + } + + # Return null if no identity was found + return $null +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.ps1 new file mode 100644 index 000000000..d15898f7e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.ps1 @@ -0,0 +1,44 @@ +<# +.SYNOPSIS +Formats a user principal name by combining a prefix and a group name. + +.DESCRIPTION +The Format-AzDoGroup function takes a prefix and a group name as input parameters and returns a formatted user principal name. The user principal name is formatted as "[Prefix]\[GroupName]". + +.PARAMETER Prefix +The prefix to be used in the user principal name. + +.PARAMETER GroupName +The group name to be used in the user principal name. + +.EXAMPLE +Format-AzDoGroup -Prefix "Contoso" -GroupName "Developers" +Returns: "[Contoso]\Developers" + +#> + +Function Format-AzDoGroup { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [Alias('ProjectName', 'Organization')] + [string] + $Prefix, + + [Parameter(Mandatory)] + [String] + $GroupName + ) + + # If the prefix contains starting or ending square brackets, remove them. + $Prefix = $Prefix -replace '^\[|\]$', '' + + # Build the User Principal Name string + $userPrincipalName = '[{0}]\{1}' -f $Prefix, $GroupName + + # Use a verbose statement to show the input and resulting formatted UPN + Write-Verbose "[Format-AzDoGroup] Formatting User Principal Name with Prefix: '$Prefix' and GroupName: '$GroupName'." + Write-Verbose "[Format-AzDoGroup] Resulting User Principal Name: '$userPrincipalName'." + + return $userPrincipalName +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroupMember.ps1 new file mode 100644 index 000000000..236d7e061 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroupMember.ps1 @@ -0,0 +1,18 @@ +Function Format-AzDoGroupMember +{ + param( + [Parameter(Mandatory)] + [System.String]$GroupName + ) + + # If the group name contains starting or ending square brackets, remove them. + $GroupName = $GroupName -replace '^\[|\]', '' + + # Build the GroupName string + + # Split the GroupName into the prefix and the group name. + $prefix, $group = $GroupName -split '\\' + + return "[{0}]\{1}" -f $prefix, $group + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.ps1 new file mode 100644 index 000000000..55248da33 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.ps1 @@ -0,0 +1,47 @@ +Function Format-AzDoProjectName { + [CmdletBinding()] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter(Mandatory)] + [Alias('Organization')] + [System.String]$OrganizationName + ) + + # Logging + Write-Verbose "[Format-AzDoProjectName] Formatting GroupName." + + # If the GroupName contains [Project|Organization]\GroupName, it's in the correct format. + if ($GroupName -match '^\[.*\]\\.*$') { + return $GroupName + } + + # Split the group name with a '\' or '/' and create an array. + $splitGroupName = $GroupName -split '\\|\/' + + # There must be at least 2 elements in the array. The first element is the project name and the second element is the group name. + if ($splitGroupName.Length -lt 2) { + Throw "The GroupName '$GroupName' is not in the correct format. The GroupName must be in the format 'ProjectName\GroupName' or 'Project/GroupName." + } + + # If the first element contains '%ORG%' or is empty, replace it with the organization name. + if ($splitGroupName[0] -eq '%ORG%' -or [String]::IsNullOrEmpty($splitGroupName[0].Trim())) { + $splitGroupName[0] = $OrganizationName + # If the first element contains '%TFS%', replace it with 'TEAM FOUNDATION'. + } elseif ($splitGroupName[0].Trim() -eq '%TFS%') { + $splitGroupName[0] = 'TEAM FOUNDATION' + } + + # The group name cannot be null. + if ([String]::IsNullOrEmpty($splitGroupName[1].Trim())) { + Throw "The GroupName '$GroupName' is not in the correct format. The GroupName must be in the format 'ProjectName\GroupName'." + } + + # Format the group name with the organization name. + $formattedGroupName = '[{0}]\{1}' -f $splitGroupName[0].Trim(), $splitGroupName[1].Trim() + + return $formattedGroupName +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.ps1 new file mode 100644 index 000000000..0909fcb55 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.ps1 @@ -0,0 +1,24 @@ +Function Format-DescriptorType { + + [CmdletBinding()] + [OutputType([String])] + param ( + [Parameter(Mandatory)] + [System.String]$DescriptorType + ) + + # Switch on the DescriptorType + switch ($DescriptorType) { + + # The Descriptor Name in the API is different to the Descriptor Name in the DSC Resource. + "GitRepositories" { + return "Git Repositories" + } + + # All other else, keep the same descriptor type + default { + return $DescriptorType + } + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiHttpRequestHeader.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiHttpRequestHeader.ps1 similarity index 100% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiHttpRequestHeader.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiHttpRequestHeader.ps1 diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResourceName.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceName.ps1 similarity index 100% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResourceName.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceName.ps1 diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResourceUri.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceUri.ps1 similarity index 100% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResourceUri.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceUri.ps1 diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriAreaName.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriAreaName.ps1 similarity index 100% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriAreaName.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriAreaName.ps1 diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriResourceName.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriResourceName.ps1 similarity index 100% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriResourceName.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriResourceName.ps1 diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.ps1 similarity index 84% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.ps1 index f867af536..58e832890 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.ps1 @@ -19,7 +19,7 @@ function Get-AzDevOpsApiVersion $Default ) - [string]$defaultApiVersion = '6.1' + [string]$defaultApiVersion = '7.0-preview.1' [string[]]$apiVersions = @( @@ -27,7 +27,10 @@ function Get-AzDevOpsApiVersion #'5.0', # Not supported #'5.1', # Not supported '6.0', - '6.1' + '7.0-preview.1', + '7.1-preview.1', + '7.1-preview.4', + '7.2-preview.4' ) diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitIntervalMs.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitIntervalMs.ps1 similarity index 100% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitIntervalMs.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitIntervalMs.ps1 diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitTimeoutMs.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitTimeoutMs.ps1 similarity index 100% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitTimeoutMs.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitTimeoutMs.ps1 diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.ps1 new file mode 100644 index 000000000..ff3398e3b --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.ps1 @@ -0,0 +1,19 @@ +function Get-AzDoCacheObjects +{ + return @( + 'Project', + 'Team', + 'Group', + 'SecurityDescriptor', + 'LiveGroups', + 'LiveProjects', + 'LiveUsers', + 'LiveGroupMembers', + 'LiveRepositories', + 'LiveServicePrinciples', + 'LiveACLList', + 'LiveProcesses', + 'SecurityNamespaces' + ) + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 new file mode 100644 index 000000000..a30fb82f1 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 @@ -0,0 +1,261 @@ +<# + .SYNOPSIS + This is a light, generic, wrapper proceedure around 'Invoke-RestMethod' to handle + multiple retries and error/exception handling. + + This function makes no assumptions around the versions of the API used, the resource + being operated/actioned upon, the operation/method being performed, nor the content + of the HTTP headers and body. + + .PARAMETER ApiUri + The URI of the Azure DevOps API to be connected to. For example: + + https://dev.azure.com/someOrganizationName/_apis/ + + .PARAMETER HttpMethod + The HTTP method being used in the HTTP/REST request sent to the Azure DevOps API. + + .PARAMETER HttpHeaders + The headers for the HTTP/REST request sent to the Azure DevOps API. + + .PARAMETER HttpBody + The body for the HTTP/REST request sent to the Azure DevOps API. If performing a 'Post', + 'Put' or 'Patch' method/request, this will typically contain the JSON document of the resource. + + .PARAMETER RetryAttempts + The number of times the method/request will attempt to be resent/retried if unsuccessful on the + initial attempt. + + If any attempt is successful, the remaining attempts are ignored. + + .PARAMETER RetryIntervalMs + The interval (in Milliseconds) between retry attempts. + + .EXAMPLE + Invoke-AzDevOpsApiRestMethod -ApiUri 'YourApiUriHere' -HttpMethod 'Get' -HttpHeaders $YouHttpHeadersHashtableHere + + Submits a 'Get' request to the Azure DevOps API (relying on the 'ApiUri' value to determine what is being retrieved). + + .EXAMPLE + Invoke-AzDevOpsApiRestMethod -ApiUri 'YourApiUriHere' -HttpMethod 'Patch' -HttpHeaders $YourHttpHeadersHashtableHere ` + -HttpBody $YourHttpBodyHere -RetryAttempts 3 + + Submits a 'Patch' request to the Azure DevOps API with the supplied 'HttpBody' and will attempt to retry 3 times (4 in + total, including the intitial attempt) if unsuccessful. +#> +function Invoke-AzDevOpsApiRestMethod +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject])] + param + ( + [Parameter(Mandatory=$true)] + [Alias('Uri')] + [System.String] + $ApiUri, + + [Parameter(Mandatory=$true)] + [ValidateSet('Get','Post','Patch','Put','Delete')] + [System.String] + [Alias('Method')] + $HttpMethod, + + [Parameter()] + [ValidateScript( { Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $_ -IsValid })] + [Hashtable] + [Alias('Headers','HttpRequestHeader')] + $HttpHeaders=@{}, + + [Parameter()] + [System.String] + [Alias('Body')] + $HttpBody, + + [Parameter()] + [System.String] + [Alias('ContentType')] + [ValidateSet('application/json','application/json-patch+json')] + $HttpContentType = 'application/json', + + [Parameter()] + [ValidateRange(0,5)] + [Int32] + $RetryAttempts = 5, + + [Parameter()] + [ValidateRange(250,10000)] + [Int32] + $RetryIntervalMs = 250, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default), + + [Parameter()] + [Switch] + $NoAuthentication, + + [Parameter()] + [Switch] + $AzureArcAuthentication + + ) + + $invokeRestMethodParameters = @{ + Uri = $ApiUri + Method = $HttpMethod + Headers = $HttpHeaders + Body = $HttpBody + ContentType = $HttpContentType + ResponseHeadersVariable = 'responseHeaders' + } + + Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] Invoking the Azure DevOps API REST method '{0}'." -f $HttpMethod) + Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] API URI: {0}" -f $ApiUri) + + # Remove the 'Body' and 'ContentType' if not relevant to request + if ($HttpMethod -in $('Get','Delete')) + { + $invokeRestMethodParameters.Remove('Body') + $invokeRestMethodParameters.Remove('ContentType') + } + + # Intially set this value to -1, as the first attempt does not want to be classed as a "RetryAttempt" + $CurrentNoOfRetryAttempts = -1 + # Set the Continuation Token to be False + $isContinuationToken = $false + $results = [System.Collections.ArrayList]::new() + + while ($CurrentNoOfRetryAttempts -lt $RetryAttempts) + { + + # + # Slow down the retry attempts if the API resource is close to being overwelmed + + # If there are any retry attempts, wait for the specified number of seconds before retrying + if (($null -ne $Global:DSCAZDO_APIRateLimit.xRateLimitRemaining) -and ($Global:DSCAZDO_APIRateLimit.retryAfter -ge 0)) + { + Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] Waiting for {0} seconds before retrying." -f $Global:DSCAZDO_APIRateLimit.retryAfter) + Start-Sleep -Seconds $Global:DSCAZDO_APIRateLimit.retryAfter + } + + # If the API resouce is close to beig overwelmed, wait for the specified number of seconds before sending the request + if (($null -ne $Global:DSCAZDO_APIRateLimit.xRateLimitRemaining) -and ($Global:DSCAZDO_APIRateLimit.xRateLimitRemaining -le 50) -and ($Global:DSCAZDO_APIRateLimit.xRateLimitRemaining -ge 5)) + { + Write-Verbose -Message "[Invoke-AzDevOpsApiRestMethod] Resource is close to being overwelmed. Waiting for $RetryIntervalMs seconds before sending the request." + Start-Sleep -Milliseconds $RetryIntervalMs + } + # If the API resouce is overwelmed, wait for the specified number of seconds before sending the request + elseif (($null -ne $Global:DSCAZDO_APIRateLimit.xRateLimitRemaining) -and ($Global:DSCAZDO_APIRateLimit.xRateLimitRemaining -lt 5)) + { + Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] Resource is overwhelmed. Waiting for {0} seconds to reset the TSTUs." -f $Global:DSCAZDO_APIRateLimit.xRateLimitReset) + Start-Sleep -Milliseconds $RetryIntervalMs + } + + # + # Invoke the REST method. Loop until the Continuation Token is False. + + Do { + + # + # Add the Authentication Header + + # If the 'NoAuthentication' switch is NOT PRESENT and the 'Authentication' header is empty, add the authentication header + if (([String]::IsNullOrEmpty($invokeRestMethodParameters.Headers.Authentication)) -and (-not $NoAuthentication.IsPresent)) + { + $invokeRestMethodParameters.Headers.Authorization = Add-AuthenticationHTTPHeader + } + + # + # Invoke the REST method + + try + { + # Invoke the REST method. If the 'Verbose' switch is present, set it to $false. + # This is to prevent the output from being displayed in the console. + $response = Invoke-RestMethod @invokeRestMethodParameters -Verbose:$false + + # Zero out the 'Authorization' header + $invokeRestMethodParameters.Headers.Authorization = $null + # Add the response to the results array + $null = $results.Add($response) + + # + # Test to see if there is no continuation token + + if ([String]::IsNullOrEmpty($responseHeaders.'x-ms-continuationtoken')) + { + # If not, set the continuation token to False + $isContinuationToken = $false + # Update the Rate Limit information + $Global:DSCAZDO_APIRateLimit = $null + + Write-Verbose "[Invoke-AzDevOpsApiRestMethod] No continuation token found. Breaking loop." + + return $results + + } + + # + # A continuation token is present. + + # If so, set the continuation token to True + $isContinuationToken = $true + # Update the URI to include the continuation token + $invokeRestMethodParameters.Uri = '{0}&continuationToken={1}&{2}' -f $ApiUri, $responseHeaders.'x-ms-continuationtoken', $ApiVersion + # Reset the RetryAttempts counter + $CurrentNoOfRetryAttempts = -1 + + } + catch + { + + # If AzureArcAuthentication is present, then we need to handle the error differently. + # Stop and Pass the error back to the caller. The caller will handle the error. + if ($AzureArcAuthentication.IsPresent) + { + throw $_ + } + + # Zero out the 'Authorization' header + $invokeRestMethodParameters.Headers.Authorization = $null + # Check to see if it is an HTTP 429 (Too Many Requests) error + if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::TooManyRequests) + { + # If so, wait for the specified number of seconds before retrying + $retryAfter = $_.Exception.Response.Headers['Retry-After'] + + if ($retryAfter) + { + $retryAfter = [int]$retryAfter + Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $retryAfter seconds before retrying." + $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($retryAfter) + } else { + # If the Retry-After header is not present, wait for the specified number of milliseconds before retrying + Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $RetryIntervalMs milliseconds before retrying." + $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($RetryIntervalMs) + } + + } + + # Increment the number of retries attempted and obtain any exception message + $CurrentNoOfRetryAttempts++ + $restMethodExceptionMessage = $_.Exception.Message + + # Wait before the next attempt/retry + Start-Sleep -Milliseconds $RetryIntervalMs + + # Break the continuation token loop so that the next attempt can be made + break; + + } + + } Until (-not $isContinuationToken) + + } + + # If all retry attempts have failed, throw an exception + $errorMessage = $script:localizedData.AzDevOpsApiRestMethodException -f $MyInvocation.MyCommand, $RetryAttempts, $restMethodExceptionMessage + New-InvalidOperationException -Message $errorMessage -Throw + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Flush-Log.txt b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Flush-Log.txt new file mode 100644 index 000000000..4d6507145 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Flush-Log.txt @@ -0,0 +1,36 @@ +Function Flush-Log +{ + [CmdletBinding()] + param + () + + Write-Verbose "[Flush-Log] Started." + + # Write the log messages to the log files + Write-Verbose "[Flush-Log] Writing log messages to log files." + $LogFilePaths = @( + $Global:AZDO_LogSettings.VerboseLogFilePath + $Global:AZDO_LogSettings.WarningLogFilePath + $Global:AZDO_LogSettings.ErrorLogFilePath + ) + + $LogMessages = @( + $Global:AZDO_VerboseLog + $Global:AZDO_WarningLog + $Global:AZDO_ErrorLog + ) + + for ($i = 0; $i -lt $LogFilePaths.Count; $i++) + { + $LogFilePath = $LogFilePaths[$i] + $LogMessages = $LogMessages[$i] + + $LogMessages | Out-File -FilePath $LogFilePath -Append + + # Clear the log messages + $LogMessages.Clear() + $Global:AZDO_LogSettings."$($LogFilePath.Split('.')[-2])Count" = 0 + } + + Write-Verbose "[Flush-Log] Log messages written to log files." +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt new file mode 100644 index 000000000..7b14711a7 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt @@ -0,0 +1,31 @@ +Function Initialize-Log { + [CmdletBinding()] + param ( + # The directory where the log files will be stored. + [Parameter()] + [string]$LogDirectory=$ENV:AZDODSC_CACHE_DIRECTORY + ) + + Write-Verbose "[Initialize-Log] Started." + + # Define the log path + $Global:AZDO_VerboseLog = [System.Collections.Generic.List[String]]::new() + $Global:AZDO_WarningLog = [System.Collections.Generic.List[String]]::new() + + $Global:AZDO_LogSettings = @{ + VerboseLogFilePath = Join-Path -Path $LogDirectory -ChildPath "Verbose.log" + WarningLogFilePath = Join-Path -Path $LogDirectory -ChildPath "Warning.log" + VerboseCount = 0 + WarningCount = 0 + LogCountLimit = 100 + } + + # Ensure the log directory exists + if (-not (Test-Path -Path $LogDirectory)) { + Write-Verbose "[Initialize-Log] Log directory does not exist. Creating directory." + New-Item -ItemType Directory -Path $LogDirectory -Force | Out-Null + } + + # Initialize the log files + Write-Verbose "[Initialize-Log] Log files initialized at: $LogDirectory" +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 new file mode 100644 index 000000000..7778dc39a --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 @@ -0,0 +1,19 @@ +Function Write-Error { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Message, + + [Parameter()] + [string]$LogFilePath = "C:\Temp\error_log.txt" + ) + + # Call the original Write-Error cmdlet to display the message + Microsoft.PowerShell.Utility\Write-Error $Message + $VerbosePreference = $originalPreference + + # Append the message to the log file + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Add-Content -Path $LogFilePath -Value "[$timestamp] $Message" + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 new file mode 100644 index 000000000..399fa23ae --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 @@ -0,0 +1,21 @@ +Function Write-Verbose { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Message, + + [Parameter()] + [string]$LogFilePath = "$($env:AZDO_VERBOSELOGGING_FILEPATH)" + ) + + # Call the original Write-Verbose cmdlet to display the message if verbose preference is enabled + Microsoft.PowerShell.Utility\Write-Verbose $Message + + # Test if the env:enableVerboseLogging variable is set to true + if ($null -ne $env:AZDO_VERBOSELOGGING_FILEPATH) { + # Append the message to the log file + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Add-Content -Path $LogFilePath -Value "[$timestamp] $Message" + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 new file mode 100644 index 000000000..d5e37b13e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 @@ -0,0 +1,20 @@ +Function Write-Warning { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Message, + + [Parameter()] + [string]$LogFilePath = "C:\Temp\warning_log.txt" + ) + + # Call the original Write-Verbose cmdlet to display the message if verbose preference is enabled + $originalPreference = $VerbosePreference + $VerbosePreference = 'Continue' + Microsoft.PowerShell.Utility\Write-Warning $Message + $VerbosePreference = $originalPreference + + # Append the message to the log file + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Add-Content -Path $LogFilePath -Value "[$timestamp] $Message" +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt new file mode 100644 index 000000000..1918a6a3b --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt @@ -0,0 +1,51 @@ +Function Write-Log +{ + [CmdletBinding()] + param + ( + # The message to be logged. + [Parameter(Mandatory)] + [string]$Message, + + # The type of log message. + [Parameter(Mandatory)] + [ValidateSet('Verbose', 'Warning')] + [string]$Type + ) + + Write-Verbose "[Write-Log] Started." + + # Get the current date and time + $DateTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + + # Construct the log message + $LogMessage = "[$DateTime] $Message" + + # Write the log message to the appropriate log file + switch ($Type) + { + 'Verbose' { $Global:AZDO_VerboseLog.Add($LogMessage) } + 'Warning' { $Global:AZDO_WarningLog.Add($LogMessage) } + 'Error' { $Global:AZDO_ErrorLog.Add($LogMessage) } + } + + # Increment the log count + $Global:AZDO_LogSettings."$Type"Count++ + + # Check if the log count limit has been reached + if ($Global:AZDO_LogSettings."$Type"Count -ge $Global:AZDO_LogSettings.LogCountLimit) + { + # Write the log messages to the log file + Write-Verbose "[Write-Log] Writing log messages to log file." + $LogFilePath = $Global:AZDO_LogSettings."$Type"LogFilePath + $LogMessages = $Global:AZDO_"$Type"Log + $LogMessages | Out-File -FilePath $LogFilePath -Append + + # Clear the log messages + $Global:AZDO_"$Type"Log = [System.Collections.Generic.List[String]]::new() + $Global:AZDO_LogSettings."$Type"Count = 0 + } + + Write-Verbose "[Write-Log] Log message written to log file." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.ps1 new file mode 100644 index 000000000..fd53be055 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.ps1 @@ -0,0 +1,52 @@ +<# +.SYNOPSIS +Creates a token for Azure DevOps access control. + +.DESCRIPTION +The New-AzDevOpsACLToken function creates a token for Azure DevOps access control. +The token can be used to grant access at either the project level or the team level. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization. + +.PARAMETER ProjectId +The ID of the Azure DevOps project. + +.PARAMETER TeamId +The ID of the Azure DevOps team. If not specified, the token will be created for project-level access. + +.EXAMPLE +New-AzDevOpsACLToken -OrganizationName "Contoso" -ProjectId "MyProject" -TeamId "MyTeam" +Creates a token for team-level access to the specified Azure DevOps project and team. + +.EXAMPLE +New-AzDevOpsACLToken -OrganizationName "Contoso" -ProjectId "MyProject" +Creates a token for project-level access to the specified Azure DevOps project. +#> + +function New-AzDevOpsACLToken { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$OrganizationName, + + [Parameter(Mandatory)] + [string]$ProjectId, + + [Parameter()] + [string]$TeamId + ) + + process { + if ($TeamId) { + # Construct a token for team-level access + $token = "vstfs:///Classification/TeamProject/$ProjectId/$TeamId" + } else { + # Construct a token for project-level access + $token = "vstfs:///Classification/TeamProject/$ProjectId" + } + + return $token + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.ps1 new file mode 100644 index 000000000..810b3691d --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.ps1 @@ -0,0 +1,37 @@ +using namespace System.Management.Automation + +function New-InvalidOperationException { + [CmdletBinding()] + [OutputType([System.Management.Automation.ErrorRecord])] + + ## PARAMETERS ############################################################# + param( + [Parameter( + Position = 0, + Mandatory + )] + [ValidateNotNullOrEmpty()] + [string] + $Message, + + [Parameter()] + [switch] + $Throw + ) + + ## PROCESS ################################################################ + process { + $ErrorRecord = [ErrorRecord]::new( + [InvalidOperationException]::new($Message), + "System.InvalidOperationException", + [ErrorCategory]::ConnectionError, + $null + ) + + if ($Throw) { + throw $ErrorRecord + } + + Write-Output $ErrorRecord + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.ps1 new file mode 100644 index 000000000..fe89f743e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.ps1 @@ -0,0 +1,30 @@ +<# +$ExecutionContext.InvokeCommand.PreCommandLookupAction = { + param($command, $commandScriptBlock) + + # + # If the Command is 'Add-AuthenticationHTTPHeader' and is called outside of 'Invoke-AzDevOpsApiRestMethod' function + + if ($command -eq 'Add-AuthenticationHTTPHeader' -and $MyInvocation.MyCommand.Name -ne 'Invoke-AzDevOpsApiRestMethod') + { + throw "The function 'Add-AuthenticationHTTPHeader' can only be called inside of 'Invoke-AzDevOpsApiRestMethod' function." + } + + # + # Any export function used within Invoke-AzDevOpsApiRestMethod is not allowed. + + if ($command -match 'Export-') + { + throw "The command '$command' is not allowed to be used within 'Invoke-AzDevOpsApiRestMethod' function." + } + + # + # Any attempt of using [System.Runtime.InteropServices.Marshal] outside of 'AuthenticationToken' class is not allowed. + + if ($command -match 'System.Runtime.InteropServices.Marshal' -and $MyInvocation.MyCommand.Name -ne 'AuthenticationToken') + { + throw "The command '$command' is not allowed to be used outside of 'AuthenticationToken' class." + } + +} +#> diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 new file mode 100644 index 000000000..184b8c455 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 @@ -0,0 +1,21 @@ +Function New-Thread { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ScriptBlock]$ScriptBlock + ) + + # + # Logging + Write-Verbose "[New-Thread] Started." + + # + # Create a new thread + $thread = [System.Threading.Thread]::new($ScriptBlock) + $thread.Start() + + # + # Return the thread + Write-Verbose "[New-Thread] Completed." + return $thread +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.ps1 deleted file mode 100644 index 2f1d458bd..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.ps1 +++ /dev/null @@ -1,199 +0,0 @@ -<# - .SYNOPSIS - This is a light, generic, wrapper proceedure around 'Invoke-RestMethod' to handle - multiple retries and error/exception handling. - - This function makes no assumptions around the versions of the API used, the resource - being operated/actioned upon, the operation/method being performed, nor the content - of the HTTP headers and body. - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER HttpMethod - The HTTP method being used in the HTTP/REST request sent to the Azure DevOps API. - - .PARAMETER HttpHeaders - The headers for the HTTP/REST request sent to the Azure DevOps API. - - .PARAMETER HttpBody - The body for the HTTP/REST request sent to the Azure DevOps API. If performing a 'Post', - 'Put' or 'Patch' method/request, this will typically contain the JSON document of the resource. - - .PARAMETER RetryAttempts - The number of times the method/request will attempt to be resent/retried if unsuccessful on the - initial attempt. - - If any attempt is successful, the remaining attempts are ignored. - - .PARAMETER RetryIntervalMs - The interval (in Milliseconds) between retry attempts. - - .EXAMPLE - Invoke-AzDevOpsApiRestMethod -ApiUri 'YourApiUriHere' -HttpMethod 'Get' -HttpHeaders $YouHttpHeadersHashtableHere - - Submits a 'Get' request to the Azure DevOps API (relying on the 'ApiUri' value to determine what is being retrieved). - - .EXAMPLE - Invoke-AzDevOpsApiRestMethod -ApiUri 'YourApiUriHere' -HttpMethod 'Patch' -HttpHeaders $YourHttpHeadersHashtableHere ` - -HttpBody $YourHttpBodyHere -RetryAttempts 3 - - Submits a 'Patch' request to the Azure DevOps API with the supplied 'HttpBody' and will attempt to retry 3 times (4 in - total, including the intitial attempt) if unsuccessful. -#> -function Invoke-AzDevOpsApiRestMethod -{ - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject])] - param - ( - [Parameter(Mandatory=$true)] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter(Mandatory=$true)] - [ValidateSet('Get','Post','Patch','Put','Delete')] - [System.String] - [Alias('Method')] - $HttpMethod, - - [Parameter(Mandatory=$true)] - [ValidateScript( { Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $_ -IsValid })] - [Hashtable] - [Alias('Headers','HttpRequestHeader')] - $HttpHeaders, - - [Parameter()] - [System.String] - [Alias('Body')] - $HttpBody, - - [Parameter()] - [System.String] - [Alias('ContentType')] - [ValidateSet('application/json')] - $HttpContentType = 'application/json', - - [Parameter()] - [ValidateRange(0,5)] - [Int32] - $RetryAttempts = 5, - - [Parameter()] - [ValidateRange(250,10000)] - [Int32] - $RetryIntervalMs = 250 - ) - - $invokeRestMethodParameters = @{ - Uri = $ApiUri - Method = $HttpMethod - Headers = $HttpHeaders - Body = $HttpBody - ContentType = $HttpContentType - ResponseHeadersVariable = 'responseHeaders' - } - - # Remove the 'Body' and 'ContentType' if not relevant to request - if ($HttpMethod -in $('Get','Delete')) - { - $invokeRestMethodParameters.Remove('Body') - $invokeRestMethodParameters.Remove('ContentType') - } - - # Intially set this value to -1, as the first attempt does not want to be classed as a "RetryAttempt" - $CurrentNoOfRetryAttempts = -1 - - while ($CurrentNoOfRetryAttempts -lt $RetryAttempts) - { - - # - # Slow down the retry attempts if the API resource is close to being overwelmed - - # If there are any retry attempts, wait for the specified number of seconds before retrying - if ($Global:DSCAZDO_APIRateLimit.retryAfter -ge 0) - { - Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] Waiting for {0} seconds before retrying." -f $Global:DSCAZDO_APIRateLimit.retryAfter) - Start-Sleep -Seconds $Global:DSCAZDO_APIRateLimit.retryAfter - } - - # If the API resouce is close to beig overwelmed, wait for the specified number of seconds before sending the request - if (($Global:DSCAZDO_APIRateLimit.xRateLimitRemaining -le 50) -and ($Global:DSCAZDO_APIRateLimit.xRateLimitRemaining -ge 5)) - { - Write-Verbose -Message "[Invoke-AzDevOpsApiRestMethod] Resource is close to being overwelmed. Waiting for $RetryIntervalMs seconds before sending the request." - Start-Sleep -Milliseconds $RetryIntervalMs - } - # If the API resouce is overwelmed, wait for the specified number of seconds before sending the request - elseif ($Global:DSCAZDO_APIRateLimit.xRateLimitRemaining -lt 5) - { - Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] Resource is overwelmed. Waiting for {0} seconds to reset the TSTUs." -f $Global:DSCAZDO_APIRateLimit.xRateLimitReset) - Start-Sleep -Milliseconds $RetryIntervalMs - } - - # - # Test if a Managed Identity Token is required and if so, add it to the HTTP Headers - if ($Global:DSCAZDO_ManagedIdentityToken -ne $null) - { - # Test if the Managed Identity Token has expired - if ($Global:DSCAZDO_ManagedIdentityToken.isExpired()) - { - # If so, get a new token - $Global:DSCAZDO_ManagedIdentityToken = Update-AzManagedIdentityToken -OrganizationName $Global:DSCAZDO_OrganizationName - } - - # Add the Managed Identity Token to the HTTP Headers - $invokeRestMethodParameters.Headers.Authorization = 'Bearer {0}' -f $Global:DSCAZDO_ManagedIdentityToken.Get() - } - - - # - # Invoke the REST method - - try - { - $result = Invoke-RestMethod @invokeRestMethodParameters - - # Update - $Global:DSCAZDO_APIRateLimit = $null - return $result - - } - catch - { - - # Check to see if it is an HTTP 429 (Too Many Requests) error - if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::TooManyRequests) - { - # If so, wait for the specified number of seconds before retrying - $retryAfter = $_.Exception.Response.Headers.'Retry-After' - if ($retryAfter) - { - $retryAfter = [int]$retryAfter - Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $retryAfter seconds before retrying." - $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($_.Exception.Response.Headers) - } else { - # If the Retry-After header is not present, wait for the specified number of milliseconds before retrying - Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $RetryIntervalMs milliseconds before retrying." - $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($RetryIntervalMs) - } - - } - - # Increment the number of retries attempted and obtain any exception message - $CurrentNoOfRetryAttempts++ - $restMethodExceptionMessage = $_.Exception.Message - - # Wait before the next attempt/retry - Start-Sleep -Milliseconds $RetryIntervalMs - } - } - - - # If all retry attempts have failed, throw an exception - $errorMessage = $script:localizedData.AzDevOpsApiRestMethodException -f $MyInvocation.MyCommand, $RetryAttempts, $restMethodExceptionMessage - New-InvalidOperationException -Message $errorMessage - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/New-AzDevOpsApiResource.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/New-AzDevOpsApiResource.ps1 deleted file mode 100644 index 6a1a36308..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/New-AzDevOpsApiResource.ps1 +++ /dev/null @@ -1,115 +0,0 @@ -<# - .SYNOPSIS - Attempts to create an resource within Azure DevOps. - - The type of resource type created is provided in the 'ResourceName' parameter and it is - assumed that the 'Resource' parameter value passed in meets the specification of the resource. - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER ApiVersion - The version of the Azure DevOps API to use in the call/execution to/against the API. - - .PARAMETER Pat - The 'Personal Access Token' (PAT) to be used by any subsequent requests/operations - against the Azure DevOps API. This PAT must have the relevant permissions assigned - for the subsequent operations being performed. - - .PARAMETER ResourceName - The name of the resource being created within Azure DevOps (e.g. 'Project') - - .PARAMETER Resource - The resource being created (typically provided by another function (e.g. 'New-AzDevOpsApiProject')). - - .EXAMPLE - New-AzDevOpsApiResource -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ResourceName 'Project' -Resource $YourResource -Wait - - Creates the 'Project' resource in Azure DevOps within to the Organization relating to the to the 'ApiUri' - provided. - - NOTE: In this example, the '-Wait' switch is provided so the function will wait for the corresponding API 'Operation' - to complete before the function completes. No return value is provided by this function and if the creation of the - resource has failed, an exception will be thrown. - - .EXAMPLE - New-AzDevOpsApiResource -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ResourceName 'Project' -Resource $YourResource - - Creates the 'Project' resource in Azure DevOps within to the Organization relating to the to the 'ApiUri' - provided. - - NOTE: In this example, no '-Wait' switch is provided so the request is made to the API but the operation may - not complete before the function completes (and may not complete successfully at all). -#> -function New-AzDevOpsApiResource -{ - [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] - [OutputType([System.Object])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateScript( { Test-AzDevOpsApiUri -ApiUri $_ -IsValid })] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter()] - [ValidateScript( { Test-AzDevOpsApiVersion -ApiVersion $_ -IsValid })] - [System.String] - $ApiVersion = $(Get-AzDevOpsApiVersion -Default), - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsPat -Pat $_ -IsValid })] - [Alias('PersonalAccessToken')] - [System.String] - $Pat, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsApiResourceName -ResourceName $_ -IsValid })] - [System.String] - $ResourceName, - - [Parameter(Mandatory = $true)] - [System.Object] - $Resource, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Wait, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - if ($Force -or $PSCmdlet.ShouldProcess($apiResourceUri, $ResourceName)) - { - $apiResourceUriParameters = @{ - ApiUri = $ApiUri - ApiVersion = $ApiVersion - ResourceName = $ResourceName - } - - [string]$apiResourceUri = Get-AzDevOpsApiResourceUri @apiResourceUriParameters - [Hashtable]$apiHttpRequestHeader = Get-AzDevOpsApiHttpRequestHeader -Pat $Pat - [string]$apiHttpRequestBody = $Resource | ConvertTo-Json -Depth 10 -Compress - - [System.Object]$apiOperation = $null - [System.Object]$apiOperation = Invoke-AzDevOpsApiRestMethod -Uri $apiResourceUri -Method 'Post' ` - -Headers $apiHttpRequestHeader -Body $apiHttpRequestBody ` - -ContentType 'application/json' - - if ($Wait) - { - # Waits for operation to complete successfully. Throws exception if operation is not successful and/or timeout is reached. - Wait-AzDevOpsOperation -ApiUri $ApiUri -Pat $Pat ` - -OperationId $apiOperation.id ` - -IsSuccessful - - # Adds an additional, post-operation delay/buffer to mitigate subsequent calls trying to obtain new/updated items too quickly from the API - Start-Sleep -Milliseconds $(Get-AzDevOpsApiWaitIntervalMs) - } - } -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Remove-AzDevOpsApiResource.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Remove-AzDevOpsApiResource.ps1 deleted file mode 100644 index b503e19ca..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Remove-AzDevOpsApiResource.ps1 +++ /dev/null @@ -1,122 +0,0 @@ -<# - .SYNOPSIS - Attempts to remove a resource within Azure DevOps. - - The type of resource type removed is provided in the 'ResourceName' parameter and it is - assumed that the 'ResourceId' parameter value passed in is present (in order to be deleted). - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER ApiVersion - The version of the Azure DevOps API to use. Defaults to value suppored by the module. - - .PARAMETER Pat - The 'Personal Access Token' (PAT) to be used by any subsequent requests/operations - against the Azure DevOps API. This PAT must have the relevant permissions assigned - for the subsequent operations being performed. - - .PARAMETER ResourceName - The name of the resource being deleted within Azure DevOps (e.g. 'Project') - - .PARAMETER ResourceId - The 'ResourceId' of the resource being created (typically provided by another function (e.g. 'Remove-AzDevOpsApiProject')). - - .PARAMETER Wait - Using this switch ensures that the execution will run synchronously and wait for the resource to be removed before - continuing. By not using this switch, execution will run asynchronously. - - .PARAMETER Force - Using this switch will override any confirmations prior to the deletion/removal of the resource. - - .EXAMPLE - Remove-AzDevOpsApiResource -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ResourceName 'Project' -ResourceId $YourResourceId -Wait - - Removes/Deletes the 'Project' resource in Azure DevOps within to the Organization relating to the to the 'ApiUri' - provided. - - NOTE: In this example, the '-Wait' switch is provided so the function will wait for the corresponding API 'Operation' - to complete before the function completes. No return value is provided by this function and if the creation of the - resource has failed, an exception will be thrown. - - .EXAMPLE - New-AzDevOpsApiResource -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ResourceName 'Project' -ResourceId $YourResourceId - - Remmoves/Deletes the 'Project' resource in Azure DevOps within to the Organization relating to the to the 'ApiUri' - provided. - - NOTE: In this example, no '-Wait' switch is provided so the request is made to the API but the operation may - not complete before the function completes (and may not complete successfully at all). -#> -function Remove-AzDevOpsApiResource -{ - [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] - [OutputType([System.Object])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateScript( { Test-AzDevOpsApiUri -ApiUri $_ -IsValid })] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter()] - [ValidateScript( { Test-AzDevOpsApiVersion -ApiVersion $_ -IsValid })] - [System.String] - $ApiVersion = $(Get-AzDevOpsApiVersion -Default), - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsPat -Pat $_ -IsValid })] - [Alias('PersonalAccessToken')] - [System.String] - $Pat, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsApiResourceName -ResourceName $_ -IsValid })] - [System.String] - $ResourceName, - - [Parameter(Mandatory = $true)] - [System.Object] - [ValidateScript({ Test-AzDevOpsApiResourceId -ResourceId $_ -IsValid })] - $ResourceId, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Wait, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - if ($Force -or $PSCmdlet.ShouldProcess($apiResourceUri, $ResourceName)) - { - $apiResourceUriParameters = @{ - ApiUri = $ApiUri - ApiVersion = $ApiVersion - ResourceName = $ResourceName - ResourceId = $ResourceId - } - - [string]$apiResourceUri = Get-AzDevOpsApiResourceUri @apiResourceUriParameters - [Hashtable]$apiHttpRequestHeader = Get-AzDevOpsApiHttpRequestHeader -Pat $Pat - - [System.Object]$apiOperation = $null - [System.Object]$apiOperation = Invoke-AzDevOpsApiRestMethod -Uri $apiResourceUri -Method 'Delete' ` - -Headers $apiHttpRequestHeader - - if ($Wait) - { - # Waits for operation to complete successfully. Throws exception if operation is not successful and/or timeout is reached. - Wait-AzDevOpsOperation -ApiUri $ApiUri -Pat $Pat ` - -OperationId $apiOperation.id ` - -IsSuccessful - - # Adds an additional, post-operation delay/buffer to mitigate subsequent calls trying to obtain new/updated items too quickly from the API - Start-Sleep -Milliseconds $(Get-AzDevOpsApiWaitIntervalMs) - } - } -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Set-AzDevOpsApiResource.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Set-AzDevOpsApiResource.ps1 deleted file mode 100644 index 0f748c403..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Set-AzDevOpsApiResource.ps1 +++ /dev/null @@ -1,121 +0,0 @@ -<# - .SYNOPSIS - Attempts to update a resource within Azure DevOps. - - The type of resource type updated is provided in the 'ResourceName' parameter and it is - assumed that the 'Resource' parameter value passed in meets the specification of the resource. - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER ApiVersion - The version of the Azure DevOps API to use in the call/execution to/against the API. - - .PARAMETER Pat - The 'Personal Access Token' (PAT) to be used by any subsequent requests/operations - against the Azure DevOps API. This PAT must have the relevant permissions assigned - for the subsequent operations being performed. - - .PARAMETER ResourceName - The name of the resource being updated within Azure DevOps (e.g. 'Project') - - .PARAMETER Resource - The resource being updated (typically provided by another function (e.g. 'Set-AzDevOpsApiProject')). - - .EXAMPLE - Set-AzDevOpsApiResource -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ResourceName 'Project' -Resource $YourResource -Wait - - Updates the 'Project' resource in Azure DevOps within to the Organization relating to the to the 'ApiUri' - provided. - - NOTE: In this example, the '-Wait' switch is provided so the function will wait for the corresponding API 'Operation' - to complete before the function completes. No return value is provided by this function and if the creation of the - resource has failed, an exception will be thrown. - - .EXAMPLE - Set-AzDevOpsApiResource -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ResourceName 'Project' -Resource $YourResource - - Updates the 'Project' resource in Azure DevOps within to the Organization relating to the to the 'ApiUri' - provided. - - NOTE: In this example, no '-Wait' switch is provided so the request is made to the API but the operation may - not complete before the function completes (and may not complete successfully at all). -#> -function Set-AzDevOpsApiResource -{ - [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] - [OutputType([System.Object])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateScript( { Test-AzDevOpsApiUri -ApiUri $_ -IsValid })] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter()] - [ValidateScript( { Test-AzDevOpsApiVersion -ApiVersion $_ -IsValid })] - [System.String] - $ApiVersion = $(Get-AzDevOpsApiVersion -Default), - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsPat -Pat $_ -IsValid })] - [Alias('PersonalAccessToken')] - [System.String] - $Pat, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsApiResourceName -ResourceName $_ -IsValid })] - [System.String] - $ResourceName, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsApiResourceId -ResourceId $_ -IsValid })] - [System.String] - $ResourceId, - - [Parameter(Mandatory = $true)] - [System.Object] - $Resource, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Wait, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - if ($Force -or $PSCmdlet.ShouldProcess($apiResourceUri, $ResourceName)) - { - $apiResourceUriParameters = @{ - ApiUri = $ApiUri - ApiVersion = $ApiVersion - ResourceName = $ResourceName - ResourceId = $ResourceId - } - - [string]$apiResourceUri = Get-AzDevOpsApiResourceUri @apiResourceUriParameters - [Hashtable]$apiHttpRequestHeader = Get-AzDevOpsApiHttpRequestHeader -Pat $Pat - [string]$apiHttpRequestBody = $Resource | ConvertTo-Json -Depth 10 -Compress - - [System.Object]$apiOperation = $null - [System.Object]$apiOperation = Invoke-AzDevOpsApiRestMethod -Uri $apiResourceUri -Method 'Patch' ` - -Headers $apiHttpRequestHeader -Body $apiHttpRequestBody ` - -ContentType 'application/json' - - if ($Wait) - { - # Waits for operation to complete successfully. Throws exception if operation is not successful and/or timeout is reached. - Wait-AzDevOpsOperation -ApiUri $ApiUri -Pat $Pat ` - -OperationId $apiOperation.id ` - -IsSuccessful - - # Adds an additional, post-operation delay/buffer to mitigate subsequent calls trying to obtain new/updated items too quickly from the API - Start-Sleep -Milliseconds $(Get-AzDevOpsApiWaitIntervalMs) - } - } -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResource.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResource.ps1 deleted file mode 100644 index fcdb07a69..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResource.ps1 +++ /dev/null @@ -1,73 +0,0 @@ -<# - .SYNOPSIS - Tests for the presence of an Azure DevOps API Resource. - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER ApiVersion - The version of the Azure DevOps API to use in the call/execution to/against the API. - - .PARAMETER Pat - The 'Personal Access Token' (PAT) to be used by any subsequent requests/Resources - against the Azure DevOps API. This PAT must have the relevant permissions assigned - for the subsequent Resources being performed. - - .PARAMETER ResourceName - The name of the resource being updated within Azure DevOps (e.g. 'Project') - - .PARAMETER ResourceId - The 'id' of the Azure DevOps API Resource. This is typically obtained from a response - provided by the API when a request is made to it. - - .EXAMPLE - Test-AzDevOpsApiResource -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' ` - -ResourceName 'YourResourceName' -ResourceId 'YourResourceId' - - Tests that the Azure DevOps 'Resource' (identified by the 'ResourceId' for the resource of type - provided by the 'ResourceName' field) exists. Returns $true if it exists and returns $false - if it does not. -#> -function Test-AzDevOpsApiResource -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateScript( { Test-AzDevOpsApiUri -ApiUri $_ -IsValid })] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter()] - [ValidateScript( { Test-AzDevOpsApiVersion -ApiVersion $_ -IsValid })] - [System.String] - $ApiVersion = $(Get-AzDevOpsApiVersion -Default), - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsPat -Pat $_ -IsValid })] - [Alias('PersonalAccessToken')] - [System.String] - $Pat, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsApiResourceName -ResourceName $_ -IsValid })] - [System.String] - $ResourceName, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsApiResourceId -ResourceId $_ -IsValid })] - [Alias('Id')] - [System.String] - $ResourceId - ) - - [System.Management.Automation.PSObject[]]$apiResource = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat ` - -ResourceName $ResourceName ` - -ResourceId $ResourceId - - return ($apiResource.Count -gt 0) -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceName.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceName.ps1 deleted file mode 100644 index 1fd11df8f..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceName.ps1 +++ /dev/null @@ -1,41 +0,0 @@ -<# - .SYNOPSIS - Peforms test on a provided 'ResourceName' to provide a boolean ($true or $false) - return value. Returns $true if the test is successful. - - NOTE: Use of the '-IsValid' switch is required. - - .PARAMETER ResourceName - The 'ResourceName' to be tested/validated. - - .PARAMETER IsValid - Use of this switch will validate the format of the 'ResourceName' - rather than the existence/presence of it. - - Failure to use this switch will throw an exception. - - .EXAMPLE - Test-AzDevOpsApiResourceName -ResourceName 'YourResourceNameHere' -IsValid - - Returns $true if the 'ResourceName' provided is of a valid format. - Returns $false if it is not. -#> -function Test-AzDevOpsApiResourceName -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $ResourceName, - - [Parameter(Mandatory = $true)] - [ValidateSet($true)] - [System.Management.Automation.SwitchParameter] - $IsValid - ) - - - return !(!($(Get-AzDevOpsApiResourceName).Contains($ResourceName))) -} diff --git a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 index cc2f6cc0e..3720b5b27 100644 --- a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 +++ b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 @@ -22,21 +22,67 @@ # 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 = @( - 'New-AzManagedIdentity', + # + # LCM Supporting Functions 'Get-AzDevOpsServicesUri', 'Get-AzDevOpsServicesApiUri', - 'Get-AzDevOpsOperation', 'Test-AzDevOpsOperation', + 'Initialize-CacheObject', + + 'New-AzDoAuthenticationProvider', + 'Get-AzDoCacheObjects', + 'AzDoAPI_0_ProjectCache', + 'AzDoAPI_1_GroupCache', + 'AzDoAPI_2_UserCache', + 'AzDoAPI_3_GroupMemberCache', + + # + # DSC Class Based Resources + 'Get-xAzDoProject', + 'New-xAzDoProject', + 'Set-xAzDoProject', + 'Remove-xAzDoProject', + 'Test-xAzDoProject', + + 'Get-xAzDoProjectGroup', + 'New-xAzDoProjectGroup', + 'Set-xAzDoProjectGroup', + 'Remove-xAzDoProjectGroup', + 'Test-xAzDoProjectGroup', + + 'Get-xAzDoOrganizationGroup', + 'New-xAzDoOrganizationGroup', + 'Set-xAzDoOrganizationGroup', + 'Remove-xAzDoOrganizationGroup', + 'Test-xAzDoOrganizationGroup', + + 'Get-xAzDoGroupMember', + 'New-xAzDoGroupMember', + 'Set-xAzDoGroupMember', + 'Remove-xAzDoGroupMember', + 'Test-xAzDoGroupMember' + + 'Get-xAzDoGitRepository', + 'New-xAzDoGitRepository', + 'Remove-xAzDoGitRepository', + + 'Get-xAzDoGitPermission', + 'New-xAzDoGitPermission', + 'Remove-xAzDoGitPermission', + 'Set-xAzDoGitPermission', + + 'Get-xAzDoGroupPermission', + 'New-xAzDoGroupPermission', + 'Remove-xAzDoGroupPermission', + 'Set-xAzDoGroupPermission', + + 'Get-xAzDoProjectServices', + 'Set-xAzDoProjectServices', + 'Test-xAzDoProjectServices', + 'Remove-xAzDoProjectServices' - 'Get-AzDevOpsProject', - 'New-AzDevOpsProject', - 'Set-AzDevOpsProject', - 'Remove-AzDevOpsProject', - 'Test-AzDevOpsProject', - - 'New-AzManagedIdentity' ) # 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. diff --git a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 index 71e157ea2..153f2d98d 100644 --- a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 +++ b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 @@ -1,3 +1,9 @@ +[CmdletBinding()] +param ( + [Parameter()] + [Switch] + $isClass +) #using module AzureDevOpsDsc # Setup/Import 'DscResource.Common' helper module #$script:resourceHelperModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common' @@ -6,24 +12,39 @@ $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' +$ModuleRoot = $PSScriptRoot # Obtain all functions within PSModule $functionSubDirectoryPaths = @( + # Classes + "$ModuleRoot\Api\Classes\", + + # Enum + #"$ModuleRoot\Api\Enums\", + + # Data + "$ModuleRoot\Api\Data\", + "$ModuleRoot\LocalizedData\", + # Api - "$PSScriptRoot\Api\Functions\Private", + "$ModuleRoot\Api\Functions\Private\Api", + "$ModuleRoot\Api\Functions\Private\Cache", + "$ModuleRoot\Api\Functions\Private\Helper", + "$ModuleRoot\Api\Functions\Private\Cache\Cache Initalization" + "$ModuleRoot\Api\Functions\Private\Authentication" # Connection - "$PSScriptRoot\Connection\Functions\Private", + "$ModuleRoot\Connection\Functions\Private", # Resources - "$PSScriptRoot\Resources\Functions\Public", - "$PSScriptRoot\Resources\Functions\Private", + "$ModuleRoot\Resources\Functions\Public", + "$ModuleRoot\Resources\Functions\Private", # Server # Services - "$PSScriptRoot\Services\Functions\Public" + "$ModuleRoot\Services\Functions\Public" ) $functions = Get-ChildItem -Path $functionSubDirectoryPaths -Recurse -Include "*.ps1" @@ -38,9 +59,25 @@ foreach ($function in $functions) ) ) - if ($function.FullName -ilike "$PSScriptRoot\*\Functions\Public\*") + if ($function.FullName -ilike "$ModuleRoot\*\Functions\Public\*") { Write-Verbose "Exporting '$($function.BaseName)'..." Export-ModuleMember -Function $($function.BaseName) } } + +# +# Static Functions that need to be exported + +Export-ModuleMember -Function 'AzDoAPI_*' + +Export-ModuleMember -Function 'Set-CacheObject' +Export-ModuleMember -Function 'Get-CacheItem' +Export-ModuleMember -Function 'Get-AzDoAPIGroupCache' +Export-ModuleMember -Function 'Get-AzDoAPIProjectCache' +Export-ModuleMember -Function 'Initialize-CacheObject' +Export-ModuleMember -Function 'Get-AzDoCacheObjects' +Export-ModuleMember -Function '*-xAzDoProjectGroup' + +# Stop processing +if ($isClass) { return } diff --git a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 new file mode 100644 index 000000000..176776083 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 @@ -0,0 +1,28 @@ +data LocalizedDataAzACLTokenPatten +{ +@' + +# +# Git ACL Token Patterns +# + +# Organizational Level Token +OrganizationGit = ^repoV2$ +# Project-Level Git Repository Token +GitProject = \^\(repoV2\)\\/\(\?\[A-Za-z0-9-]\+\)\$ +# Git Repository Token +GitRepository = \^\(repoV2\)\\/\(\?\[A-Za-z0-9-]\+\)\\/\(\?\[A-Za-z0-9-]\+\)\$ +# Git Branch Token +GitBranch = \^\(repoV2\)\\/\(\?\[A-Za-z0-9-]\+\)\\/\(\?\[A-Za-z0-9-]\+\)\\/refs\\/heads\\/\(\?\[A-Za-z0-9]\+\)\$ + +# +# Identity ACL Token Patterns +# + +# Group Permission Token +GroupPermission = ^(?[A-Za-z0-9-_]+)\\\\(?[A-Za-z0-9-_]+)$ +# Resource (Project or Identity) Permission +ResourcePermission = \^\(\?\[A-Za-z0-9-_]\+\)\$ + +'@ | ConvertFrom-StringData +} diff --git a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/001.LocalizedDataAzResourceTokenPatten.ps1 b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/001.LocalizedDataAzResourceTokenPatten.ps1 new file mode 100644 index 000000000..4b448dfb5 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/001.LocalizedDataAzResourceTokenPatten.ps1 @@ -0,0 +1,26 @@ +data LocalizedDataAzResourceTokenPatten +{ +@' + +# +# Git ACL Token Patterns +# + +# Organizational Level Token +OrganizationGit = \^azdoorg\$ +# Project-Level Git Repository Token +GitProject = \^\(\?\[A-Za-z0-9-]\+\)\$ +# Git Repository Token +GitRepository = \(\?\[A-Za-z0-9-_]\+\)\(\\/\|\\\\\)\(\?\[A-Za-z0-9-_]\+\) + +# +# Identity Permissions + +# Group Permission +GroupPermission = \^\(\?\[A-Za-z0-9-_]\+\)\\\\(\?\[A-Za-z0-9-_]\+\)\$ + +# Project Permissions +ProjectPermission = \^\\\$PROJECT:vstfs:\\/\{3}Classification\\/TeamProject\\/\(\?\[A-Za-z0-9-_]\+\)\$ + +'@ | ConvertFrom-StringData +} diff --git a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/002.LocalizedDataAzSerializationPatten.ps1 b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/002.LocalizedDataAzSerializationPatten.ps1 new file mode 100644 index 000000000..ca029f736 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/002.LocalizedDataAzSerializationPatten.ps1 @@ -0,0 +1,19 @@ +data LocalizedDataAzSerializationPatten +{ +@' + +# Git Repository ACL Token Patterns. Exclude the refs token since these are branch level ACLs. +# Example: repoV2/ProjectId/RepoId +# Not: repoV2/ProjectId/RepoId/refs/heads/BranchName +GitRepository = \^repoV2\\/\{0}\\/\(\?!\.\*\\/refs\)\.\* + +# Group Permissions +# Example: 78a5065f-3043-426f-9cc5-785748b18f9d\\242ea4ca-e150-4499-a491-00f4ce1f480e +GroupPermission = \^\{0}\\\\\\\\\{1}\$ +# +# Project Permissions +# Example: $PROJECT:vstfs:///Classification/TeamProject/78a5065f-3043-426f-9cc5-785748b18f9d +ProjectPermission = \^\\\$PROJECT:vstfs:\\/\{3}Classification\\/TeamProject\\/\{0}\$ + +'@ | ConvertFrom-StringData +} diff --git a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/003.LocalizedDataAzURLParams.ps1 b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/003.LocalizedDataAzURLParams.ps1 new file mode 100644 index 000000000..415034a1c --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/003.LocalizedDataAzURLParams.ps1 @@ -0,0 +1,13 @@ +data LocalizedDataAzURLParams +{ +@' +# +# Azure DevOps Project Services URL Parameters + +ProjectService_Pipelines=ms.vss-build.pipelines +ProjectService_TestPlans=ms.vss-test-web.test +ProjectService_Boards=ms.vss-work.agile +ProjectService_Repos=ms.vss-code.version-control +ProjectService_Artifacts=ms.azure-artifacts.feature +'@ | ConvertFrom-StringData +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.ps1 index f58cab88d..bf5b7a785 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.ps1 @@ -26,16 +26,20 @@ function Test-AzDevOpsPat [OutputType([System.Boolean])] param ( - [Parameter(Mandatory = $true)] + [Parameter()] [System.String] $Pat, - [Parameter(Mandatory = $true)] - [ValidateSet($true)] + [Parameter()] [System.Management.Automation.SwitchParameter] $IsValid ) + # If the Pat token is blank it means that managed identity is being used. + # In this case, the function will return $true. + + if ([System.String]::IsNullOrWhiteSpace($Pat)) { return $true } + return !([System.String]::IsNullOrWhiteSpace($Pat) -or $Pat.Length -ne 52) # Note: 52 is the current/expected length of PAT } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-ObjectProperty.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-ObjectProperty.ps1 new file mode 100644 index 000000000..8f8966625 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-ObjectProperty.ps1 @@ -0,0 +1,60 @@ +<# +.SYNOPSIS +Checks if a property exists in an object. + +.DESCRIPTION +The Test-ObjectProperty function checks if a specified property exists in an object. It supports checking properties in hashtables, PSCustomObjects, and PSObjects. + +.PARAMETER Object +The object to check for the existence of the property. + +.PARAMETER PropertyName +The name of the property to check for. + +.OUTPUTS +System.Boolean +Returns $true if the property exists in the object, otherwise returns $false. + +.EXAMPLE +$object = [PSCustomObject]@{ + Name = "John" + Age = 30 +} + +Test-ObjectProperty -Object $object -PropertyName "Name" +# Returns $true + +Test-ObjectProperty -Object $object -PropertyName "Email" +# Returns $false +#> + +Function Test-ObjectProperty { + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory)] + [System.Object] + $Object, + + [Parameter(Mandatory)] + [System.String] + $PropertyName + ) + + # If the object is a hashtable, check if the key exists + if ($Object -is [System.Collections.Hashtable]) { + return $Object.ContainsKey($PropertyName) + } + # If the object is a PSCustomObject, check if the property exists + elseif ($Object -is [PSCustomObject]) { + return $Object.PSObject.Properties.Name -contains $PropertyName + } + # If the object is a PSObject, check if the property exists + elseif ($Object -is [PSObject]) { + return $Object.PSObject.Properties.Name -contains $PropertyName + } + + # Return false + return $false +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsProject.ps1 deleted file mode 100644 index 3fdca1c4a..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsProject.ps1 +++ /dev/null @@ -1,113 +0,0 @@ -<# - .SYNOPSIS - Returns an Azure DevOps 'Project' as identified by the 'ProjectId' and/or 'ProjectName' provided. - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER Pat - The 'Personal Access Token' (PAT) to be used by any subsequent requests/operations - against the Azure DevOps API. This PAT must have the relevant permissions assigned - for the subsequent operations being performed. - - .PARAMETER ProjectId - The 'id' of the 'Project' being obtained/requested. - - .PARAMETER ProjectName - The 'name' of the 'Project' being obtained/requested. Wildcards (e.g. '*') are allowed. - - .EXAMPLE - Get-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' - - Returns all the 'Project' resources (assocated with the Organization/ApiUrl) from Azure DevOps. - - .EXAMPLE - Get-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ProjectName '*' - - Returns all the 'Project' resources (assocated with the Organization/ApiUrl) from Azure DevOps. - - .EXAMPLE - Get-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ProjectId 'YourProjectIdHere' - - Returns the 'Project' resources (assocated with the Organization/ApiUrl) from Azure DevOps related to the 'ProjectId' value provided. - - .EXAMPLE - Get-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ProjectName 'YourProjectNameHere' - - Returns the 'Project' resources (assocated with the Organization/ApiUrl) from Azure DevOps related to the 'ProjectName' value provided. - - .EXAMPLE - Get-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ProjectId 'YourProjectIdHere' -ProjectName 'YourProjectNameHere' - - Returns the 'Project' resources (assocated with the Organization/ApiUrl) from Azure DevOps related to the 'ProjectId' and 'ProjectName' value provided. -#> -function Get-AzDevOpsProject -{ - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateScript( { Test-AzDevOpsApiUri -ApiUri $_ -IsValid })] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsPat -Pat $_ -IsValid })] - [Alias('PersonalAccessToken')] - [System.String] - $Pat, - - [Parameter()] - [ValidateScript({ Test-AzDevOpsProjectId -ProjectId $_ -IsValid })] - [Alias('ResourceId','Id')] - [System.String] - $ProjectId, - - [Parameter()] - [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] - [Alias('Name')] - [System.String] - $ProjectName - ) - - # Prepare initial 'Get-AzDevOpsApiResource' function parameters - $azDevOpsApiResourceParameters = @{ - ApiUri = $ApiUri - Pat = $Pat - ResourceName = 'Project' - } - If(![System.String]::IsNullOrWhiteSpace($ProjectId)){ - $azDevOpsApiResourceParameters.ResourceId = $ProjectId - } - - # Obtain all 'Projects' (Note: This returns a limited set of properties, hence why subsequent calls are made) - [System.Management.Automation.PSObject[]]$apiListResources = Get-AzDevOpsApiResource @azDevOpsApiResourceParameters - [System.Management.Automation.PSObject[]]$projects = @() - - # Filter projects by 'ProjectId' - If(![System.String]::IsNullOrWhiteSpace($ProjectId)){ - $apiListResources = $apiListResources | - Where-Object id -eq $ProjectId - } - - # Filter projects by 'ProjectName' (using 'ilike') - If(![System.String]::IsNullOrWhiteSpace($ProjectName)){ - $apiListResources = $apiListResources | - Where-Object name -ilike $ProjectName - } - - # For each project (if any), call 'Get-AzDevOpsApiResource' again to obtain all 'Project' properties - if ($apiListResources.Count -gt 0) - { - $apiListResources | ForEach-Object { - $azDevOpsApiResourceParameters.ResourceId = $_.id - $projects += $(Get-AzDevOpsApiResource @azDevOpsApiResourceParameters) - } - } - - return [System.Management.Automation.PSObject[]]$projects -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDevOpsProject.ps1 deleted file mode 100644 index a40c71fbc..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDevOpsProject.ps1 +++ /dev/null @@ -1,108 +0,0 @@ -<# - .SYNOPSIS - Creates a new Azure DevOps 'Project' with the specified properties set by the parameters. - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER Pat - The 'Personal Access Token' (PAT) to be used by any subsequent requests/operations - against the Azure DevOps API. This PAT must have the relevant permissions assigned - for the subsequent operations being performed. - - .PARAMETER ProjectName - The 'name' of the 'Project' being created. - - .PARAMETER ProjectDescription - The 'description' of the 'Project' being created. - - .PARAMETER SourceControlType - The 'sourceControlType' of the 'Project' being created. - - Options are 'Tfvc' or 'Git'. Defaults to 'Git' if no value provided. - - .PARAMETER Force - When this switch is used, any confirmation will be overidden/ignored. - - .EXAMPLE - New-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' ` - -ProjectName 'YourProjectNameHere' ` - -ProjectDescription 'YourProjectDescriptionHere' -SourceControlType 'Git' - - Creates a 'Project' (assocated with the Organization/ApiUrl) in Azure DevOps using project-related, parameter values provided. -#> -function New-AzDevOpsProject -{ - [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] - [OutputType([System.Object])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateScript( { Test-AzDevOpsApiUri -ApiUri $_ -IsValid })] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsPat -Pat $_ -IsValid })] - [Alias('PersonalAccessToken')] - [System.String] - $Pat, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid })] - [Alias('Name')] - [System.String] - $ProjectName, - - [Parameter()] - [ValidateScript({ Test-AzDevOpsProjectDescription -ProjectDescription $_ -IsValid })] - [AllowEmptyString()] - [Alias('Description')] - [System.String] - $ProjectDescription = '', - - [Parameter()] - [ValidateSet('Git','Tfvc')] - [System.String] - $SourceControlType = 'Git', - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - [string]$resourceJson = ' - { - "id": "00000000-0000-0000-0000-000000000000", - "name": "' + $ProjectName + '", - "description": "' + $ProjectDescription + '", - "capabilities": { - "versioncontrol": { - "sourceControlType": "' + $SourceControlType + '" - }, - "processTemplate": { - "templateTypeId": "6b724908-ef14-45cf-84f8-768b5384da45" - } - } - } -' - - [System.Object]$newResource = $null - $ResourceName = 'Project' - - if ($Force -or $PSCmdlet.ShouldProcess($ApiUri, $ResourceName)) - { - New-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat ` - -ResourceName $ResourceName ` - -Resource $($resourceJson | ConvertFrom-Json) ` - -Force:$Force -Wait | Out-Null - - [System.Object]$newResource = Get-AzDevOpsProject -ApiUri $ApiUri -Pat $Pat ` - -ProjectName $ProjectName - } - - return $newResource -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 new file mode 100644 index 000000000..a00da8375 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 @@ -0,0 +1,144 @@ +<# +.SYNOPSIS +Creates a new Azure Managed Identity. + +.DESCRIPTION +The New-AzDoAuthenticationProvider function creates a new Azure Managed Identity for use in Azure DevOps DSC. + +.PARAMETER OrganizationName +Specifies the name of the organization associated with the Azure Managed Identity. + +.EXAMPLE +New-AzDoAuthenticationProvider -OrganizationName "Contoso" + +This example creates a new Azure Managed Identity for the organization named "Contoso". + +#> +Function New-AzDoAuthenticationProvider { + + [CmdletBinding(DefaultParameterSetName = 'PersonalAccessToken')] + param ( + # Organization Name + [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] + [Parameter(Mandatory, ParameterSetName = 'SecureStringPersonalAccessToken')] + [Parameter(Mandatory, ParameterSetName = 'ManagedIdentity')] + [Alias('OrgName')] + [String] + $OrganizationName, + + # Personal Access Token + [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] + [Alias('PAT')] + [String] + $PersonalAccessToken, + + # SecureString Personal Access Token + [Parameter(Mandatory, ParameterSetName = 'SecureStringPersonalAccessToken')] + [Alias('SecureStringPAT')] + [SecureString] + $SecureStringPersonalAccessToken, + + # Use Managed Identity + [Parameter(ParameterSetName = 'ManagedIdentity')] + [Switch] + $useManagedIdentity, + + # Don't verify the Token + [Parameter(ParameterSetName = 'ManagedIdentity')] + [Parameter(ParameterSetName = 'PersonalAccessToken')] + [Switch] + $NoVerify, + + # Do not export the Token + # Used by Resources that do not require the Token to be exported. + [Parameter(ParameterSetName = 'PersonalAccessToken')] + [Parameter(ParameterSetName = 'SecureStringPersonalAccessToken')] + [Parameter(ParameterSetName = 'ManagedIdentity')] + [Switch] + $isResource + + ) + + # Test if $ENV:AZDODSC_CACHE_DIRECTORY is set. If not, throw an error. + if ($null -eq $ENV:AZDODSC_CACHE_DIRECTORY) { + Throw "[New-AzDoAuthenticationProvider] The Environment Variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the Environment Variable 'AZDODSC_CACHE_DIRECTORY' to the Cache Directory." + } + + # Set the Global Variables + $Global:DSCAZDO_OrganizationName = $OrganizationName + $Global:DSCAZDO_AuthenticationToken = $null + + # + # If the parameterset is PersonalAccessToken + if ($PSCmdlet.ParameterSetName -eq 'PersonalAccessToken') { + + Write-Verbose "[New-AzDoAuthenticationProvider] Creating a new Personal Access Token with OrganizationName $OrganizationName." + + # if the NoVerify switch is not set, verify the Token. + if ($NoVerify) { + $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -PersonalAccessToken $PersonalAccessToken + } else { + $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -PersonalAccessToken $PersonalAccessToken -Verify + } + + } + # + # If the parameterset is ManagedIdentity + elseif ($PSCmdlet.ParameterSetName -eq 'ManagedIdentity') { + + Write-Verbose "[New-AzDoAuthenticationProvider] Creating a new Azure Managed Identity with OrganizationName $OrganizationName." + # If the Token is not Valid. Get a new Token. + + if ($NoVerify) { + $Global:DSCAZDO_AuthenticationToken = Get-AzManagedIdentityToken -OrganizationName $OrganizationName + } else { + $Global:DSCAZDO_AuthenticationToken = Get-AzManagedIdentityToken -OrganizationName $OrganizationName -Verify + } + + } + # + # If the parameterset is SecureStringPersonalAccessToken + elseif ($PSCmdlet.ParameterSetName -eq 'SecureStringPersonalAccessToken') { + Write-Verbose "[New-AzDoAuthenticationProvider] Creating a new Personal Access Token with OrganizationName $OrganizationName." + # If the Token is not Valid. Get a new Token. + $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -SecureStringPersonalAccessToken $SecureStringPersonalAccessToken + } + + # + # Export the Token information to the Cache Directory + + if ($isResource.IsPresent) { + Write-Verbose "[New-AzDoAuthenticationProvider] isResource is set. The Token will not be exported." + return + } + + # + # Initialize the Cache + + # Initialize the Cache Objects + Get-AzDoCacheObjects | ForEach-Object { + Initialize-CacheObject -CacheType $_ + } + + # Iterate through Each of the Caching Commands and initalize the Cache. + Get-Command "AzDoAPI_*" | Where-Object Source -eq 'AzureDevOpsDsc.Common' | ForEach-Object { + . $_.Name -OrganizationName $AzureDevopsOrganizationName + } + + # + # Export the Token to the Cache Directory + + # Create an Object Containing the Organization Name. + $moduleSettingsPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "ModuleSettings.clixml" + Write-Verbose "[New-AzDoAuthenticationProvider] Exporting the Module Settings to $moduleSettingsPath." + + $objectSettings = [PSCustomObject]@{ + OrganizationName = $Global:DSCAZDO_OrganizationName + Token = $Global:DSCAZDO_AuthenticationToken + SecurityDescriptorTypes = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "SecurityDescriptors.clixml" + } + + # Export the Object to the Cache Directory + $objectSettings | Export-Clixml -LiteralPath $moduleSettingsPath -Depth 5 + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzManagedIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzManagedIdentity.ps1 deleted file mode 100644 index c18ce9d77..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzManagedIdentity.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -<# -.SYNOPSIS -Creates a new Azure Managed Identity. - -.DESCRIPTION -The New-AzManagedIdentity function creates a new Azure Managed Identity for use in Azure DevOps DSC. - -.PARAMETER OrganizationName -Specifies the name of the organization associated with the Azure Managed Identity. - -.EXAMPLE -New-AzManagedIdentity -OrganizationName "Contoso" - -This example creates a new Azure Managed Identity for the organization named "Contoso". - -#> -Function New-AzManagedIdentity { - - [CmdletBinding()] - param ( - [Parameter()] - [Alias('OrgName')] - [String] - $OrganizationName - ) - - $Global:DSCAZDO_OrganizationName = $OrganizationName - $Global:DSCAZDO_ManagedIdentityToken = $null - - # If the Token is not Valid. Get a new Token. - $Global:DSCAZDO_ManagedIdentityToken = Get-AzManagedIdentityToken -OrganizationName $OrganizationName -Verify - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Remove-AzDevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Remove-AzDevOpsProject.ps1 deleted file mode 100644 index 266333e6d..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Remove-AzDevOpsProject.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -<# - .SYNOPSIS - Removes/deletes a Azure DevOps 'Project' with the provided 'ProjectId'. - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER Pat - The 'Personal Access Token' (PAT) to be used by any subsequent requests/operations - against the Azure DevOps API. This PAT must have the relevant permissions assigned - for the subsequent operations being performed. - - .PARAMETER ProjectId - The 'id' of the 'Project' being deleted. - - .PARAMETER Force - When this switch is used, any confirmation will be overidden/ignored. - - .EXAMPLE - New-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' ` - -ProjectName 'YourProjectNameHere' ` - -ProjectDescription 'YourProjectDescriptionHere' -SourceControlType 'Git' - - Creates a 'Project' (assocated with the Organization/ApiUrl) in Azure DevOps using project-related, parameter values provided. -#> -function Remove-AzDevOpsProject -{ - [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] - [OutputType([System.Object])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateScript( { Test-AzDevOpsApiUri -ApiUri $_ -IsValid })] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsPat -Pat $_ -IsValid })] - [Alias('PersonalAccessToken')] - [System.String] - $Pat, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsProjectId -ProjectId $_ -IsValid })] - [Alias('ResourceId','Id')] - [System.String] - $ProjectId, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - if ($Force -or $PSCmdlet.ShouldProcess($ApiUri, $ResourceName)) - { - Remove-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat ` - -ResourceName 'Project' ` - -ResourceId $ProjectId ` - -Force:$Force -Wait | Out-Null - - } -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Set-AzDevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Set-AzDevOpsProject.ps1 deleted file mode 100644 index 5dbc00229..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Set-AzDevOpsProject.ps1 +++ /dev/null @@ -1,121 +0,0 @@ -<# - .SYNOPSIS - Updates an Azure DevOps 'Project' with the specified properties set by the parameters. - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER Pat - The 'Personal Access Token' (PAT) to be used by any subsequent requests/operations - against the Azure DevOps API. This PAT must have the relevant permissions assigned - for the subsequent operations being performed. - - .PARAMETER ProjectName - The 'name' of the 'Project' being updated. - - .PARAMETER ProjectDescription - The 'description' of the 'Project' being updated. - - .PARAMETER SourceControlType - The 'sourceControlType' of the 'Project' being updated. - - Options are 'Tfvc' or 'Git'. Defaults to 'Git' if no value provided. - - .PARAMETER Force - When this switch is used, any confirmation will be overidden/ignored. - - .EXAMPLE - Set-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' ` - -ProjectName 'YourProjectNameHere' ` - -ProjectDescription 'YourProjectDescriptionHere' -SourceControlType 'Git' - - Creates a 'Project' (assocated with the Organization/ApiUrl) in Azure DevOps using project-related, parameter values provided. -#> -function Set-AzDevOpsProject -{ - [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] - [OutputType([System.Object])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateScript( { Test-AzDevOpsApiUri -ApiUri $_ -IsValid })] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsPat -Pat $_ -IsValid })] - [Alias('PersonalAccessToken')] - [System.String] - $Pat, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsProjectId -ProjectId $_ -IsValid })] - [Alias('ResourceId','Id')] - [System.String] - $ProjectId, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid })] - [Alias('Name')] - [System.String] - $ProjectName, - - [Parameter()] - [ValidateScript({ Test-AzDevOpsProjectDescription -ProjectDescription $_ -IsValid })] - [AllowEmptyString()] - [Alias('Description')] - [System.String] - $ProjectDescription = '', - - # $SourceControlType - Not supported for updates/set - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - -# [string]$resourceJson = ' -# { -# "id": "'+ $ProjectId +'", -# "name": "' + $ProjectName + '", -# "description": "' + $ProjectDescription + '", -# "capabilities": { -# "versioncontrol": { -# "sourceControlType": "' + $SourceControlType + '" -# }, -# "processTemplate": { -# "templateTypeId": "6b724908-ef14-45cf-84f8-768b5384da45" -# } -# } -# } -# ' - - [string]$resourceJson = ' - { - "id": "'+ $ProjectId +'", - "name": "' + $ProjectName + '", - "description": "' + $ProjectDescription + '" - } - ' - - [System.Object]$newResource = $null - $ResourceName = 'Project' - - if ($Force -or $PSCmdlet.ShouldProcess($ApiUri, $ResourceName)) - { - Set-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat ` - -ResourceName $ResourceName ` - -ResourceId $ProjectId ` - -Resource $($resourceJson | ConvertFrom-Json) ` - -Force:$Force -Wait | Out-Null - - [System.Object]$newResource = Get-AzDevOpsProject -ApiUri $ApiUri -Pat $Pat ` - -ProjectId $ProjectId ` - -ProjectName $ProjectName - } - - return $newResource -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Test-AzDevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Test-AzDevOpsProject.ps1 deleted file mode 100644 index ba5e6ace7..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Test-AzDevOpsProject.ps1 +++ /dev/null @@ -1,87 +0,0 @@ -<# - .SYNOPSIS - Tests the presence of an Azure DevOps API project. - - .PARAMETER ApiUri - The URI of the Azure DevOps API to be connected to. For example: - - https://dev.azure.com/someOrganizationName/_apis/ - - .PARAMETER Pat - The 'Personal Access Token' (PAT) to be used by any subsequent requests/projects - against the Azure DevOps API. This PAT must have the relevant permissions assigned - for the subsequent operations being performed. - - .PARAMETER ProjectId - The 'id' of the Azure DevOps API project. - - .PARAMETER ProjectName - The 'name' of the Azure DevOps API project. - - .EXAMPLE - Test-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ProjectId 'YourProjectId' - - Tests that the Azure DevOps 'Project' (identified by the 'ProjectId') exists. - - .EXAMPLE - Test-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' -ProjectId 'YourProjectName' - - Tests that the Azure DevOps 'Project' (identified by the 'ProjectName') exists. - - .EXAMPLE - Test-AzDevOpsProject -ApiUri 'YourApiUriHere' -Pat 'YourPatHere' ` - -ProjectId 'YourProjectId' -ProjectId 'YourProjectName' - - Tests that the Azure DevOps 'Project' (identified by the 'ProjectId' and 'ProjectName') exists. -#> -function Test-AzDevOpsProject -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - [ValidateScript( { Test-AzDevOpsApiUri -ApiUri $_ -IsValid })] - [Alias('Uri')] - [System.String] - $ApiUri, - - [Parameter(Mandatory = $true)] - [ValidateScript({ Test-AzDevOpsPat -Pat $_ -IsValid })] - [Alias('PersonalAccessToken')] - [System.String] - $Pat, - - [Parameter(Mandatory = $true, ParameterSetName='ProjectId')] - [Parameter(Mandatory = $true, ParameterSetName='ProjectIdAndProjectName')] - [ValidateScript({ Test-AzDevOpsProjectId -ProjectId $_ -IsValid })] - [Alias('ResourceId','Id')] - [System.String] - $ProjectId, - - [Parameter(Mandatory = $true, ParameterSetName='ProjectName')] - [Parameter(Mandatory = $true, ParameterSetName='ProjectIdAndProjectName')] - [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid })] - [Alias('Name')] - [System.String] - $ProjectName - ) - - $azDevOpsProjectParameters = @{ - ApiUri = $ApiUri; - Pat = $Pat - } - - If(![string]::IsNullOrWhiteSpace($ProjectId)){ - $azDevOpsProjectParameters.ProjectId = $ProjectId - } - - If(![string]::IsNullOrWhiteSpace($ProjectName)){ - $azDevOpsProjectParameters.ProjectName = $ProjectName - } - - [object[]]$project = Get-AzDevOpsProject @azDevOpsProjectParameters - - - return $($null -ne $project.id) -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.ps1 new file mode 100644 index 000000000..270690555 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.ps1 @@ -0,0 +1,161 @@ +<# + [DscProperty(Key, Mandatory)] + [Alias('Name')] + [System.String]$ProjectName + + [DscProperty(Mandatory)] + [Alias('Repository')] + [System.String]$RepositoryName + + [DscProperty()] + [Alias('Inherited')] + [System.Boolean]$isInherited=$true + + [DscProperty()] + [Alias('Permissions')] + [Permission[]]$PermissionsList + +#> +Function Get-xAzDoGitPermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$ProjectName, + + [Parameter(Mandatory)] + [string]$RepositoryName, + + [Parameter(Mandatory)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[Get-xAzDoGitPermission] Started." + + # Define the Descriptor Type and Organization Name + $SecurityNamespace = 'Git Repositories' + $OrganizationName = $Global:DSCAZDO_OrganizationName + + Write-Verbose "[Get-xAzDoGitPermission] Security Namespace: $SecurityNamespace" + Write-Verbose "[Get-xAzDoGitPermission] Organization Name: $OrganizationName" + + # + # Construct a hashtable detailing the group + + $getGroupResult = @{ + Ensure = [Ensure]::Absent + propertiesChanged = @() + project = $ProjectName + repositoryName = $RepositoryName + status = $null + reason = $null + } + + Write-Verbose "[Get-xAzDoGitPermission] Group result hashtable constructed." + Write-Verbose "[Get-xAzDoGitPermission] Performing lookup of permissions for the repository." + + # Define the ACL List + $ACLList = [System.Collections.Generic.List[Hashtable]]::new() + + # + # Perform a Lookup within the Cache for the Repository + $repository = Get-CacheItem -Key $("{0}\{1}" -f $ProjectName, $RepositoryName) -Type 'LiveRepositories' + + # Test if the Repository was found + if (-not $repository) { + Write-Warning "[Get-xAzDoGitPermission] Repository not found: $RepositoryName" + $getGroupResult.status = [DSCGetSummaryState]::NotFound + return $getGroupResult + } + + # + # Perform Lookup of the Permissions for the Repository + + $namespace = Get-CacheItem -Key $SecurityNamespace -Type 'SecurityNamespaces' + Write-Verbose "[Get-xAzDoGitPermission] Retrieved namespace: $($namespace.namespaceId)" + + # Add to the ACL Lookup Params + $getGroupResult.namespace = $namespace + + $ACLLookupParams = @{ + OrganizationName = $OrganizationName + SecurityDescriptorId = $namespace.namespaceId + } + + # Get the ACL List and format the ACLS + Write-Verbose "[Get-xAzDoGitPermission] ACL Lookup Params: $($ACLLookupParams | Out-String)" + + # Get the ACLs for the Repository + $DevOpsACLs = Get-DevOpsACL @ACLLookupParams + + # Test if the ACLs were found + if ($DevOpsACLs -eq $null) { + Write-Warning "[Get-xAzDoGitPermission] No ACLs found for the repository." + $getGroupResult.status = [DSCGetSummaryState]::NotFound + return $getGroupResult + } + + # Convert the ACLs to a formatted ACL + $DifferenceACLs = $DevOpsACLs | ConvertTo-FormattedACL -SecurityNamespace $SecurityNamespace -OrganizationName $OrganizationName + + # Test if the ACLs were found + if ($DifferenceACLs -eq $null) { + Write-Warning "[Get-xAzDoGitPermission] No ACLs found for the repository." + $getGroupResult.status = [DSCGetSummaryState]::NotFound + return $getGroupResult + } + + $DifferenceACLs = $DifferenceACLs | Where-Object { + ($_.Token.Type -eq 'GitRepository') -and ($_.Token.RepoId -eq $repository.id) + } + + Write-Verbose "[Get-xAzDoGitPermission] ACL List retrieved and formatted." + + # + # Convert the Permissions into an ACL Token + + $params = @{ + Permissions = $Permissions + SecurityNamespace = $SecurityNamespace + isInherited = $isInherited + OrganizationName = $OrganizationName + TokenName = "[{0}]\{1}" -f $ProjectName, $RepositoryName + } + + # Convert the Permissions to an ACL Token + $ReferenceACLs = ConvertTo-ACL @params | Where-Object { $_.token.Type -ne 'GitUnknown' } + + # Compare the Reference ACLs to the Difference ACLs + $compareResult = Test-ACLListforChanges -ReferenceACLs $ReferenceACLs -DifferenceACLs $DifferenceACLs + $getGroupResult.propertiesChanged = $compareResult.propertiesChanged + $getGroupResult.status = [DSCGetSummaryState]::"$($compareResult.status)" + $getGroupResult.reason = $compareResult.reason + + Write-Verbose "[Get-xAzDoGitPermission] ACL Token converted." + Write-Verbose "[Get-xAzDoGitPermission] ACL Token Comparison Result: $($getGroupResult.status)" + + # Export the ACL List to a file + $getGroupResult.ReferenceACLs = $ReferenceACLs + $getGroupResult.DifferenceACLs = $DifferenceACLs + + # Write + Write-Verbose "[Get-xAzDoGitPermission] Result Status: $($getGroupResult.status)" + Write-Verbose "[Get-xAzDoGitPermission] Returning Group Result." + + # Return the Group Result + return $getGroupResult + +} + diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.ps1 new file mode 100644 index 000000000..d43224ea9 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.ps1 @@ -0,0 +1,60 @@ +Function New-xAzDoGitPermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$ProjectName, + + [Parameter(Mandatory)] + [string]$RepositoryName, + + [Parameter(Mandatory)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[New-xAzDoGitPermission] Started." + + # + # Security Namespace ID + + $SecurityNamespace = Get-CacheItem -Key 'Git Repositories' -Type 'SecurityNamespaces' + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + if (($null -eq $SecurityNamespace) -or ($null -eq $Project)) { + Write-Warning "[New-xAzDoGitPermission] Security Namespace or Project not found." + return + } + + # + # Serialize the ACLs + + $serializeACLParams = @{ + ReferenceACLs = $LookupResult.propertiesChanged + DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' + DescriptorMatchToken = ($LocalizedDataAzSerializationPatten.GitRepository -f $Project.id) + } + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SecurityNamespaceID = $SecurityNamespace.namespaceId + SerializedACLs = ConvertTo-ACLHashtable @serializeACLParams + } + + # + # Set the Git Repository Permissions + + Set-xAzDoPermission @params + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.ps1 new file mode 100644 index 000000000..0a1b9af34 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.ps1 @@ -0,0 +1,90 @@ +Function Remove-xAzDoGitPermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$ProjectName, + + [Parameter(Mandatory)] + [string]$RepositoryName, + + [Parameter(Mandatory)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[New-xAzDoGitPermission] Started." + + # + # Security Namespace ID + + # Get the Security Namespace + $SecurityNamespace = Get-CacheItem -Key 'Git Repositories' -Type 'SecurityNamespaces' + + # If the Security Namespace is null, return + if (-not $SecurityNamespace) { + Write-Error "[New-xAzDoGitPermission] Security Namespace not found." + return + } + + # Get the Project + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + # If the Project is null, return + if (-not $Project) { + Write-Error "[New-xAzDoGitPermission] Project not found." + return + } + + # Get the Repository + $Repository = Get-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' + + # If the Repository is null, return + if (-not $Repository) { + Write-Error "[New-xAzDoGitPermission] Repository not found." + return + } + + # Get the ACLs + $DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' + + # If the ACLs are null, return + if (-not $DescriptorACLList) { + Write-Error "[New-xAzDoGitPermission] ACLs not found." + return + } + + # + # Filter the ACLs that pertain to the Git Repository + + $searchString = "repoV2/{0}/{1}" -f $Project.id, $Repository.id + + # Test if the Token exists + $Filtered = $DescriptorACLList | Where-Object { $_.token -eq $searchString } + + # If the ACLs are not null, remove them + if ($Filtered) { + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SecurityNamespaceID = $SecurityNamespace.namespaceId + TokenName = $searchString + } + + # Remove the ACLs + Remove-xAzDoPermission @params + + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.ps1 new file mode 100644 index 000000000..1c301a99f --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.ps1 @@ -0,0 +1,65 @@ +Function Set-xAzDoGitPermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$ProjectName, + + [Parameter(Mandatory)] + [string]$RepositoryName, + + [Parameter(Mandatory)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[Set-xAzDoPermission] Started." + + # + # Security Namespace ID + + $SecurityNamespace = Get-CacheItem -Key 'Git Repositories' -Type 'SecurityNamespaces' + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + if ($SecurityNamespace -eq $null) { + Write-Error "[Set-xAzDoPermission] Security Namespace not found." + return + } + + if ($Project -eq $null) { + Write-Error "[Set-xAzDoPermission] Project not found." + return + } + + # + # Serialize the ACLs + + $serializeACLParams = @{ + ReferenceACLs = $LookupResult.propertiesChanged + DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' + DescriptorMatchToken = ($LocalizedDataAzSerializationPatten.GitRepository -f $Project.id) + } + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SecurityNamespaceID = $SecurityNamespace.namespaceId + SerializedACLs = ConvertTo-ACLHashtable @serializeACLParams + } + + # + # Set the Git Repository Permissions + + Set-xAzDoPermission @params + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.ps1 new file mode 100644 index 000000000..891b4dc4f --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.ps1 @@ -0,0 +1,68 @@ + + +Function Get-xAzDoGitRepository { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter(Mandatory)] + [Alias('Repository')] + [System.String]$RepositoryName, + + [Parameter()] + [Alias('Source')] + [System.String]$SourceRepository, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # + # Construct a hashtable detailing the group + + $getRepositoryResult = @{ + #Reasons = $() + Ensure = [Ensure]::Absent + liveCache = $livegroup + propertiesChanged = @() + status = $null + } + + + # + # Attempt to retrive the Project Group from the Live and Local Cache. + Write-Verbose "[Get-xAzDoGitRepository] Retriving the Project Group from the Live and Local Cache." + + # Format the Key for the Project Group. + $projectGroupKey = "$ProjectName\$RepositoryName" + + # Retrive the Repositories from the Live Cache. + $repository = Get-CacheItem -Key $projectGroupKey -Type 'LiveRepositories' + + # If the Repository exists in the Live Cache, return the Repository object. + if ($repository) { + Write-Verbose "[Get-xAzDoGitRepository] The Repository '$RepositoryName' was found in the Live Cache." + $getRepositoryResult.status = [DSCGetSummaryState]::Unchanged + return $getRepositoryResult + + } else { + Write-Verbose "[Get-xAzDoGitRepository] The Repository '$RepositoryName' was not found in the Live Cache." + $getRepositoryResult.status = [DSCGetSummaryState]::NotFound + } + + # Return the Repository object. + return $getRepositoryResult + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.ps1 new file mode 100644 index 000000000..8c3c2829c --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.ps1 @@ -0,0 +1,57 @@ + + +Function New-xAzDoGitRepository { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter(Mandatory)] + [Alias('Repository')] + [System.String]$RepositoryName, + + [Parameter()] + [Alias('Source')] + [System.String]$SourceRepository, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[New-xAzDoGitRepository] Creating new repository '$($RepositoryName)' in project '$($ProjectName)'" + + # Define parameters for creating a new DevOps group + $params = @{ + ApiUri = "https://dev.azure.com/{0}/" -f $Global:DSCAZDO_OrganizationName + Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + RepositoryName = $RepositoryName + SourceRepository = $SourceRepository + } + + if ($null -eq $params.Project) { + Write-Error "[New-xAzDoGitRepository] Project '$($ProjectName)' does not exist in the LiveProjects cache. Skipping change." + return + } + + + # Create a new repository + $value = New-GitRepository @params + + # Add the repository to the LiveRepositories cache and write to verbose log + Add-CacheItem -Key "$ProjectName\$RepositoryName" -Value $value -Type 'LiveRepositories' + Export-CacheObject -CacheType 'LiveRepositories' -Content $AzDoLiveRepositories + Refresh-CacheObject -CacheType 'LiveRepositories' + Write-Verbose "[New-xAzDoGitRepository] Added new group to LiveGroups cache with key: '$($value.Name)'" + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.ps1 new file mode 100644 index 000000000..c8783d123 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.ps1 @@ -0,0 +1,54 @@ +Function Remove-xAzDoGitRepository +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter(Mandatory)] + [Alias('Repository')] + [System.String]$RepositoryName, + + [Parameter()] + [Alias('Source')] + [System.String]$SourceRepository, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + + Write-Verbose "[Remove-xAzDoGitRepository] Removing repository '$($RepositoryName)' in project '$($ProjectName)'" + + # Define parameters for creating a new DevOps group + $params = @{ + ApiUri = "https://dev.azure.com/{0}/" -f $Global:DSCAZDO_OrganizationName + Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + Repository = Get-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' + } + + # Check if the project exists in the LiveProjects cache + if (($null -eq $params.Project) -or ($null -eq $params.Repository)) { + Write-Error "[Remove-xAzDoGitRepository] Project '$($ProjectName)' or Repository '$($RepositoryName)' does not exist in the LiveProjects or LiveRepositories cache. Skipping change." + return + } + + # Create a new repository + $value = Remove-GitRepository @params + + # Add the repository to the LiveRepositories cache and write to verbose log + Remove-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' + Export-CacheObject -CacheType 'LiveRepositories' -Content $AzDoLiveRepositories + Write-Verbose "[Remove-xAzDoGitRepository] Added new group to LiveGroups cache with key: '$($value.Name)'" + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.ps1 new file mode 100644 index 000000000..a3b673c50 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.ps1 @@ -0,0 +1,31 @@ +Function Set-xAzDoGitRepository { + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter(Mandatory)] + [Alias('Repository')] + [System.String]$RepositoryName, + + [Parameter()] + [Alias('Source')] + [System.String]$SourceRepository, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Skipped + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.ps1 new file mode 100644 index 000000000..ce11a7d54 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.ps1 @@ -0,0 +1,147 @@ + + +Function Get-xAzDoGroupMember { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Members')] + [System.String[]]$GroupMembers=@(), + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + # Logging + Write-Verbose "[Get-xAzDoGroupMember] Retriving the GroupName from the Live and Local Cache." + + + # Format the According to the Group Name + $Key = Format-AzDoGroupMember -GroupName $GroupName + + # Check the cache for the group + $livegroupMembers = Get-CacheItem -Key $Key -Type 'LiveGroupMembers' + + Write-Verbose "[Get-xAzDoGroupMember] GroupName: '$GroupName'" + + # + # Construct a hashtable detailing the group + $getGroupResult = @{ + #Reasons = $() + Ensure = [Ensure]::Absent + groupName = $GroupName + reference = $GroupMembers + difference = $livegroupMembers + propertiesChanged = @() + status = $null + } + + Write-Verbose "[Get-xAzDoGroupMember] Testing LocalCache, LiveCache and Parameters." + + # + # Test if the group is present in the live cache + if ($null -eq $livegroupMembers) { + + Write-Verbose "[Get-xAzDoGroupMember] Group '$GroupName' not found in the live cache." + + # If there are no group members, test to see if there are group members defined in the parameters + if ($GroupMembers.Count -eq 0) { + $getGroupResult.status = [DSCGetSummaryState]::Unchanged + } else { + # If there are group members defined in the parameters, but no live group members, the group is new. + $getGroupResult.status = [DSCGetSummaryState]::NotFound + } + + # Return the result + return $getGroupResult + } + + # + # Test if there are no group members in parameters + if ($GroupMembers.Count -eq 0) { + + Write-Verbose "[Get-xAzDoGroupMember] Group '$GroupName' not found in the parameters." + + # If there are no live group members, the group is unchanged. + if ($livegroupMembers.Count -eq 0) { + $getGroupResult.status = [DSCGetSummaryState]::Unchanged + } else { + # If there are live group members, the groups members are to be removed. + $getGroupResult.status = [DSCGetSummaryState]::Missing + } + + # Return the result + return $getGroupResult + } + + # + # If both parameters and group members exist. + + # Compare the members of the live group with the parameters. + + # Format the parameters + $FormattedLiveGroups = @($livegroupMembers) + $FormattedParametersGroups = $GroupMembers | ForEach-Object { Find-AzDoIdentity $_ } + + # If the formatted live groups is empty. Modify the formatted live groups to be an empty array. + if ($null -eq $FormattedLiveGroups) { + $FormattedLiveGroups = @() + } + + # + # Compare the live group members with the parameters + + $params = @{ + ReferenceObject = $FormattedParametersGroups + DifferenceObject = $FormattedLiveGroups + Property = 'originId' + } + + # + # Compare the group members + $members = Compare-Object @Params -ErrorAction SilentlyContinue + + # + # If there are no differences, the group is unchanged. + + if ($members.Count -eq 0) { + + # The group is unchanged. + $getGroupResult.status = [DSCGetSummaryState]::Unchanged + + } else { + + # Users on the left side are in the comparison object but not in the reference object are to be added. + # Users on the right side are in the reference object but not in the comparison object are to be removed. + $getGroupResult.propertiesChanged += $members | ForEach-Object { + $originId = $_.originId + @{ + action = ($_.SideIndicator -eq '<=') ? 'Add' : 'Remove' + value = ($FormattedParametersGroups | Where-Object { $_.originId -eq $originId }) ?? + ($FormattedLiveGroups | Where-Object { $_.originId -eq $originId }) + + } + } + + # The group has changed. + $getGroupResult.status = [DSCGetSummaryState]::Changed + + } + + return $getGroupResult + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.ps1 new file mode 100644 index 000000000..54ba1492e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.ps1 @@ -0,0 +1,103 @@ +Function New-xAzDoGroupMember { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Members')] + [System.String[]]$GroupMembers=@(), + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + # Write a verbose log message indicating that the function has started executing. + Write-Verbose "[New-xAzDoGroupMember] Starting group member addition process for group '$GroupName'." + + # Fetch the Group Identity + $GroupIdentity = Find-AzDoIdentity $GroupName + Write-Verbose "[New-xAzDoGroupMember] Fetched group identity for '$GroupName'." + + # Retrieve the group members from the cache + $CachedGroupMembers = Get-CacheObject -CacheType 'LiveGroupMembers' + Write-Verbose "[New-xAzDoGroupMember] Retrieved cached group members." + + # Check if the group members are already cached + if (($null -ne $CachedGroupMembers) -and ($CachedGroupMembers.ContainsKey($GroupIdentity.principalName))) { + Write-Error "[New-xAzDoGroupMember] Group members are already cached for group '$GroupName'." + return + } + + + $params = @{ + GroupIdentity = $GroupIdentity + ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + } + + Write-Verbose "[New-xAzDoGroupMember] Starting group member addition process for group '$GroupName'." + Write-Verbose "[New-xAzDoGroupMember] Group members: $($GroupMembers -join ',')." + + # Define the members + $members = [System.Collections.Generic.List[object]]::new() + + # Fetch the group members and perform a lookup of the members + ForEach ($MemberIdentity in $GroupMembers) { + + # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. + Write-Verbose "[New-xAzDoGroupMember] Looking up identity for member '$MemberIdentity'." + $identity = Find-AzDoIdentity -Identity $MemberIdentity + + # If the identity is not found, write a warning message to the console and continue to the next member. + if ($null -eq $identity) { + Write-Warning "[New-xAzDoGroupMember] Unable to find identity for member '$MemberIdentity'." + continue + } + + # Check for circular reference + if ($GroupIdentity.originId -eq $identity.originId) { + Write-Warning "[New-xAzDoGroupMember] Circular reference detected for member '$MemberIdentity'." + continue + } + + Write-Verbose "[New-xAzDoGroupMember] Found identity for member '$MemberIdentity'." + + # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. + Write-Verbose "[New-xAzDoGroupMember] Adding member '$MemberIdentity' to group '$($params.GroupIdentity.displayName)'." + + $result = New-DevOpsGroupMember @params -MemberIdentity $identity + + # Add the member to the list + $members.Add($identity) + Write-Verbose "[New-xAzDoGroupMember] Member '$MemberIdentity' added to the internal list." + } + + # If the group members are not found, write a warning message to the console and return. + if ($members.Count -eq 0) { + Write-Warning "[New-xAzDoGroupMember] No group members found: $($GroupMembers -join ',')." + return + } + + # Add the group to the cache + Write-Verbose "[New-xAzDoGroupMember] Added group '$GroupName' with members to the cache." + Add-CacheItem -Key $GroupIdentity.principalName -Value $members -Type 'LiveGroupMembers' + + Write-Verbose "[New-xAzDoGroupMember] Updated global cache with live group information." + Set-CacheObject -Content $Global:AzDoLiveGroupMembers -CacheType 'LiveGroupMembers' + + # Write a verbose log message indicating that the function has completed the group member addition process. + Write-Verbose "[New-xAzDoGroupMember] Completed group member addition process for group '$GroupName'." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.ps1 new file mode 100644 index 000000000..30c740cc8 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.ps1 @@ -0,0 +1,91 @@ +Function Remove-xAzDoGroupMember { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Members')] + [System.String[]]$GroupMembers=@(), + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + # Group Identity + $GroupIdentity = Find-AzDoIdentity $GroupName + + # Format the According to the Group Name + $Key = Format-AzDoProjectName -GroupName $GroupName -OrganizationName $Global:DSCAZDO_OrganizationName + + # Check the cache for the group + $LiveGroupMembers = @(Get-CacheItem -Key $Key -Type 'LiveGroupMembers') + + # If the group identity or key is not found, write a warning message to the console and return. + if ([String]::IsNullOrEmpty($GroupIdentity) -or [String]::IsNullOrWhiteSpace($GroupIdentity)) { + Write-Warning "[Remove-xAzDoGroupMember] Unable to find identity for group '$GroupName'." + return + } + + $params = @{ + GroupIdentity = $GroupIdentity + ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + } + + Write-Verbose "[Remove-xAzDoGroupMember] Starting group member removal process for group '$GroupName'." + Write-Verbose "[Remove-xAzDoGroupMember] Group members: $($LiveGroupMembers.principalName -join ',')." + + # Define the members + #$members = [System.Collections.Generic.List[object]]::new() + + # Fetch the group members and perform a lookup of the members + ForEach ($MemberIdentity in $LiveGroupMembers) { + + # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. + Write-Verbose "[Remove-xAzDoGroupMember] Looking up identity for member '$($MemberIdentity.principalName)'." + $identity = Find-AzDoIdentity -Identity $MemberIdentity.principalName + + # If the identity is not found, write a warning message to the console and continue to the next member. + if ([String]::IsNullOrEmpty($identity) -or [String]::IsNullOrWhiteSpace($identity)) { + Write-Warning "[Remove-xAzDoGroupMember] Unable to find identity for member '$($MemberIdentity.principalName)'." + continue + } + + # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. + Write-Verbose "[Remove-xAzDoGroupMember] Removing member '$($MemberIdentity.principalName)' from group '$($params.GroupIdentity.displayName)'." + + $result = Remove-DevOpsGroupMember @params -MemberIdentity $identity + + } + + <# + # If the group members are not found, write a warning message to the console and return. + if ($members.Count -eq 0) { + Write-Warning "[Remove-xAzDoGroupMember] No group members found: $($GroupMembers -join ',')." + return + } + #> + + # Add the group to the cache + Write-Verbose "[Remove-xAzDoGroupMember] Removed group '$GroupName' with members to the cache." + Remove-CacheItem -Key $GroupIdentity.principalName -Type 'LiveGroupMembers' + + Write-Verbose "[Remove-xAzDoGroupMember] Updated global cache with live group information." + Set-CacheObject -Content $Global:AzDoLiveGroupMembers -CacheType 'LiveGroupMembers' + + # Write a verbose log message indicating that the function has completed the group member removal process. + Write-Verbose "[Remove-xAzDoGroupMember] Completed group member removal process for group '$GroupName'." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.ps1 new file mode 100644 index 000000000..58e454e72 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.ps1 @@ -0,0 +1,124 @@ +Function Set-xAzDoGroupMember { + + param( + + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Members')] + [System.String[]]$GroupMembers=@(), + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + # Group Identity + $GroupIdentity = Find-AzDoIdentity $GroupName + + # Format the According to the Group Name + $Key = Format-AzDoProjectName -GroupName $GroupName -OrganizationName $Global:DSCAZDO_OrganizationName + # Check the cache for the group + $members = [System.Collections.ArrayList]::New() + Get-CacheItem -Key $Key -Type 'LiveGroupMembers' | ForEach-Object { $members.Add($_) } + + # If the members are null or empty, stop. + if (($null -eq $GroupMembers) -or ($members.Count -eq 0)) { + Write-Error "[Set-xAzDoGroupMember] No members found in the LiveGroupMembers cache for group '$Key'." + return + } + + # If the lookup result is not provided, we need to look it up. + if ($null -eq $LookupResult.propertiesChanged) { + Throw "[Set-xAzDoGroupMember] - LookupResult.propertiesChanged is required." + } + + # Fetch the Group Identity + $params = @{ + GroupIdentity = $GroupIdentity + ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + } + + Write-Verbose "[Set-xAzDoGroupMember] Starting group member addition process for group '$GroupName'." + + # If the lookup result is not provided, we need to look it up. + switch ($LookupResult.propertiesChanged) { + + # Add members + { $_.action -eq "Add" } { + + # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. + Write-Verbose "[Set-xAzDoGroupMember][ADD] Adding Identity for Principal Name '$($_.value.principalName)'." + $identity = $_.value + + # Check for circular reference + if ($GroupIdentity.originId -eq $identity.originId) { + Write-Warning "[Set-xAzDoGroupMember][ADD] Circular reference detected for member '$($GroupIdentity.principalName)'." + continue + } + + # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. + Write-Verbose "[Set-xAzDoGroupMember][ADD] Adding member '$($identity.displayName)' to group '$($params.GroupIdentity.displayName)'." + + $result = New-DevOpsGroupMember @params -MemberIdentity $identity + + # Add the member to the list + $members.Add($identity) + Write-Verbose "[Set-xAzDoGroupMember][ADD] Member '$($identity.displayName)' added to the internal list." + + } + + # Remove + { $_.action -eq "Remove" } { + + # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. + Write-Verbose "[Set-xAzDoGroupMember][REMOVE] Removing Identity for Principal Name '$($_.value.principalName)'." + $identity = $_.value + + # Check for circular reference + if ($GroupIdentity.originId -eq $identity.originId) { + Write-Warning "[Set-xAzDoGroupMember][REMOVE] Circular reference detected for member '$($GroupIdentity.principalName)'." + continue + } + + # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. + Write-Verbose "[Set-xAzDoGroupMember][REMOVE] Removing member '$($identity.displayName)' to group '$($params.GroupIdentity.displayName)'." + + $result = Remove-DevOpsGroupMember @params -MemberIdentity $identity + + # Remove the member from the list + + Write-Verbose "[Set-xAzDoGroupMember][REMOVE] Removing member '$($identity.displayName)' from the internal list." + Write-Verbose "[Set-xAzDoGroupMember][REMOVE] members count: $($members.count)" + + $id = 0 .. $members.count | Where-Object { $members[$_].originId -eq $identity.originId } + $members.RemoveAt($id) + Write-Verbose "[Set-xAzDoGroupMember][REMOVE] Member '$($identity.displayName)' removed from the internal list." + + } + + # Default + Default { + Write-Warning "[Set-xAzDoGroupMember] Invalid action '$($_.action)' provided." + } + + } + + # Add the group to the cache + Write-Verbose "[Set-xAzDoGroupMember] Added group '$GroupName' with the updated member list to the cache." + Add-CacheItem -Key $GroupIdentity.principalName -Value $members -Type 'LiveGroupMembers' + + Write-Verbose "[Set-xAzDoGroupMember] Updated global cache with live group information." + Set-CacheObject -Content $Global:AzDoLiveGroupMembers -CacheType 'LiveGroupMembers' + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.ps1 new file mode 100644 index 000000000..2f471eea1 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.ps1 @@ -0,0 +1,28 @@ + +Function Test-xAzDoGroupMember{ + + param( + + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Members')] + [System.String[]]$GroupMembers=@(), + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + $return +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.ps1 new file mode 100644 index 000000000..db0beaae6 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.ps1 @@ -0,0 +1,150 @@ + +Function Get-xAzDoGroupPermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$GroupName, + + [Parameter(Mandatory)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[Get-xAzDoGroupPermission] Started." + + # Define the Descriptor Type and Organization Name + $SecurityNamespace = 'Identity' + $OrganizationName = $Global:DSCAZDO_OrganizationName + # Split the Group Name + $split = $GroupName.Split('\').Split('/') + + # Test if the Group Name is valid + if ($split.Count -ne 2) { + Write-Warning "[Get-xAzDoGroupPermission] Invalid Group Name: $GroupName" + return + } + + # Define the Project and Group Name + $ProjectName = $split[0].Replace('[', '').Replace(']', '') + $GroupName = $split[1] + + # If the Project Name contains 'organization'. Update the Project Name + + + Write-Verbose "[Get-xAzDoGroupPermission] Security Namespace: $SecurityNamespace" + Write-Verbose "[Get-xAzDoGroupPermission] Organization Name: $OrganizationName" + + # + # Construct a hashtable detailing the group + + $getGroupResult = @{ + Ensure = [Ensure]::Absent + propertiesChanged = @() + project = $ProjectName + groupName = $GroupName + status = $null + reason = $null + } + + Write-Verbose "[Get-xAzDoGroupPermission] Group result hashtable constructed." + Write-Verbose "[Get-xAzDoGroupPermission] Performing lookup of permissions for the group." + + # Define the ACL List + $ACLList = [System.Collections.Generic.List[Hashtable]]::new() + + # + # Perform a Lookup within the Cache for the Group + $group = Get-CacheItem -Key $('[{0}]\{1}' -f $ProjectName, $GroupName) -Type 'LiveGroups' + $project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + # Test if the Group was found + if (-not $group) { + Throw "[Get-xAzDoGroupPermission] Group not found: $('[{0}]\{1}' -f $ProjectName, $GroupName)" + return + } + + # + # Perform Lookup of the Permissions for the Group + + $namespace = Get-CacheItem -Key $SecurityNamespace -Type 'SecurityNamespaces' + Write-Verbose "[Get-xAzDoGroupPermission] Retrieved namespace: $($namespace.namespaceId)" + + # Add to the ACL Lookup Params + $getGroupResult.namespace = $namespace + + $ACLLookupParams = @{ + OrganizationName = $OrganizationName + SecurityDescriptorId = $namespace.namespaceId + } + + # Get the ACL List and format the ACLS + Write-Verbose "[Get-xAzDoGroupPermission] ACL Lookup Params: $($ACLLookupParams | Out-String)" + + $DifferenceACLs = Get-DevOpsACL @ACLLookupParams | ConvertTo-FormattedACL -SecurityNamespace $SecurityNamespace -OrganizationName $OrganizationName + $DifferenceACLs = $DifferenceACLs | Where-Object { + ($_.Token.Type -eq 'GroupPermission') -and + ($_.Token.GroupId -eq $group.originId) -and + ($_.Token.ProjectId -eq $project.id) + } + + # + # Iterate through each of the Permissions and append the permission identity if it contains 'Self' or 'This' + forEach ($Permission in $Permissions) { + if ($Permission.Identity -in 'self', 'this') { + $Permission.Identity = '[{0}]\{1}' -f $ProjectName, $GroupName + } + } + + Write-Verbose "[Get-xAzDoGroupPermission] ACL List retrieved and formatted." + + # + # Convert the Permissions into an ACL Token + + $params = @{ + Permissions = $Permissions + SecurityNamespace = $SecurityNamespace + isInherited = $isInherited + OrganizationName = $OrganizationName + TokenName = "{0}\\{1}" -f $project.id, $group.id + } + + # Convert the Permissions to an ACL Token + $ReferenceACLs = ConvertTo-ACL @params | Where-Object { $_.token.Type -ne 'GroupUnknown' } + + # if the ACEs are empty, skip + if ($ReferenceACLs.aces.Count -eq 0) { + Write-Verbose "[Get-xAzDoGroupPermission] No ACEs found for the group." + return + } + + # Compare the Reference ACLs to the Difference ACLs + $compareResult = Test-ACLListforChanges -ReferenceACLs $ReferenceACLs -DifferenceACLs $DifferenceACLs + $getGroupResult.propertiesChanged = $compareResult.propertiesChanged + $getGroupResult.status = [DSCGetSummaryState]::"$($compareResult.status)" + $getGroupResult.reason = $compareResult.reason + + # Export the ACL List to a file + $getGroupResult.ReferenceACLs = $ReferenceACLs + $getGroupResult.DifferenceACLs = $DifferenceACLs + + # Write + Write-Verbose "[Get-xAzDoGroupPermission] Result Status: $($getGroupResult.status)" + Write-Verbose "[Get-xAzDoGroupPermission] Returning Group Result." + + # Return the Group Result + return $getGroupResult + +} + diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.ps1 new file mode 100644 index 000000000..63b077127 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.ps1 @@ -0,0 +1,70 @@ +Function New-xAzDoGroupPermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$GroupName, + + [Parameter(Mandatory)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + + Write-Verbose "[New-xAzDoProjectGroupPermission] Started." + + # + # Format the Group Name + + # Split the Group Name + $split = $GroupName.Split('\').Split('/') + + # Test if the Group Name is valid + if ($split.Count -ne 2) { + Write-Warning "[Get-xAzDoProjectGroupPermission] Invalid Group Name: $GroupName" + return + } + + # Define the Project and Group Name + $ProjectName = $split[0] + $GroupName = $split[1] + + # + # Security Namespace ID + + $SecurityNamespace = Get-CacheItem -Key 'Identity' -Type 'SecurityNamespaces' + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + $Group = Get-CacheItem -Key $('[{0}]\{1}' -f $ProjectName, $GroupName) -Type 'LiveGroups' + + # + # Serialize the ACLs + + $serializeACLParams = @{ + ReferenceACLs = $LookupResult.propertiesChanged + DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' + DescriptorMatchToken = ($LocalizedDataAzSerializationPatten.GroupPermission -f $Project.id, $Group.id) + } + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SecurityNamespaceID = $SecurityNamespace.namespaceId + SerializedACLs = ConvertTo-ACLHashtable @serializeACLParams + } + + # + # Set the Git Repository Permissions + + Set-xAzDoPermission @params + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.ps1 new file mode 100644 index 000000000..f328f89dc --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.ps1 @@ -0,0 +1,73 @@ +Function Remove-xAzDoGroupPermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$GroupName, + + [Parameter(Mandatory)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + + Write-Verbose "[New-xAzDoGitPermission] Started." + + # + # Format the Group Name + + # Split the Group Name + $split = $GroupName.Split('\').Split('/') + + # Test if the Group Name is valid + if ($split.Count -ne 2) { + Write-Warning "[Get-xAzDoProjectGroupPermission] Invalid Group Name: $GroupName" + return + } + + # Define the Project and Group Name + $ProjectName = $split[0] + $GroupName = $split[1] + + # + # Security Namespace ID + + $SecurityNamespace = Get-CacheItem -Key 'Identity' -Type 'SecurityNamespaces' + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + $Repository = Get-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' + $DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' + + # + # Filter the ACLs that pertain to the Git Repository + + $searchString = "repoV2/{0}/{1}" -f $Project.id, $Repository.id + + # Test if the Token exists + $Filtered = $DescriptorACLList | Where-Object { $_.token -eq $searchString } + + # If the ACLs are not null, remove them + if ($Filtered) { + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SecurityNamespaceID = $SecurityNamespace.namespaceId + TokenName = $searchString + } + + # Remove the ACLs + Remove-xAzDoPermission @params + + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.ps1 new file mode 100644 index 000000000..073729c89 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.ps1 @@ -0,0 +1,70 @@ +Function Set-xAzDoGroupPermission { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$GroupName, + + [Parameter(Mandatory)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + + Write-Verbose "[Set-xAzDoGroupPermission] Started." + + # + # Format the Group Name + + # Split the Group Name + $split = $GroupName.Split('\').Split('/') + + # Test if the Group Name is valid + if ($split.Count -ne 2) { + Write-Warning "[Get-xAzDoProjectGroupPermission] Invalid Group Name: $GroupName" + return + } + + # Define the Project and Group Name + $ProjectName = $split[0] + $GroupName = $split[1] + + + # + # Security Namespace ID + + $SecurityNamespace = Get-CacheItem -Key 'Identity' -Type 'SecurityNamespaces' + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + # + # Serialize the ACLs + + $serializeACLParams = @{ + ReferenceACLs = $LookupResult.propertiesChanged + DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' + DescriptorMatchToken = ($LocalizedDataAzSerializationPatten.GitRepository -f $Project.id) + } + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SecurityNamespaceID = $SecurityNamespace.namespaceId + SerializedACLs = ConvertTo-ACLHashtable @serializeACLParams + } + + # + # Set the Git Repository Permissions + + Set-xAzDoPermission @params + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..3608ad729 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.ps1 @@ -0,0 +1,201 @@ +<# +.SYNOPSIS +Retrieves an organization group from Azure DevOps. + +.DESCRIPTION +The Get-xAzDoOrganizationGroup function retrieves an organization group from Azure DevOps based on the provided parameters. + +.PARAMETER ApiUri +The URI of the Azure DevOps API. This parameter is validated using the Test-AzDevOpsApiUri function. + +.PARAMETER Pat +The Personal Access Token (PAT) used for authentication. This parameter is validated using the Test-AzDevOpsPat function. + +.PARAMETER GroupName +The name of the organization group to retrieve. + +.OUTPUTS +[System.Management.Automation.PSObject[]] +The retrieved organization group. + +.EXAMPLE +Get-xAzDoOrganizationGroup -ApiUri 'https://dev.azure.com/contoso' -Pat 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx' -GroupName 'Developers' +Retrieves the organization group named 'Developers' from the Azure DevOps instance at 'https://dev.azure.com/contoso' using the provided PAT. + +#> + +Function Get-xAzDoOrganizationGroup { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure + + ) + + # Logging + Write-Verbose "[Get-xAzDoOrganizationGroup] Retriving the GroupName from the Live and Local Cache." + + # + # Format the Key According to the Principal Name + $Key = Format-AzDoGroup -Prefix "[$Global:DSCAZDO_OrganizationName]" -GroupName $GroupName + + # + # Check the cache for the group + $livegroup = Get-CacheItem -Key $Key -Type 'LiveGroups' + + # + # Check if the group is in the cache + $localgroup = Get-CacheItem -Key $Key -Type 'Group' + + + Write-Verbose "[Get-xAzDoOrganizationGroup] GroupName: '$GroupName'" + + # + # Construct a hashtable detailing the group + $getGroupResult = @{ + #Reasons = $() + Ensure = [Ensure]::Absent + localCache = $localgroup + liveCache = $livegroup + propertiesChanged = @() + status = $null + } + + Write-Verbose "[Get-xAzDoOrganizationGroup] Testing LocalCache, LiveCache and Parameters." + + # + # If the localgroup and lifegroup are present, compare the properties as well as the originId + if (($null -ne $livegroup.originId) -and ($null -ne $localgroup.originId)) { + + Write-Verbose "[Get-xAzDoOrganizationGroup] Testing LocalCache, LiveCache and Parameters." + + # + # Check if the originId is the same. If so, the group is unchanged. If not, the group has been renamed. + + if ($livegroup.originId -ne $localgroup.originId) { + # The group has been renamed or deleted and recreated. + + # Perform a lookup in the live cache to see if the group has been deleted and recreated. + $renamedGroup = $livegroup | Find-CacheItem { $_.originId -eq $livegroup.originId } + + # If renamed group is not null, the group has been renamed. + if ($null -ne $renamedGroup) { + # Add the renamed group to result + $getGroupResult.renamedGroup = $renamedGroup + # The group has been renamed. + $getGroupResult.status = [DSCGetSummaryState]::Renamed + + # Add the reason + #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:RenamedGroup', 'The group was renamed') + + } else { + # The group has been deleted and recreated. Treat the new group as the live group. + + # Remove the old group from the local cache + Remove-CacheItem -Key $Key -Type 'Group' + # Add the new group to the local cache + Add-CacheItem -Key $Key -Value $livegroup -Type 'Group' + + # Compare the properties of the live group with the parameters + if ($livegroup.description -ne $groupDescription) { $getGroupResult.propertiesChanged += 'description' } + if ($livegroup.name -ne $localgroup.name) { $getGroupResult.propertiesChanged += 'displayName' } + + # If the properties are the same, the group is unchanged. If not, the group has been changed. + if ($getGroupResult.propertiesChanged.count -ne 0) { + # Update the Result + $getGroupResult.status = [DSCGetSummaryState]::Changed + # Add the reason + #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Deleted&ReCreate', 'The group was deleted and recreated with another group. The properties have changed') + } else { + # Update the Result + $getGroupResult.status = [DSCGetSummaryState]::Unchanged + } + + } + + return $getGroupResult + + } + + # + # The Group hasn't been renamed. Test the properties to make sure they are the same as the parameters. + + # Compare the properties of the live group with the parameters + if ($livegroup.description -ne $groupDescription) { $getGroupResult.propertiesChanged += 'Description' } + if ($livegroup.name -ne $localgroup.name) { $getGroupResult.propertiesChanged += 'Name' } + + # If the properties are the same, the group is unchanged. If not, the group has been changed. + $getGroupResult.status = ($getGroupResult.propertiesChanged.count -ne 0) ? [DSCGetSummaryState]::Changed : [DSCGetSummaryState]::Unchanged + if ($getGroupResult.status -eq [DSCGetSummaryState]::Changed) { + # Add the reason + #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Changed', 'The group has changed') + } else { + $getGroupResult.Ensure = [Ensure]::Present + } + + # Return the group from the cache + return $getGroupResult + + } + + # + # If the livegroup is not present and the localgroup is present, the group is missing and recreate it. + if (($null -eq $livegroup) -and ($null -ne $localgroup)) { + $getGroupResult.status = [DSCGetSummaryState]::NotFound + $getGroupResult.propertiesChanged = @('description', 'displayName') + # Add the reason + #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Removed', 'The group is missing') + return $getGroupResult + } + + # + # If the localgroup is not present and the livegroup is present, the group is not found. Check the properties are the same as the parameters. + # If the properties are the same, the group is unchanged. If not, the group has been deleted and then recreated and the new group will become authoritative. + # + if (($null -eq $localgroup) -and ($null -ne $livegroup)) { + + # Validate that the live properties are the same as the parameters + if ($livegroup.description -ne $GroupDescription ) { $getGroupResult.propertiesChanged += 'description' } + if ($livegroup.displayName -ne $GroupName ) { $getGroupResult.propertiesChanged += 'displayName' } + # If the properties are the same, the group is unchanged. If not, the group has been changed. + $getGroupResult.status = ($getGroupResult.propertiesChanged.count -ne 0) ? [DSCGetSummaryState]::Changed : [DSCGetSummaryState]::Unchanged + if ($getGroupResult.status -ne [DSCGetSummaryState]::Unchanged) { + # Add the reason + #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Missing', 'The group is missing') + # Set the Ensure to Present + $getGroupResult.Ensure = [Ensure]::Present + } else { + # Add the unchanged group to the local cache + Add-CacheItem -Key $Key -Value $livegroup -Type 'Group' + } + + # Return the group from the cache + return $getGroupResult + + } + + # + # If the livegroup and localgroup are not present, the group is missing and recreate it. + if (($null -eq $livegroup) -and ($null -eq $localgroup)) { + $getGroupResult.status = [DSCGetSummaryState]::NotFound + $getGroupResult.propertiesChanged = @('description', 'displayName') + # Add the reason + #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:NotFound', 'The group is not found') + return $getGroupResult + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..936aec29f --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.ps1 @@ -0,0 +1,53 @@ +Function New-xAzDoOrganizationGroup { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + # Define parameters for creating a new DevOps group + $params = @{ + GroupName = $GroupName + GroupDescription = $GroupDescription + ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName + } + + # Write verbose log with the parameters used for creating the group + Write-Verbose "[New-xAzDoOrganizationGroup] Creating a new DevOps group with GroupName: '$($params.GroupName)', GroupDescription: '$($params.GroupDescription)' and ApiUri: '$($params.ApiUri)'" + + # Create a new group + $group = New-DevOpsGroup @params + + # Update the cache with the new group + Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' + + # Add the group to the Group cache and write to verbose log + Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' + Write-Verbose "[New-xAzDoOrganizationGroup] Added new group to Group cache with key: '$($group.principalName)'" + + # Update the global AzDoGroup object and write to verbose log + Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' + Write-Verbose "[New-xAzDoOrganizationGroup] Updated global AzDoGroup cache object." + + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..2f48323a2 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.ps1 @@ -0,0 +1,61 @@ +Function Remove-xAzDoOrganizationGroup { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # If no cache items exist, return. + if (($null -eq $LookupResult.liveCache) -and ($null -eq $LookupResult.localCache)) { + return + } + + $params = @{ + GroupDescriptor = $LookupResult.liveCache.Descriptor + ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName + } + + $cacheItem = @{ + Key = $LookupResult.liveCache.principalName + } + + # If the group is not found, return + if (($null -ne $LookupResult.localCache) -and ($null -eq $LookupResult.liveCache)) { + $cacheItem.Key = $LookupResult.localCache.principalName + $params.GroupDescriptor = $LookupResult.localCache.Descriptor + } + + # + # Remove the group from the API + $null = Remove-DevOpsGroup @params + + # + # Remove the group from the API + + Remove-CacheItem @cacheItem -Type 'LiveGroups' + Set-CacheObject -Content $Global:AZDOLiveGroups -CacheType 'LiveGroups' + + Remove-CacheItem @cacheItem -Type 'Group' + Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..adc0ed7aa --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.ps1 @@ -0,0 +1,69 @@ +Function Set-xAzDoOrganizationGroup { + + param( + + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + # + # Depending on the type of lookup status, the group has been renamed the group has been deleted and recreated. + if ($LookupResult.Status -eq [DSCGetSummaryState]::Renamed) { + + # For the time being write a warning and return + Write-Warning "[Set-xAzDoOrganizationGroup] The group has been renamed. The group will not be set." + return + + } + + # + # Update the group + $params = @{ + ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName + GroupName = $GroupName + GroupDescription = $GroupDescription + GroupDescriptor = $LookupResult.liveCache.descriptor + } + + try { + # Set the group from the API + $group = Set-DevOpsGroup @params + } catch { + throw $_ + } + + # + # Update the cache with the new group + Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' + + # + # Secondarily Replace the local cache with the new group + + if ($null -ne $LookupResult.localCache) { + Remove-CacheItem -Key $LookupResult.localCache.principalName -Type 'Group' + } + Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' + Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' + + # + # Return the group from the cache + return $group + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..b846fe0ef --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.ps1 @@ -0,0 +1,106 @@ +<# +.SYNOPSIS + Tests if an organization group exists in Azure DevOps. + +.DESCRIPTION + The Test-AzDoOrganizationGroup function checks if a specified organization group exists in Azure DevOps. + It uses a personal access token (PAT) and the Azure DevOps API to perform the check. + +.PARAMETER GroupName + Specifies the name of the organization group to test. + +.PARAMETER Pat + Specifies the personal access token (PAT) to authenticate with Azure DevOps. + The PAT is validated using the Test-AzDevOpsPat function. + +.PARAMETER ApiUri + Specifies the URI of the Azure DevOps API to connect to. + The URI is validated using the Test-AzDevOpsApiUri function. + +.OUTPUTS + System.Boolean + Returns $true if the organization group exists, otherwise returns $false. + +.EXAMPLE + Test-xAzDoOrganizationGroup -GroupName 'MyGroup' -Pat '********' -ApiUri 'https://dev.azure.com/myorg' + + Description + ----------- + Tests if the organization group named 'MyGroup' exists in the Azure DevOps organization 'myorg' + using the specified personal access token and API URI. + +#> +Function Test-xAzDoOrganizationGroup { + + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] + $GroupName, + + [Parameter()] + [string] + $GroupDescription=$null, + + [Parameter()] + [Alias('Name')] + [hashtable]$GetResult + + ) + + # + # Firstly we need to compare to see if the group names are the same. If so we can return $false. + + if ($GetResult.Status -eq [DSCGetSummaryState]::Unchanged ) { + + $result = $true + + if ($GroupDescription -eq $GetResult.Current.description) + { + $GetResult. + $result = $false + } + + return $true } + + # + # If the status has been flagged as 'Renamed', returned $true. This means that the originId has changed. + if ($GetResult.Status -eq [DSCGetSummaryState]::Renamed) { return $false } + + # + # If the status has been flagged as 'Missing', returned $true. This means that the group is missing from the live cache. + + + + if ($GetResult.Status -eq [DSCGetSummaryState]::Changed) { + + # + # If the group is present in the live cache and the local cache. This means that the originId has changed. This needs to be updated. + if (($null -ne $GetResult.Current) -and ($null -ne $GetResult.Cache)) { + return $true + } + + # + # If the group is present in the live cache but not in the local cache. Flag as Changed. + if ($GetResult.Current -and -not($GetResult.Cache)) { + return $true + } + + # + # If the group is not present in the live cache but is in the local cache. Flag as Changed. + if (-not($GetResult.Current) -and $GetResult.Cache) { + return $true + } + + } + + + # Format the Key According to the Principal Name + $Key = Format-AzDoGroup -Prefix "[$Global:DSCAZDO_OrganizationName]" -GroupName $GroupName + + # + # Check the cache for the group + $group = Get-CacheItem -Key $Key -Type 'LiveGroups' + if (-not($group)) { $false } else { $true } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.ps1 new file mode 100644 index 000000000..56ca572c2 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.ps1 @@ -0,0 +1,118 @@ +function Get-xAzDoProject +{ + [CmdletBinding()] + param ( + [Parameter()] + [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] + [Alias('Name')] + [System.String] $ProjectName, + + [Parameter()] + [Alias('Description')] + [System.String] $ProjectDescription = '', + + [Parameter()] + [ValidateSet('Git', 'Tfvc')] + [System.String] $SourceControlType = 'Git', + + [Parameter()] + [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] + [System.String] $ProcessTemplate = 'Agile', + + [Parameter()] + [ValidateSet('Public', 'Private')] + [System.String] $Visibility = 'Private', + + [Parameter()] + [HashTable] $LookupResult, + + [Parameter()] + [Ensure] $Ensure + ) + + Write-Verbose "[Get-xAzDoProject] Started." + + # Set the organization name + $OrganizationName = $Global:DSCAZDO_OrganizationName + Write-Verbose "[Get-xAzDoProject] Organization Name: $OrganizationName" + + # Construct a hashtable detailing the group + $result = @{ + Ensure = [Ensure]::Absent + ProjectName = $ProjectName + ProjectDescription = $ProjectDescription + SourceControlType = $SourceControlType + ProcessTemplate = $ProcessTemplate + Visibility = $Visibility + propertiesChanged = @() + status = $null + } + Write-Verbose "[Get-xAzDoProject] Initial result hashtable constructed." + + # Perform a lookup to see if the project exists in Azure DevOps + $project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + # Set the project description to be a string if it is not already. + Write-Verbose "[Get-xAzDoProject] Project lookup result: $project" + + $processTemplateObj = Get-CacheItem -Key $ProcessTemplate -Type 'LiveProcesses' + Write-Verbose "[Get-xAzDoProject] Process template lookup result: $processTemplateObj" + + # Test if the project exists. If the project does not exist, return NotFound + if (($null -eq $project) -and ($null -ne $ProjectName)) + { + $result.Status = [DSCGetSummaryState]::NotFound + Write-Verbose "[Get-xAzDoProject] Project not found." + return $result + } + + # Test if the process template exists. If the process template does not exist, throw an error. + if ($null -eq $processTemplateObj) + { + throw "[Get-xAzDoProject] Process template '$processTemplateObj' not found." + } + + Write-Verbose "[Get-xAzDoProject] Testing source control type." + + # Test if the project is using the same source control type. If the source control type is different, return a conflict. + if ($SourceControlType -ne $project.SourceControlType) + { + Write-Warning "[Get-xAzDoProject] Source control type is different. Current: $($project.SourceControlType), Desired: $SourceControlType" + Write-Warning "[Get-xAzDoProject] Source control type cannot be changed. Please delete the project and recreate it." + } + + # If the project description is null, set it to an empty string. + if ($null -eq $project.description) + { + $project | Add-Member -MemberType NoteProperty -Name description -Value '' + Write-Verbose "[Get-xAzDoProject] Project description was null, set to empty string." + } + + # Test if the project description is the same. If the description is different, return a conflict. + if ($ProjectDescription.Trim() -ne $project.description.Trim()) + { + $result.Status = [DSCGetSummaryState]::Changed + $result.propertiesChanged += 'Description' + Write-Verbose "[Get-xAzDoProject] Project description has changed." + } + + <# + # Test if the project is using the same process template. If the process template is different, return a conflict. + if ($ProcessTemplate -ne $project.ProcessTemplate) + { + $result.Status = [DSCGetSummaryState]::Changed + $result.propertiesChanged += 'ProcessTemplate' + } + #> + + # Test if the project visibility is the same. If the visibility is different, return a conflict. + if ($Visibility -ne $project.Visibility) + { + $result.Status = [DSCGetSummaryState]::Changed + $result.propertiesChanged += 'Visibility' + Write-Verbose "[Get-xAzDoProject] Project visibility has changed." + } + + # Return the group from the cache + Write-Verbose "[Get-xAzDoProject] Returning final result." + return $result +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.ps1 new file mode 100644 index 000000000..e9d5c92c6 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.ps1 @@ -0,0 +1,77 @@ +function New-xAzDoProject +{ + [CmdletBinding()] + param + ( + [Parameter()] + [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] + [Alias('Name')] + [System.String] + $ProjectName, + + [Parameter()] + [Alias('Description')] + [System.String] + $ProjectDescription, + + [Parameter()] + [ValidateSet('Git','Tfvc')] + [System.String] + $SourceControlType = 'Git', + + [Parameter()] + [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] + [System.String]$ProcessTemplate = 'Agile', + + [Parameter()] + [ValidateSet('Public', 'Private')] + [System.String]$Visibility = 'Private', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + # Set the organization name + $OrganizationName = $Global:DSCAZDO_OrganizationName + + # + # Perform a lookup to see if the group exists in Azure DevOps + $processTemplateObj = Get-CacheItem -Key $ProcessTemplate -Type 'LiveProcesses' + + # + # Construct the parameters for the API call + $parameters = @{ + organization = $OrganizationName + projectName = $ProjectName + description = $ProjectDescription + sourceControlType = $SourceControlType + processTemplateId = $processTemplateObj.id + visibility = $Visibility + } + + # + # Create the project + + $projectJob = New-DevOpsProject @parameters + + # + # Wait for the project to be created + + Wait-DevOpsProject -ProjectURL $projectJob.url -OrganizationName $OrganizationName + + # + # Once the project has been created, refresh the entire cache. + + Refresh-AzDoCache -OrganizationName $OrganizationName + +} + + diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.ps1 new file mode 100644 index 000000000..43f27185d --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.ps1 @@ -0,0 +1,62 @@ +function Remove-xAzDoProject +{ + [CmdletBinding()] + param ( + [Parameter()] + [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] + [Alias('Name')] + [System.String] $ProjectName, + + [Parameter()] + [Alias('Description')] + [System.String] $ProjectDescription, + + [Parameter()] + [ValidateSet('Git', 'Tfvc')] + [System.String] $SourceControlType = 'Git', + + [Parameter()] + [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] + [System.String] $ProcessTemplate = 'Agile', + + [Parameter()] + [ValidateSet('Public', 'Private')] + [System.String] $Visibility = 'Private', + + [Parameter()] + [HashTable] $LookupResult, + + [Parameter()] + [Ensure] $Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] $Force + ) + + # Set the organization name + $OrganizationName = $Global:DSCAZDO_OrganizationName + Write-Verbose "[Remove-xAzDoProject] Using organization name: $OrganizationName" + + # Perform a lookup to see if the group exists in Azure DevOps + Write-Verbose "[Remove-xAzDoProject] Looking up project: $ProjectName" + $project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + if ($null -eq $project) + { + Write-Verbose "[Remove-xAzDoProject] Project $ProjectName not found in cache." + return + } + + Write-Verbose "[Remove-xAzDoProject] Found project $ProjectName with ID: $($project.id)" + + # Remove the project + Write-Verbose "[Remove-xAzDoProject] Removing project $ProjectName from Azure DevOps" + Remove-DevOpsProject -Organization $OrganizationName -ProjectId $project.id + + # Remove the project from the cache and export the cache + Write-Verbose "[Remove-xAzDoProject] Removing project $ProjectName from local cache" + Remove-CacheItem -Key $ProjectName -Type 'LiveProjects' + + Write-Verbose "[Remove-xAzDoProject] Exporting updated cache object for LiveProjects" + Export-CacheObject -CacheType 'LiveProjects' -Content $AzDoLiveProjects +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.ps1 new file mode 100644 index 000000000..8b4373973 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.ps1 @@ -0,0 +1,74 @@ +function Set-xAzDoProject +{ + [CmdletBinding()] + param + ( + [Parameter()] + [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] + [Alias('Name')] + [System.String] + $ProjectName, + + [Parameter()] + [Alias('Description')] + [System.String] + $ProjectDescription, + + [Parameter()] + [ValidateSet('Git','Tfvc')] + [System.String] + $SourceControlType = 'Git', + + [Parameter()] + [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] + [System.String]$ProcessTemplate = 'Agile', + + [Parameter()] + [ValidateSet('Public', 'Private')] + [System.String]$Visibility = 'Private', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + $OrganizationName = $Global:DSCAZDO_OrganizationName + + # + # Perform a lookup to see if the group exists in Azure DevOps + $project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + $processTemplateObj = Get-CacheItem -Key $ProcessTemplate -Type 'LiveProcesses' + + # + # Construct the parameters for the API call + $parameters = @{ + organization = $OrganizationName + projectId = $project.id + description = $ProjectDescription + processTemplateId = $processTemplateObj.id + visibility = $Visibility + } + + # + # Update the project + + $projectJob = Update-DevOpsProject @parameters + + # + # Wait for the project to be updated + + Wait-DevOpsProject -ProjectURL $projectJob.url -OrganizationName $OrganizationName + + # + # Once the project has been created, refresh the entire cache. + + Refresh-AzDoCache -OrganizationName $OrganizationName + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.ps1 new file mode 100644 index 000000000..30f7dbdf6 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.ps1 @@ -0,0 +1,44 @@ +function Test-xAzDoProject +{ + [CmdletBinding()] + param + ( + [Parameter()] + [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] + [Alias('Name')] + [System.String] + $ProjectName, + + [Parameter()] + [Alias('Description')] + [System.String] + $ProjectDescription, + + [Parameter()] + [ValidateSet('Git','Tfvc')] + [System.String] + $SourceControlType = 'Git', + + [Parameter()] + [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] + [System.String]$ProcessTemplate = 'Agile', + + [Parameter()] + [ValidateSet('Public', 'Private')] + [System.String]$Visibility = 'Private', + + [Parameter()] + [PSCustomObject]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + # Should not be triggered. This is a placeholder for the test function. + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.ps1 new file mode 100644 index 000000000..fbb9d8135 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.ps1 @@ -0,0 +1,207 @@ +<# +.SYNOPSIS +Retrieves an organization group from Azure DevOps. + +.DESCRIPTION +The Get-AzDoProjectGroup function retrieves an organization group from Azure DevOps based on the provided parameters. + +.PARAMETER ApiUri +The URI of the Azure DevOps API. This parameter is validated using the Test-AzDevOpsApiUri function. + +.PARAMETER Pat +The Personal Access Token (PAT) used for authentication. This parameter is validated using the Test-AzDevOpsPat function. + +.PARAMETER GroupName +The name of the organization group to retrieve. + +.OUTPUTS +[System.Management.Automation.PSObject[]] +The retrieved organization group. + +.EXAMPLE +Get-AzDoProjectGroup -ApiUri 'https://dev.azure.com/contoso' -Pat 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx' -GroupName 'Developers' +Retrieves the organization group named 'Developers' from the Azure DevOps instance at 'https://dev.azure.com/contoso' using the provided PAT. + +#> + +Function Get-xAzDoProjectGroup { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + + [Parameter(Mandatory)] + [Alias('Project')] + [System.String]$ProjectName, + + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure + + ) + + # Logging + Write-Verbose "[Get-AzDoProjectGroup] Retriving the GroupName from the Live and Local Cache." + + # + # Format the Key According to the Principal Name + $Key = Format-AzDoGroup -Prefix "[$ProjectName]" -GroupName $GroupName + + # + # Check the cache for the group + $livegroup = Get-CacheItem -Key $Key -Type 'LiveGroups' + + # + # Check if the group is in the cache + $localgroup = Get-CacheItem -Key $Key -Type 'Group' + + # + # Retrive the Project + $project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + Write-Verbose "[Get-AzDoProjectGroup] GroupName: '$GroupName'" + + # + # Construct a hashtable detailing the group + $getGroupResult = @{ + #Reasons = $() + Ensure = [Ensure]::Absent + localCache = $localgroup + liveCache = $livegroup + propertiesChanged = @() + status = $null + project = $project + } + + Write-Verbose "[Get-AzDoProjectGroup] Testing LocalCache, LiveCache and Parameters." + + # + # If the localgroup and lifegroup are present, compare the properties as well as the originId + if (($null -ne $livegroup.originId) -and ($null -ne $localgroup.originId)) { + + Write-Verbose "[Get-AzDoProjectGroup] Testing LocalCache, LiveCache and Parameters." + + # + # Check if the originId is the same. If so, the group is unchanged. If not, the group has been renamed. + + if ($livegroup.originId -ne $localgroup.originId) { + # The group has been renamed or deleted and recreated. + + # Perform a lookup in the live cache to see if the group has been deleted and recreated. + $renamedGroup = $livegroup | Find-CacheItem -Filter { $_.originId -eq $livegroup.originId } + + # If renamed group is not null, the group has been renamed. + if ($null -ne $renamedGroup) { + # Add the renamed group to result + $getGroupResult.renamedGroup = $renamedGroup + # The group has been renamed. + $getGroupResult.status = [DSCGetSummaryState]::Renamed + + } else { + # The group has been deleted and recreated. Treat the new group as the live group. + + # Remove the old group from the local cache + Remove-CacheItem -Key $Key -Type 'Group' + # Add the new group to the local cache + Add-CacheItem -Key $Key -Value $livegroup -Type 'Group' + + # Compare the properties of the live group with the parameters + if ($livegroup.description -ne $groupDescription) { $getGroupResult.propertiesChanged += 'description' } + if ($livegroup.name -ne $localgroup.name) { $getGroupResult.propertiesChanged += 'displayName' } + + # If the properties are the same, the group is unchanged. If not, the group has been changed. + if ($getGroupResult.propertiesChanged.count -ne 0) { + # Update the Result + $getGroupResult.status = [DSCGetSummaryState]::Changed + # Add the reason + } else { + # Update the Result + $getGroupResult.status = [DSCGetSummaryState]::Unchanged + } + + } + + return $getGroupResult + + } + + # + # The Group hasn't been renamed. Test the properties to make sure they are the same as the parameters. + + # Compare the properties of the live group with the parameters + if ($livegroup.description -ne $groupDescription) { $getGroupResult.propertiesChanged += 'Description' } + if ($livegroup.name -ne $localgroup.name) { $getGroupResult.propertiesChanged += 'Name' } + + # If the properties are the same, the group is unchanged. If not, the group has been changed. + $getGroupResult.status = ($getGroupResult.propertiesChanged.count -ne 0) ? [DSCGetSummaryState]::Changed : [DSCGetSummaryState]::Unchanged + if ($getGroupResult.status -eq [DSCGetSummaryState]::Changed) { + # Add the reason + #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Changed', 'The group has changed') + } else { + $getGroupResult.Ensure = [Ensure]::Present + } + + # Return the group from the cache + return $getGroupResult + + } + + # + # If the livegroup is not present and the localgroup is present, the group is missing and recreate it. + if (($null -eq $livegroup) -and ($null -ne $localgroup)) { + $getGroupResult.status = [DSCGetSummaryState]::NotFound + $getGroupResult.propertiesChanged = @('description', 'displayName') + # Add the reason + #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Removed', 'The group is missing') + return $getGroupResult + } + + # + # If the localgroup is not present and the livegroup is present, the group is not found. Check the properties are the same as the parameters. + # If the properties are the same, the group is unchanged. If not, the group has been deleted and then recreated and the new group will become authoritative. + # + if (($null -eq $localgroup) -and ($null -ne $livegroup)) { + + # Validate that the live properties are the same as the parameters + if ($livegroup.description -ne $GroupDescription ) { $getGroupResult.propertiesChanged += 'description' } + if ($livegroup.displayName -ne $GroupName ) { $getGroupResult.propertiesChanged += 'displayName' } + # If the properties are the same, the group is unchanged. If not, the group has been changed. + $getGroupResult.status = ($getGroupResult.propertiesChanged.count -ne 0) ? [DSCGetSummaryState]::Changed : [DSCGetSummaryState]::Unchanged + + if ($getGroupResult.status -ne [DSCGetSummaryState]::Unchanged) { + # Add the reason + #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Missing', 'The group is missing') + # Set the Ensure to Present + $getGroupResult.Ensure = [Ensure]::Present + } else { + # Add the unchanged group to the local cache + Add-CacheItem -Key $Key -Value $livegroup -Type 'Group' + } + + # Return the group from the cache + return $getGroupResult + + } + + # + # If the livegroup and localgroup are not present, the group is missing and recreate it. + if (($null -eq $livegroup) -and ($null -eq $localgroup)) { + $getGroupResult.status = [DSCGetSummaryState]::NotFound + $getGroupResult.propertiesChanged = @('description', 'displayName') + # Add the reason + #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:NotFound', 'The group is not found') + return $getGroupResult + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.ps1 new file mode 100644 index 000000000..a2b9f4c5e --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.ps1 @@ -0,0 +1,64 @@ +Function New-xAzDoProjectGroup { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter(Mandatory)] + [Alias('Project')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + # Define parameters for creating a new DevOps group + $params = @{ + GroupName = $GroupName + GroupDescription = $GroupDescription + ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName + ProjectScopeDescriptor = (Get-CacheItem -Key $ProjectName -Type 'LiveProjects').ProjectDescriptor + } + + # If the project scope descriptor is not found, write a warning message to the console and return. + if ($null -eq $params.ProjectScopeDescriptor) { + Write-Warning "[New-xAzDoProjectGroup] Unable to find project scope descriptor for project '$ProjectName'. Aborting group creation." + return + } + + # Write verbose log before creating a new group + Write-Verbose "[New-xAzDoProjectGroup] Creating a new DevOps group with the following parameters: $($params | Out-String)" + + # Create a new group + $group = New-DevOpsGroup @params + + # Write verbose log after group creation + Write-Verbose "[New-xAzDoProjectGroup] New DevOps group created: $($group | Out-String)" + + # Update the cache with the new group + Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' + + Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' + Write-Verbose "[New-xAzDoProjectGroup] Added new group to Group cache with key: $($group.principalName)" + + Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' + Write-Verbose "[New-xAzDoProjectGroup] Updated global AzDoGroup cache object." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.ps1 new file mode 100644 index 000000000..944564ef4 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.ps1 @@ -0,0 +1,65 @@ +Function Remove-xAzDoProjectGroup { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter(Mandatory)] + [Alias('Project')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # If no cache items exist, return. + if (($null -eq $LookupResult.liveCache) -and ($null -eq $LookupResult.localCache)) { + return + } + + $params = @{ + GroupDescriptor = $LookupResult.liveCache.Descriptor + ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName + } + + $cacheItem = @{ + Key = $LookupResult.liveCache.principalName + } + + # If the group is not found, return + if (($null -ne $LookupResult.localCache) -and ($null -eq $LookupResult.liveCache)) { + $cacheItem.Key = $LookupResult.localCache.principalName + $params.GroupDescriptor = $LookupResult.localCache.Descriptor + } + + # + # Remove the group from the API + $null = Remove-DevOpsGroup @params + + # + # Remove the group from the API + + Remove-CacheItem @cacheItem -Type 'LiveGroups' + Set-CacheObject -Content $Global:AZDOLiveGroups -CacheType 'LiveGroups' + + Remove-CacheItem @cacheItem -Type 'Group' + Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.ps1 new file mode 100644 index 000000000..7d19525ef --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.ps1 @@ -0,0 +1,74 @@ +Function Set-xAzDoProjectGroup { + + param( + + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter(Mandatory)] + [Alias('Project')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + + ) + + # + # Depending on the type of lookup status, the group has been renamed the group has been deleted and recreated. + if ($LookupResult.Status -eq [DSCGetSummaryState]::Renamed) { + + # For the time being write a warning and return + Write-Warning "[Set-AzDoProjectGroup] The group has been renamed. The group will not be set." + return + + } + + # + # Update the group + $params = @{ + ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName + GroupName = $GroupName + GroupDescription = $GroupDescription + GroupDescriptor = $LookupResult.liveCache.descriptor + } + + try { + # Set the group from the API + $group = Set-DevOpsGroup @params + } catch { + throw $_ + } + + # + # Firstly Replace the live cache with the new group + + # Update the cache with the new group + Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' + + # + # Secondarily Replace the local cache with the new group + if ($null -ne $LookupResult.localCache) { + Remove-CacheItem -Key $LookupResult.localCache.principalName -Type 'Group' + } + Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' + Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' + + # + # Return the group from the cache + return $group + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.ps1 new file mode 100644 index 000000000..b3891ae0d --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.ps1 @@ -0,0 +1,110 @@ +<# +.SYNOPSIS + Tests if an organization group exists in Azure DevOps. + +.DESCRIPTION + The Test-AzDoOrganizationGroup function checks if a specified organization group exists in Azure DevOps. + It uses a personal access token (PAT) and the Azure DevOps API to perform the check. + +.PARAMETER GroupName + Specifies the name of the organization group to test. + +.PARAMETER Pat + Specifies the personal access token (PAT) to authenticate with Azure DevOps. + The PAT is validated using the Test-AzDevOpsPat function. + +.PARAMETER ApiUri + Specifies the URI of the Azure DevOps API to connect to. + The URI is validated using the Test-AzDevOpsApiUri function. + +.OUTPUTS + System.Boolean + Returns $true if the organization group exists, otherwise returns $false. + +.EXAMPLE + Test-xAzDoOrganizationGroup -GroupName 'MyGroup' -Pat '********' -ApiUri 'https://dev.azure.com/myorg' + + Description + ----------- + Tests if the organization group named 'MyGroup' exists in the Azure DevOps organization 'myorg' + using the specified personal access token and API URI. + +#> +Function Test-xAzDoProjectGroup { + + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] + $GroupName, + + [Parameter()] + [string] + $GroupDescription=$null, + + [Parameter()] + [Alias('Project')] + [hashtable]$ProjectName, + + [Parameter()] + [Alias('Name')] + [hashtable]$GetResult + + ) + + # + # Firstly we need to compare to see if the group names are the same. If so we can return $false. + + if ($GetResult.Status -eq [DSCGetSummaryState]::Unchanged ) { + + $result = $true + + if ($GroupDescription -eq $GetResult.Current.description) + { + $GetResult. + $result = $false + } + + return $true } + + # + # If the status has been flagged as 'Renamed', returned $true. This means that the originId has changed. + if ($GetResult.Status -eq [DSCGetSummaryState]::Renamed) { return $false } + + # + # If the status has been flagged as 'Missing', returned $true. This means that the group is missing from the live cache. + + + + if ($GetResult.Status -eq [DSCGetSummaryState]::Changed) { + + # + # If the group is present in the live cache and the local cache. This means that the originId has changed. This needs to be updated. + if (($null -ne $GetResult.Current) -and ($null -ne $GetResult.Cache)) { + return $true + } + + # + # If the group is present in the live cache but not in the local cache. Flag as Changed. + if ($GetResult.Current -and -not($GetResult.Cache)) { + return $true + } + + # + # If the group is not present in the live cache but is in the local cache. Flag as Changed. + if (-not($GetResult.Current) -and $GetResult.Cache) { + return $true + } + + } + + + # Format the Key According to the Principal Name + $Key = Format-AzDoGroup -Prefix "[$Global:DSCAZDO_OrganizationName]" -GroupName $GroupName + + # + # Check the cache for the group + $group = Get-CacheItem -Key $Key -Type 'LiveGroups' + if (-not($group)) { $false } else { $true } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Get-xAzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Get-xAzDoProjectServices.ps1 new file mode 100644 index 000000000..6931c07e0 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Get-xAzDoProjectServices.ps1 @@ -0,0 +1,124 @@ +Function Get-xAzDoProjectServices { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Repos')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$GitRepositories = 'Enabled', + + [Parameter()] + [Alias('Board')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$WorkBoards = 'Enabled', + + [Parameter()] + [Alias('Pipelines')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$BuildPipelines = 'Enabled', + + [Parameter()] + [Alias('Tests')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$TestPlans = 'Enabled', + + [Parameter()] + [Alias('Artifacts')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$AzureArtifact = 'Enabled', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # + # Construct a hashtable detailing the group + + $Result = @{ + #Reasons = $() + Ensure = [Ensure]::Absent + propertiesChanged = @() + status = [DSCGetSummaryState]::Unchanged + } + + # + # Attempt to retrive the Project from the Live Cache. + Write-Verbose "[Get-xAzDevOpsProjectServices] Retriving the Project from the Live Cache." + + # Retrive the Repositories from the Live Cache. + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + # If the Project does not exist in the Live Cache, return the Project object. + if ($null -eq $Project) { + Write-Warning "[Get-xAzDevOpsProjectServices] The Project '$ProjectName' was not found in the Live Cache." + $Result.Status = [DSCGetSummaryState]::NotFound + return $Result + } + + $params = @{ + Organization = $Global:DSCAZDO_OrganizationName + ProjectId = $Project.id + } + + # Enumerate the Project Services. + $Result.LiveServices = @{ + Repos = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Repos + Boards = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Boards + Pipelines = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Pipelines + Tests = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_TestPlans + Artifacts = Get-ProjectServiceStatus @params -ServiceName $LocalizedDataAzURLParams.ProjectService_Artifacts + } + + # Compare the Project Services with the desired state. + if ($GitRepositories -ne $Result.LiveServices.Repos.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $GitRepositories + FeatureId = $LocalizedDataAzURLParams.ProjectService_Repos + } + } + if ($WorkBoards -ne $Result.LiveServices.Boards.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $WorkBoards + FeatureId = $LocalizedDataAzURLParams.ProjectService_Boards + } + } + if ($BuildPipelines -ne $Result.LiveServices.Pipelines.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $BuildPipelines + FeatureId = $LocalizedDataAzURLParams.ProjectService_Pipelines + } + } + if ($TestPlans -ne $Result.LiveServices.Tests.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $TestPlans + FeatureId = $LocalizedDataAzURLParams.ProjectService_TestPlans + } + } + if ($AzureArtifact -ne $Result.LiveServices.Artifacts.state) { + $Result.Status = [DSCGetSummaryState]::Changed + $Result.propertiesChanged += @{ + Expected = $AzureArtifact + FeatureId = $LocalizedDataAzURLParams.ProjectService_Artifacts + } + } + + return $Result + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/New-xAzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/New-xAzDoProjectServices.ps1 new file mode 100644 index 000000000..66237dccd --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/New-xAzDoProjectServices.ps1 @@ -0,0 +1,48 @@ +Function New-xAzDoProjectServices { + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Repos')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$GitRepositories = 'Enabled', + + [Parameter()] + [Alias('Board')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$WorkBoards = 'Enabled', + + [Parameter()] + [Alias('Pipelines')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$BuildPipelines = 'Enabled', + + [Parameter()] + [Alias('Tests')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$TestPlans = 'Enabled', + + [Parameter()] + [Alias('Artifacts')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$AzureArtifact = 'Enabled', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Won't be triggered. + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Remove-xAzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Remove-xAzDoProjectServices.ps1 new file mode 100644 index 000000000..849e320f9 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Remove-xAzDoProjectServices.ps1 @@ -0,0 +1,48 @@ +Function Remove-xAzDoProjectServices { + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Repos')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$GitRepositories = 'Enabled', + + [Parameter()] + [Alias('Board')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$WorkBoards = 'Enabled', + + [Parameter()] + [Alias('Pipelines')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$BuildPipelines = 'Enabled', + + [Parameter()] + [Alias('Tests')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$TestPlans = 'Enabled', + + [Parameter()] + [Alias('Artifacts')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$AzureArtifact = 'Enabled', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Won't be triggered. + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Set-xAzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Set-xAzDoProjectServices.ps1 new file mode 100644 index 000000000..e6058ca7b --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Set-xAzDoProjectServices.ps1 @@ -0,0 +1,67 @@ +Function Set-xAzDoProjectServices { + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Repos')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$GitRepositories = 'Enabled', + + [Parameter()] + [Alias('Board')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$WorkBoards = 'Enabled', + + [Parameter()] + [Alias('Pipelines')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$BuildPipelines = 'Enabled', + + [Parameter()] + [Alias('Tests')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$TestPlans = 'Enabled', + + [Parameter()] + [Alias('Artifacts')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$AzureArtifact = 'Enabled', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Retrive the Repositories from the Live Cache. + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + # Construct a hashtable detailing the group + ForEach ($PropertyChanged in $LookupResult.propertiesChanged) { + + $params = @{ + Organization = $Global:DSCAZDO_OrganizationName + ProjectId = $Project.id + ServiceName = $PropertyChanged.FeatureId + Body = $LookupResult.LiveServices.Keys | Where-Object { $LookupResult.LiveServices[$_].featureId -eq $PropertyChanged.FeatureId } | ForEach-Object { $LookupResult.LiveServices[$_] } + } + + # Set the Project Service Status + $params.Body.state = ($PropertyChanged.Expected -eq 'Enabled') ? 1 : 0 + + Set-ProjectServiceStatus @params + + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Test-xAzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Test-xAzDoProjectServices.ps1 new file mode 100644 index 000000000..ea67dc312 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Test-xAzDoProjectServices.ps1 @@ -0,0 +1,48 @@ +Function Test-xAzDoProjectServices { + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Repos')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$GitRepositories = 'Enabled', + + [Parameter()] + [Alias('Board')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$WorkBoards = 'Enabled', + + [Parameter()] + [Alias('Pipelines')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$BuildPipelines = 'Enabled', + + [Parameter()] + [Alias('Tests')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$TestPlans = 'Enabled', + + [Parameter()] + [Alias('Artifacts')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$AzureArtifact = 'Enabled', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Won't be triggered. + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Initalize-AzDevOpsCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Initalize-AzDevOpsCache.ps1 new file mode 100644 index 000000000..6391c608f --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Initalize-AzDevOpsCache.ps1 @@ -0,0 +1,44 @@ +<# +.SYNOPSIS + Initializes the cache for Azure DevOps resources. + +.DESCRIPTION + This function initializes the cache for Azure DevOps resources by loading the cache from a file for each type. + The cache types include Project, Team, Group, and SecurityDescriptor. + +.PARAMETER None + This function does not accept any parameters. + +.EXAMPLE + Initialize-Cache + Initializes the cache for Azure DevOps resources. + +.NOTES + +#> +function Initialize-Cache +{ + [CmdletBinding()] + param () + + # Write initial verbose message + Write-Verbose "[Initialize-Cache] Starting cache initialization process." + + try { + # Attempt to load the cache from the file for each type + $cacheTypes = @('Project', 'Team', 'Group', 'SecurityDescriptor', 'LiveGroups', 'LiveProjects') + foreach ($cacheType in $cacheTypes) { + Write-Verbose "[Initialize-Cache] Initializing cache object of type: $cacheType" + Initialize-CacheObject -CacheType $cacheType + } + + # Confirm completion of cache initialization + Write-Verbose "[Initialize-Cache] Cache initialization process completed successfully." + + } catch { + + Write-Error "[Initialize-Cache] An error occurred during cache initialization: $_" + throw + + } +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 new file mode 100644 index 000000000..e69de29bb diff --git a/source/WikiSource/Home.md b/source/WikiSource/Home.md index a1cf946e9..6f736b10f 100644 --- a/source/WikiSource/Home.md +++ b/source/WikiSource/Home.md @@ -33,12 +33,20 @@ DSC resources available: Get-DscResource -Module AzureDevOpsDsc ``` +## DSC Resource Documentation + +* [xAzDoGitPermission](\Resources\xAzDoGitPermission.md) +* [xAzDoGitRepository](\Resources\xAzDoGitRepository.md) +* [xAzDoGroupMember](\Resources\xAzDoGroupMember.md) +* [xAzDoGroupPermission](\Resources\xAzDoGroupPermission.md) +* [xAzDoOrganizationGroup](\Resources\xAzDoOrganizationGroup.md) +* [xAzDoProject](\Resources\xAzDoProject.md) +* [xAzDoProjectGroup](\Resources\xAzDoProjectGroup.md) +* [xAzDoProjectServices](\Resources\xAzDoProjectServices.md) + ## Prerequisites -The minimum Windows Management Framework (PowerShell) version required is 5.0 -or higher, which ships with Windows 10 or Windows Server 2016, -but can also be installed on Windows 7 SP1, Windows 8.1, Windows Server 2012, -and Windows Server 2012 R2. +The minimum requirement for this module is PowerShell 7.0. ## Change log diff --git a/source/WikiSource/Resources/xAzDoGitPermission.md b/source/WikiSource/Resources/xAzDoGitPermission.md new file mode 100644 index 000000000..3aa3fa3db --- /dev/null +++ b/source/WikiSource/Resources/xAzDoGitPermission.md @@ -0,0 +1,195 @@ +# DSC xAzDoGitPermission Resource + +# Syntax + +``` PowerShell +xAzDoGitPermission [string] #ResourceName +{ + ProjectName = [String]$ProjectName + RepositoryName = [String]$RepositoryName + Permissions = [HashTable]$Permissions # See Permissions Syntax + [ Ensure = [String] {'Present', 'Absent'}] +} +``` + +## Permissions Syntax + +``` PowerShell +xAzDoGitPermission/Permissions +{ + Identity = [String]$Identity # Syntax + # SYNTAX: '[ProjectName | OrganizationName]\ServicePrincipalName, UserPrincipalName, UserDisplayName, GroupDisplayName' + # EXAMPLE: '[TestProject]\UserName@email.com' + # EXAMPLE: '[SampleOrganizationName]\Project Collection Administrators' + Permission = [Hashtable[]]$Permissions # See 'Permission List" +} +``` + +## Permission Usage + +``` PowerShell +xAzDoGitPermission/Permissions/Permission +{ + PermissionName|PermissionDisplayName = [String]$Name { 'Allow, Deny' } +} + +``` + +## Permission List + +> Either 'Name' or 'DisplayName' can be used + +| Name | DisplayName | Values | Note | +| ------------- | ------------- | - | - | +|Administer | Administer | [ allow, deny ] | Not recommended. | +|GenericRead | Read | [ allow, deny ] | | +|GenericContribute | Contribute | [ allow, deny ] | | +|ForcePush | Force push (rewrite history, delete branches and tags) | [ allow, deny ] | | +|CreateBranch | Create branch |[ allow, deny ] | | +|CreateTag | Create tag | [ allow, deny ] | | +|ManageNote | Manage notes | [ allow, deny ] | | +|PolicyExempt | Bypass policies when pushing | [ allow, deny ] | | +|CreateRepository | Create repository | [ allow, deny ] | | +|DeleteRepository | Delete or disable repository | [ allow, deny ] | | +|RenameRepository | Rename repository | [ allow, deny ] | | +|EditPolicies | Edit policies | [ allow, deny ] | | +|RemoveOthersLocks | Remove others' locks | [ allow, deny ] | | +|ManagePermissions | Manage permissions | [ allow, deny ] | | +|PullRequestContribute | Contribute to pull requests | [ allow, deny ] | | +|PullRequestBypassPolicy | Bypass policies when completing pull requests | [ allow, deny ] | | +|ViewAdvSecAlerts | Advanced Security: view alerts | [ allow, deny ] | | +|DismissAdvSecAlerts | Advanced Security: manage and dismiss alerts | [ allow, deny ] | | +|ManageAdvSecScanning | Advanced Security: manage settings | [ allow, deny ] | | + +# Common Properties + +- __ProjectName__: The name of the Azure DevOps project. +- __RepositoryName__: The name of the Git repository within the project. +- __Permissions__: A HashTable that specifies the permissions to be set. Refer to: 'Permissions Syntax'. +- __Ensure__: Specifies whether the repository should exist. Defaults to 'Absent'. + +# Additional Information + +This resource allows you to manage Azure DevOps projects using Desired State Configuration (DSC). +It includes properties for specifying the project name, description, source control type, process template, and visibility. + +# Examples + +## Example 1: Sample Configuration using xAzDoGitPermission Resource + +``` PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + xAzDoGitPermission GitPermission { + Ensure = 'Present' + ProjectName = 'SampleProject' + RepositoryName = 'SampleGitRepository' + isInherited = $true + Permissions = @( + @{ + Identity = '[ProjectName]\GroupName' + Permissions = @{ + Read = 'Allow' + "Manage Notes" = 'Allow' + "Contribute" = 'Deny' + } + } + ) + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose + +``` + +## Example 2: Sample Configuration using Invoke-DSCResource + +``` PowerShell +# Return the current configuration for xAzDoGitPermission +# Ensure is not required +$properties = @{ + ProjectName = 'SampleProject' + RepositoryName = 'SampleGitRepository' + isInherited = $true + Permissions = @( + @{ + Identity = '[ProjectName]\GroupName' + Permissions = @{ + Read = 'Allow' + "Manage Notes" = 'Allow' + "Contribute" = 'Deny' + } + } + ) +} + +Invoke-DSCResource -Name 'xAzDoGitPermission' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Example 3: Sample Configuration to clear permissions for an identity within a group + +``` PowerShell +# Remove all group members from the group. +$properties = @{ + ProjectName = 'SampleProject' + RepositoryName = 'SampleGitRepository' + isInherited = $true + Permissions = @( + @{ + Identity = '[ProjectName]\GroupName' + Permissions = @{} + } + ) +} + +Invoke-DSCResource -Name 'xAzDoGitPermission' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Example 4: Sample Configuration using xAzDoDSCDatum + +``` YAML +parameters: {} + +variables: { + ProjectName: SampleProject, + RepositoryName: SampleRepository +} + +resources: + + - name: SampleGroup Permissions + type: AzureDevOpsDsc/xAzDoGitPermission + dependsOn: + - AzureDevOpsDsc/xAzDoProjectGroup/SampleGroupReadAccess + properties: + projectName: $ProjectName + RepositoryName: $RepositoryName + isInherited: false + Permissions: + - Identity: '[$ProjectName]\SampleGroupReadAccess' + Permission: + Read: "Allow" + "Manage notes": "Allow" +``` + +LCM Initialization: + +``` PowerShell + +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +.\Invoke-AZDOLCM.ps1 @params + +``` diff --git a/source/WikiSource/Resources/xAzDoGitRepository.md b/source/WikiSource/Resources/xAzDoGitRepository.md new file mode 100644 index 000000000..e71979d7e --- /dev/null +++ b/source/WikiSource/Resources/xAzDoGitRepository.md @@ -0,0 +1,83 @@ +# DSC xAzDoGitRepository Resource + +## Syntax + +```PowerShell +xAzDoGitRepository [string] #ResourceName +{ + ProjectName = [String]$ProjectName + RepositoryName = [String]$RepositoryName + [ SourceRepository = [String]$SourceRepository ] + [ Ensure = [String] {'Present', 'Absent'}] +} +``` + +## Permissions Syntax + +This resource does not directly manage permissions. It focuses on managing Git repositories within an Azure DevOps project. + +## Permission Usage + +Not applicable for this resource. + +## Permission List + +Not applicable for this resource. + +## Common Properties + +- __Ensure__: Specifies whether the repository should exist. Defaults to 'Absent'. +- __ProjectName__: The name of the Azure DevOps project. +- __RepositoryName__: The name of the Git repository within the project. +- __SourceRepository__: (Optional) The source repository from which to create the new repository. + +## Additional Information + +This resource allows you to manage Git repositories in Azure DevOps projects using Desired State Configuration (DSC). It includes properties for specifying the project name, repository name, and optionally a source repository. + +## Examples + +### Example 1: Create a Git Repository + +```PowerShell +Configuration Sample_xAzDoGitRepository +{ + Import-DscResource -ModuleName AzDevOpsDsc + + Node localhost + { + xAzDoGitRepository MyRepository + { + ProjectName = 'MySampleProject' + RepositoryName = 'MySampleRepository' + SourceRepository = 'TemplateRepository' + Ensure = 'Present' + } + } +} + +Sample_xAzDoGitRepository -OutputPath 'C:\DSC\' +Start-DscConfiguration -Path 'C:\DSC\' -Wait -Verbose -Force +``` + +### Example 2: Remove a Git Repository + +```PowerShell +Configuration Remove_xAzDoGitRepository +{ + Import-DscResource -ModuleName AzDevOpsDsc + + Node localhost + { + xAzDoGitRepository MyRepository + { + ProjectName = 'MySampleProject' + RepositoryName = 'MySampleRepository' + Ensure = 'Absent' + } + } +} + +Remove_xAzDoGitRepository -OutputPath 'C:\DSC\' +Start-DscConfiguration -Path 'C:\DSC\' -Wait -Verbose -Force +``` diff --git a/source/WikiSource/Resources/xAzDoGroupMember.md b/source/WikiSource/Resources/xAzDoGroupMember.md new file mode 100644 index 000000000..3a27fc5e1 --- /dev/null +++ b/source/WikiSource/Resources/xAzDoGroupMember.md @@ -0,0 +1,127 @@ +# DSC xAzDoGroupMember Resource + +## Syntax + +```PowerShell +xAzDoGroupMember [string] #ResourceName +{ + GroupName = [String]$GroupName # [ProjectName|OrganizationName]\GroupName + # For GroupMember Syntax, refer to # GroupMembers Syntax + [ GroupMembers = [String[]]$GroupMembers ] +} +``` + +### GroupMembers Syntax + +``` PowerShell +{ + GroupMember = [String]$GroupMemberName # [ProjectName|OrganizationName]\GroupName +} +``` + +The following string represents the service accounts for the project collection in Azure DevOps Organization: + +```text +[ProjectName|AZDOOrganizationName]\Project Collection Service Accounts +``` + +- __[ProjectName|AZDOOrganizationName]__: The AzDO Project or Organizational Name. +- __Project Collection Service Accounts__: The Group Member Name. This can be a Group Name, Service Principal Name or Service Principle. + +#### Example + +If your Azure DevOps Organization name is `MyOrg`, the string would look like: + +```text +[MyOrg]\Project Collection Service Accounts +``` + +## Properties + +Common Properties: + +- __GroupName__: The name of the Azure DevOps group. +- __GroupMembers__: An array of members to be included in the Azure DevOps group. + +## Additional Information + +This resource is used to manage Azure DevOps group memberships using Desired State Configuration (DSC). It allows you to define the properties of an Azure DevOps group and ensures that the group is configured according to those properties. + +## Examples + +### Example 1: Sample Configuration for Azure DevOps Group using xAzDoGroupMember Resource + +```PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + xAzDoGroupMember GroupExample { + GroupName = 'MySampleGroup' + GroupMembers = @('user1@example.com', 'user2@example.com') + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose +``` + +### Example 2: Sample Configuration for Azure DevOps Group using Invoke-DSCResource + +```PowerShell +# Return the current configuration for xAzDoGroupMember +$properties = @{ + GroupName = 'MySampleGroup' + GroupMembers = @('user1@example.com', 'user2@example.com') +} + +Invoke-DSCResource -Name 'xAzDoGroupMember' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +### Example 3: Sample Configuration to remove/exclude an Azure DevOps Group using Invoke-DSCResource + +```PowerShell +# Remove the Azure DevOps Group and ensure that it is not recreated. +$properties = @{ + GroupName = 'MySampleGroup' + Ensure = 'Absent' +} + +Invoke-DSCResource -Name 'xAzDoGroupMember' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +### Example 4: Sample Configuration using xAzDoDSCDatum + +```YAML +parameters: {} + +variables: { + GroupName: SampleGroup, + GroupMembers: ['user1@example.com', 'user2@example.com'] +} + +resources: + + - name: Group + type: AzureDevOpsDsc/xAzDoGroupMember + properties: + groupName: $GroupName + groupMembers: $GroupMembers +``` + +## LCM Initialization + +```PowerShell +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +.\Invoke-AZDOLCM.ps1 @params +``` diff --git a/source/WikiSource/Resources/xAzDoGroupPermission.md b/source/WikiSource/Resources/xAzDoGroupPermission.md new file mode 100644 index 000000000..797b7cd83 --- /dev/null +++ b/source/WikiSource/Resources/xAzDoGroupPermission.md @@ -0,0 +1,144 @@ +# xAzDoGroupPermission Resource Documentation (Currently Disabled) + +## Overview + +The `xAzDoGroupPermission` resource is part of the Azure DevOps Desired State Configuration (DSC) module. It allows you to manage group permissions within an Azure DevOps project repository. This resource provides properties for specifying the group name, permission inheritance, and a list of permissions to be set. + +## Syntax + +```PowerShell +xAzDoGroupPermission [string] #ResourceName +{ + GroupName = [String]$GroupName + [ isInherited = [Boolean]$isInherited ] + [ Permissions = [HashTable[]]$Permissions ] +} +``` + +### Properties + +- **GroupName**: The name of the Azure DevOps group. This property is mandatory. +- **isInherited**: Specifies whether the permissions should be inherited. Defaults to `$true`. +- **Permissions**: A HashTable array that specifies the permissions to be set for the group. Refer to the 'Permissions Syntax' section below. + +## Permissions Syntax + +```PowerShell +xAzDoGroupPermission/Permissions +{ + Identity = [String]$Identity + # SYNTAX: '[ProjectName | OrganizationName]\ServicePrincipalName, UserPrincipalName, UserDisplayName, GroupDisplayName' + # ALTERNATIVE SYNTAX: 'this' Referring to the group. + # EXAMPLE: '[TestProject]\UserName@email.com' + # EXAMPLE: '[SampleOrganizationName]\Project Collection Administrators' + Permission = [Hashtable[]]$Permissions +} +``` + +### Permission Usage + +```PowerShell +xAzDoGroupPermission/Permissions/Permission +{ + PermissionName|PermissionDisplayName = [String]$Name { 'Allow, Deny' } +} +``` + +### Permission List + +Either 'Name' or 'DisplayName' can be used: + +| Name | DisplayName | Values | Note | +|-------------------------|------------------------------------------------------|-----------------|------------------| +| Read | View identity information | [ allow, deny ] | | +| Write | Edit identity information | [ allow, deny ] | | +| Delete | Delete identity information | [ allow, deny ] | | +| ManageMembership | Manage group membership | [ allow, deny ] | | +| CreateScope | Create identity scopes | [ allow, deny ] | | +| RestoreScope | Restore identity scopes | [ allow, deny ] | | + +## Examples + +### Example 1: Set Group Permissions + +```PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + xAzDoGroupPermission GroupPermission { + GroupName = 'SampleGroup' + isInherited = $true + Permissions = @( + @{ + Identity = '[SampleProject]\SampleGroup' + Permissions = @{ + "Read" = 'Allow' + "Write" = 'Allow' + "Delete" = 'Deny' + } + } + ) + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose +``` + +### Example 2: Clear Group Permissions + +```PowerShell +# Remove all permissions from the group. +$properties = @{ + GroupName = 'SampleGroup' + isInherited = $true + Permissions = @( + @{ + Identity = '[SampleProject]\SampleGroup' + Permissions = @{} + } + ) +} + +Invoke-DSCResource -Name 'xAzDoGroupPermission' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Methods + +### Get Method + +Retrieves the current state properties of the `xAzDoGroupPermission` resource. + +```PowerShell +[xAzDoGroupPermission] Get() +{ + return [xAzDoGroupPermission]$($this.GetDscCurrentStateProperties()) +} +``` + +### GetDscCurrentStateProperties Method + +Returns the current state properties of the resource object. + +```PowerShell +hidden [Hashtable] GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) +{ + $properties = @{ + Ensure = [Ensure]::Absent + } + + if ($null -eq $CurrentResourceObject) { return $properties } + + $properties.GroupName = $CurrentResourceObject.GroupName + $properties.isInherited = $CurrentResourceObject.isInherited + $properties.Permissions = $CurrentResourceObject.Permissions + + Write-Verbose "[xAzDoGroupPermission] Current state properties: $($properties | Out-String)" + + return $properties +} +``` + +This class inherits from the `AzDevOpsDscResourceBase` class, which provides the base functionality for DSC resources in the Azure DevOps DSC module. diff --git a/source/WikiSource/Resources/xAzDoOrganizationGroup.md b/source/WikiSource/Resources/xAzDoOrganizationGroup.md new file mode 100644 index 000000000..8ea8371ba --- /dev/null +++ b/source/WikiSource/Resources/xAzDoOrganizationGroup.md @@ -0,0 +1,96 @@ +# DSC xAzDoOrganizationGroup Resource + +## Syntax + +```PowerShell +xAzDoOrganizationGroup [string] #ResourceName +{ + GroupName = [String]$GroupName + [ GroupDescription = [String]$GroupDescription ] +} +``` + +## Properties + +### Common Properties + +- **GroupName**: The name of the organization group. This property is mandatory and serves as the key property for the resource. +- **GroupDescription**: A description of the organization group. + +## Additional Information + +This resource is used to manage Azure DevOps organization groups using Desired State Configuration (DSC). It allows you to define the properties of an Azure DevOps organization group and ensures that the group is configured according to those properties. + +## Examples + +## Example 1: Sample Configuration using xAzDoOrganizationGroup Resource + +``` PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + xAzDoOrganizationGroup OrgGroup { + Ensure = 'Present' + GroupName = 'SampleGroup' + GroupDescription = 'This is a sample group!' + } + } +} + +OrgGroup +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose + +``` + +## Example 2: Sample Configuration using Invoke-DSCResource + +``` PowerShell +# Return the current configuration for xAzDoGitPermission +# Ensure is not required +$properties = @{ + GroupName = 'SampleGroup' + GroupDescription = 'This is a sample group!' +} + +Invoke-DSCResource -Name 'xAzDoOrganizationGroup' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Example 3: Sample Configuration using xAzDoDSCDatum + +``` YAML +parameters: {} + +variables: { + "PlaceHolder2": "PlaceHolder" +} + +resources: +- name: Team Leaders Organization Group + type: AzureDevOpsDsc/xAzDoOrganizationGroup + properties: + GroupName: AZDO_TeamLeaders_Group + GroupDescription: Team Leaders Organization Group + +- name: Service Accounts Organization Group + type: AzureDevOpsDsc/xAzDoOrganizationGroup + properties: + GroupName: AZDO_ServiceAccounts_Group + GroupDescription: Service Accounts Organization Group +``` + +LCM Initialization: + +``` PowerShell + +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +.\Invoke-AZDOLCM.ps1 @params diff --git a/source/WikiSource/Resources/xAzDoProject.md b/source/WikiSource/Resources/xAzDoProject.md new file mode 100644 index 000000000..5df0cad48 --- /dev/null +++ b/source/WikiSource/Resources/xAzDoProject.md @@ -0,0 +1,123 @@ +# DSC xAzDoProject Resource + +# Syntax + +``` PowerShell +xAzDoProject [string] #ResourceName +{ + ProjectName = [String]$ProjectName + [ Ensure = [String] {'Present', 'Absent'}] + [ ProjectDescription = [String]$ProjectDescription] + [ SourceControlType = [String] {'Git', 'Tfvc'}] + [ ProcessTemplate = [String] {'Agile', 'Scrum', 'CMMI', 'Basic'}] + [ Visibility = [String] {'Public', 'Private'}] +} +``` + +# Properties + +Common Properties: + +- __ProjectName__: The name of the Azure DevOps project. +- __ProjectDescription__: A description for the Azure DevOps project. +- __SourceControlType__: The type of source control (Git or Tfvc). Default is Git. +- __ProcessTemplate__: The process template to use (Agile, Scrum, CMMI, Basic). Default is Agile. +- __Visibility__: The visibility of the project (Public or Private). Default is Private. + +# Additional Information + +This resource is used to manage Azure DevOps projects using Desired State Configuration (DSC). +It allows you to define the properties of an Azure DevOps project and ensures that the project is configured according to those properties. + +# Examples + +## Example 1: Sample Configuration for Azure DevOps Project using xAzDoProject Resource + +``` PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + xAzDoProject ProjectExample { + Ensure = 'Present' + ProjectName = 'MySampleProject' + ProjectDescription = 'This is a sample Azure DevOps project.' + SourceControlType = 'Git' + ProcessTemplate = 'Agile' + Visibility = 'Private' + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose + +``` + +## Example 2: Sample Configuration for Azure DevOps Project using Invoke-DSCResource + +``` PowerShell +# Return the current configuration for xAzDoProject +# Ensure is not required +$properties = @{ + ProjectName = 'MySameProject' + ProjectDiscription = 'This is a sample Azure DevOps project' + SourceControlType = 'Git' + ProcessTemplate = 'Agile' + Visibility = 'Private' +} + +Invoke-DSCResource -Name 'xAzDoProject' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Example 3: Sample Configuration to remove/exclude an Azure DevOps Project using Invoke-DSCResource + +``` PowerShell +# Remove the Azure Devops Project and ensure that it is not recreated. +$properties = @{ + ProjectName = 'MySameProject' + Ensure = 'Absent' +} + +Invoke-DSCResource -Name 'xAzDoProject' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Example 4: Sample Configuration using xAzDoDSCDatum + +``` YAML +parameters: {} + +variables: { + ProjectName: SampleProject, + ProjectDescription: This is a SampleProject! +} + +resources: + + - name: Project + type: AzureDevOpsDsc/xAzDoProject + properties: + projectName: $ProjectName + projectDescription: $ProjectDescription + visibility: private + SourceControlType: Git + ProcessTemplate: Agile +``` + +LCM Initialization: + +``` PowerShell + +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +.\Invoke-AZDOLCM.ps1 @params + +``` diff --git a/source/WikiSource/Resources/xAzDoProjectGroup.md b/source/WikiSource/Resources/xAzDoProjectGroup.md new file mode 100644 index 000000000..7bc167b9b --- /dev/null +++ b/source/WikiSource/Resources/xAzDoProjectGroup.md @@ -0,0 +1,102 @@ +# DSC xAzDoProjectGroup Resource + +## Syntax + +```PowerShell +xAzDoProjectGroup [string] #ResourceName +{ + GroupName = [String]$GroupName + ProjectName = [String]$ProjectName + [ GroupDescription = [String]$GroupDescription ] +} +``` + +## Properties + +### Common Properties + +- **GroupName**: The name of the project group. This property is mandatory and serves as the key property for the resource. +- **ProjectName**: The name of the Azure DevOps project associated with this group. This property is mandatory. +- **GroupDescription**: A description of the project group. + +## Additional Information + +This resource is used to manage Azure DevOps project groups using Desired State Configuration (DSC). It allows you to define the properties of an Azure DevOps project group and ensures that the group is configured according to those properties. + +## Examples + +### Example 1: Sample Configuration using xAzDoProjectGroup Resource + +```PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + xAzDoProjectGroup ProjectGroup { + Ensure = 'Present' + GroupName = 'SampleProjectGroup' + ProjectName = 'SampleProject' + GroupDescription = 'This is a sample project group!' + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose +``` + +### Example 2: Sample Configuration using Invoke-DSCResource + +```PowerShell +# Return the current configuration for xAzDoProjectGroup +# Ensure is not required +$properties = @{ + GroupName = 'SampleProjectGroup' + ProjectName = 'SampleProject' + GroupDescription = 'This is a sample project group!' +} + +Invoke-DSCResource -Name 'xAzDoProjectGroup' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +### Example 3: Sample Configuration using xAzDoDSCDatum + +```YAML +parameters: {} + +variables: { + "PlaceHolder2": "PlaceHolder" +} + +resources: +- name: Team Leaders Project Group + type: AzureDevOpsDsc/xAzDoProjectGroup + properties: + GroupName: AZDO_TeamLeaders_ProjectGroup + ProjectName: SampleProject + GroupDescription: Team Leaders Project Group + +- name: Service Accounts Project Group + type: AzureDevOpsDsc/xAzDoProjectGroup + properties: + GroupName: AZDO_ServiceAccounts_ProjectGroup + ProjectName: SampleProject + GroupDescription: Service Accounts Project Group +``` + +LCM Initialization: + +```PowerShell + +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +.\Invoke-AZDOLCM.ps1 @params +``` diff --git a/source/WikiSource/Resources/xAzDoProjectServices.md b/source/WikiSource/Resources/xAzDoProjectServices.md new file mode 100644 index 000000000..55526e594 --- /dev/null +++ b/source/WikiSource/Resources/xAzDoProjectServices.md @@ -0,0 +1,111 @@ +# DSC xAzDoProjectServices Resource + +## Syntax + +```PowerShell +xAzDoProjectServices [string] #ResourceName +{ + ProjectName = [String]$ProjectName + [ GitRepositories = [String]$GitRepositories { 'Enabled' | 'Disabled' } ] + [ WorkBoards = [String]$WorkBoards { 'Enabled' | 'Disabled' } ] + [ BuildPipelines = [String]$BuildPipelines { 'Enabled' | 'Disabled' } ] + [ TestPlans = [String]$TestPlans { 'Enabled' | 'Disabled' } ] + [ AzureArtifact = [String]$AzureArtifact { 'Enabled' | 'Disabled' } ] +} +``` + +## Properties + +### Common Properties + +- **ProjectName**: The name of the Azure DevOps project. This property is mandatory and serves as the key property for the resource. +- **GitRepositories**: Specifies whether Git repositories are enabled or disabled. Valid values are `Enabled` or `Disabled`. Default is `Enabled`. +- **WorkBoards**: Specifies whether work boards are enabled or disabled. Valid values are `Enabled` or `Disabled`. Default is `Enabled`. +- **BuildPipelines**: Specifies whether build pipelines are enabled or disabled. Valid values are `Enabled` or `Disabled`. Default is `Enabled`. +- **TestPlans**: Specifies whether test plans are enabled or disabled. Valid values are `Enabled` or `Disabled`. Default is `Enabled`. +- **AzureArtifact**: Specifies whether Azure artifacts are enabled or disabled. Valid values are `Enabled` or `Disabled`. Default is `Enabled`. + +## Additional Information + +This resource is used to manage Azure DevOps project services using Desired State Configuration (DSC). It allows you to define the properties of an Azure DevOps project and ensures that the services are configured according to those properties. + +## Examples + +### Example 1: Sample Configuration using xAzDoProjectServices Resource + +```PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + xAzDoProjectServices ProjectServices { + Ensure = 'Present' + ProjectName = 'SampleProject' + GitRepositories = 'Enabled' + WorkBoards = 'Enabled' + BuildPipelines = 'Enabled' + TestPlans = 'Enabled' + AzureArtifact = 'Enabled' + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose +``` + +### Example 2: Sample Configuration using Invoke-DSCResource + +```PowerShell +# Return the current configuration for xAzDoProjectServices +# Ensure is not required +$properties = @{ + ProjectName = 'SampleProject' + GitRepositories = 'Enabled' + WorkBoards = 'Enabled' + BuildPipelines = 'Enabled' + TestPlans = 'Enabled' + AzureArtifact = 'Enabled' +} + +Invoke-DSCResource -Name 'xAzDoProjectServices' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +### Example 3: Sample Configuration using xAzDoDSCDatum + +```YAML +parameters: {} + +variables: { + "PlaceHolder2": "PlaceHolder" +} + +resources: +- name: Sample Project Services + type: AzureDevOpsDsc/xAzDoProjectServices + properties: + ProjectName: SampleProject + GitRepositories: Enabled + WorkBoards: Enabled + BuildPipelines: Enabled + TestPlans: Enabled + AzureArtifact: Enabled +``` + +LCM Initialization: + +```PowerShell + +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +.\Invoke-AZDOLCM.ps1 @params +``` + diff --git a/tests/DSC/InModuleScope.ps1 b/tests/DSC/InModuleScope.ps1 new file mode 100644 index 000000000..f79362906 --- /dev/null +++ b/tests/DSC/InModuleScope.ps1 @@ -0,0 +1,45 @@ +$VerbosePreference = 'Continue' +$ErrorActionPreference = 'Break' + +Get-ChildItem -LiteralPath 'C:\Temp\AzureDevOpsDSC\source\Enum' -File | ForEach-Object { + Write-Verbose "Dot Sourcing $($_.FullName)" + . $_.FullName +} +Get-ChildItem -LiteralPath 'C:\Temp\AzureDevOpsDSC\source\Classes' -File | ForEach-Object { + Write-Verbose "Dot Sourcing $($_.FullName)" + # Read the file and remove [DscResource()] attribute + $file = Get-Command $_.FullName + # Remove [DscResource()] attribute + $content = $file.ScriptContents -replace '\[DscResource\(\)\]', '' + # Conver the string array into ScriptBlock + $scriptBlock = [ScriptBlock]::Create($content) + # Dot source the script block + . $scriptBlock + +} + + +Get-ChildItem -LiteralPath 'C:\Temp\AzureDevOpsDSC\source\Modules\AzureDevOpsDsc.Common\Resources\Functions' -Recurse -File | ForEach-Object { + Write-Verbose "Dot Sourcing $($_.FullName)" + . $_.FullName +} +Get-ChildItem -LiteralPath 'C:\Temp\AzureDevOpsDSC\source\Modules\AzureDevOpsDsc.Common\Api' -Recurse -File | ForEach-Object { + Write-Verbose "Dot Sourcing $($_.FullName)" + . $_.FullName +} + +$ModuleRoot = 'C:\Temp\AzureDevOpsDSC\source\Modules\AzureDevOpsDsc.Common\' + +# Initalize the Cache +'LiveGroups', 'LiveProjects', 'Project','Team', 'Group', 'SecurityDescriptor' | ForEach-Object { + Initialize-CacheObject -CacheType $_ +} + +<# +# Create a Managed Identity Token +New-AzManagedIdentity -OrganizationName "akkodistestorg" -Verbose + +# Set the Group Cache +Set-AzDoAPICacheGroup -OrganizationName $Global:DSCAZDO_OrganizationName +Set-AzDoAPICacheProject -OrganizationName $Global:DSCAZDO_OrganizationName -Verbose +#> diff --git a/tests/Integration/DSCClassResources/AzDevOpsProject.Integration.Tests.ps1 b/tests/Integration/DSCClassResources/Archive/AzDevOpsProject.Integration.Tests.ps1 similarity index 100% rename from tests/Integration/DSCClassResources/AzDevOpsProject.Integration.Tests.ps1 rename to tests/Integration/DSCClassResources/Archive/AzDevOpsProject.Integration.Tests.ps1 diff --git a/tests/Integration/DSCClassResources/AzDevOpsProject.config.ps1 b/tests/Integration/DSCClassResources/Archive/AzDevOpsProject.config.ps1 similarity index 100% rename from tests/Integration/DSCClassResources/AzDevOpsProject.config.ps1 rename to tests/Integration/DSCClassResources/Archive/AzDevOpsProject.config.ps1 diff --git a/tests/Integration/Invoke-Tests.ps1 b/tests/Integration/Invoke-Tests.ps1 new file mode 100644 index 000000000..b2579b5dc --- /dev/null +++ b/tests/Integration/Invoke-Tests.ps1 @@ -0,0 +1,29 @@ +#Requires -Modules @{ ModuleName="Pester"; ModuleVersion="5.0.0" } +param( + [Parameter(Mandatory)] + [String]$TestFrameworkConfigurationPath +) + +# +# Dot Source the Supporting Functions + +$CurrentLocation = Get-Location + +Get-ChildItem -Path "$($CurrentLocation.Path)\Supporting\Functions" -Filter "*.ps1" | ForEach-Object { . $_.FullName } + +# +# Firstly Initialize the test environment +. "$($CurrentLocation.Path)\Supporting\Initalize-TestFramework.ps1" -TestFrameworkConfigurationPath $TestFrameworkConfigurationPath + +# +# Trigger the Tests + +Invoke-Pester -Path "$PSScriptRoot\Resources" + +# +# Tear down the test environment + +Get-ChildItem -Path "$($CurrentLocation.Path)\Supporting\API" -Filter "*.ps1" | ForEach-Object { . $_.FullName } +Get-ChildItem -Path "$($CurrentLocation.Path)\Supporting\APICalls" -Filter "*.ps1" | ForEach-Object { . $_.FullName } + +. "$($CurrentLocation.Path)\Supporting\Teardown.ps1" -ClearAll -OrganizationName $GLOBAL:DSCAZDO_OrganizationName diff --git a/tests/Integration/Resources/xAzDoGitPermission.tests.ps1 b/tests/Integration/Resources/xAzDoGitPermission.tests.ps1 new file mode 100644 index 000000000..195fb9952 --- /dev/null +++ b/tests/Integration/Resources/xAzDoGitPermission.tests.ps1 @@ -0,0 +1,147 @@ + + +Describe "xAzDoGitPermission Integration Tests" { + + BeforeAll { + + # Perform setup tasks here + $PROJECTNAME = 'TESTPROJECT_GIT_PERMISSION' + + $parameters = @{ + Name = 'xAzDoGitPermission' + ModuleName = 'AzureDevOpsDsc' + property = @{ + ProjectName = $PROJECTNAME + RepositoryName = 'TESTREPOSITORY' + isInherited = $false + Permissions = @( + @{ + Identity = "[$PROJECTNAME]\Group1" + Permission = @{ + Read = 'Allow' + Write = 'Allow' + } + } + @{ + Identity = "[$PROJECTNAME]\Group2" + Permission = @{ + Read = 'Deny' + Write = 'Deny' + } + } + ) + } + } + + # + # Create a new project + New-Project $PROJECTNAME + + # + # Create a new repository + + New-Repository -ProjectName $PROJECTNAME -RepositoryName 'TESTREPOSITORY' + + # + # Create some new groups + + 'Group1', 'Group2' | ForEach-Object { + New-Group -ProjectName $PROJECTNAME -GroupName $_ + } + } + + Context "Testing if the permissions exist" { + + BeforeAll { + $parameters.Method = 'Test' + } + + It "Should not throw any exceptions" { + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return False" { + $result = Invoke-DscResource @parameters + $result.InDesiredState | Should -BeFalse + } + + } + + Context "Creating new permissions" { + + BeforeAll { + $parameters.Method = 'Set' + } + + It "Should not throw any exceptions" { + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + # Set up the parameters for the DSC resource invocation. + $parameters.Method = 'Test' + + $result = Invoke-DscResource @parameters + $result.InDesiredState | Should -BeTrue + } + + } + + Context "Changing permissions" { + + BeforeAll { + $parameters.Method = 'Set' + $parameters.property.Permissions = @( + @{ + Identity = "[$PROJECTNAME]\Group1" + Permission = @{ + Read = 'Allow' + Write = 'Deny' + } + } + @{ + Identity = "[$PROJECTNAME]\Group2" + Permission = @{ + Read = 'Deny' + Write = 'Allow' + } + } + ) + } + + It "Should not throw any exceptions" { + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + # Set up the parameters for the DSC resource invocation. + $parameters.Method = 'Test' + + $result = Invoke-DscResource @parameters + $result.InDesiredState | Should -BeTrue + } + } + + Context "Clearing permissions should revert to inherited" { + + BeforeAll { + $parameters.Method = 'Set' + $parameters.property.Permissions = @() + $parameters.property.isInherited = $false + } + + It "Should not throw any exceptions" { + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + # Set up the parameters for the DSC resource invocation. + $parameters.Method = 'Test' + $result = Invoke-DscResource @parameters + $result.InDesiredState | Should -BeTrue + } + } + +} diff --git a/tests/Integration/Resources/xAzDoGitRepository.tests.ps1 b/tests/Integration/Resources/xAzDoGitRepository.tests.ps1 new file mode 100644 index 000000000..2af3fe06f --- /dev/null +++ b/tests/Integration/Resources/xAzDoGitRepository.tests.ps1 @@ -0,0 +1,127 @@ +Describe "xAzDoGitRepository Integration Tests" { + + BeforeAll { + + # Perform setup tasks here + $PROJECTNAME = 'TESTPROJECT_GITREPOSITORY' + + # Define common parameters + $parameters = @{ + Name = 'xAzDoGitRepository' + ModuleName = 'AzureDevOpsDsc' + } + + # + # Create a new project + + New-Project $PROJECTNAME + + } + + # This context is used to test if a git repository exists. + Context "Testing if a Git Repository Exists" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Test', which means we are testing the presence of a resource. + $parameters.Method = 'Test' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTPROJECT'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + RepositoryName = 'TESTREPOSITORY' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return False" { + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Absent', + # indicating that the git repository 'TESTREPOSITORY' does not exist. + $result.InDesiredState | Should -BeFalse + } + + } + + # This context is used to test the creation of a new git repository. + Context "Creating a new Git Repository Permissions" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name using the variable '$PROJECTNAME'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + RepositoryName = 'TESTREPOSITORY' + Ensure = 'Present' + } + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + # Update the 'Method' property to 'Test' to test the presence of the git repository. + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the git repository 'TESTREPOSITORY' exists. + $result.InDesiredState | Should -BeTrue + } + + } + + # This context is used to test the deletion of a git repository. + Context "Deleting an Existing Git Repository" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are deleting an existing resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name using the variable '$PROJECTNAME'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + RepositoryName = 'TESTREPOSITORY' + Ensure = 'Absent' + } + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Absent', + # indicating that the git repository 'TESTREPOSITORY' was deleted. + $result.InDesiredState | Should -BeTrue + } + + } + +} diff --git a/tests/Integration/Resources/xAzDoGroupMember.tests.ps1 b/tests/Integration/Resources/xAzDoGroupMember.tests.ps1 new file mode 100644 index 000000000..a7021adbe --- /dev/null +++ b/tests/Integration/Resources/xAzDoGroupMember.tests.ps1 @@ -0,0 +1,133 @@ +Describe "xAzDoGroupMember Integration Tests" { + + BeforeAll { + + # Perform setup tasks here + $PROJECTNAME = 'TESTPROJECT_GROUPMEMBER' + + # Define common parameters + $parameters = @{ + Name = 'xAzDoGroupMember' + ModuleName = 'AzureDevOpsDsc' + } + + # + # Create a new project + + New-Project $PROJECTNAME + + # + # Create some new groups + + 'TESTGROUP' ,'Group1', 'Group2' | ForEach-Object { + New-Group -ProjectName $PROJECTNAME -GroupName $_ + } + + } + + # This context is used to test if a group member exists. + Context "Testing if a Group Member Exists" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Test', which means we are testing the presence of a resource. + $parameters.Method = 'Test' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTPROJECT_GROUPMEMBER'. + $parameters.property = @{ + GroupName = "[{0}]\TESTGROUP" -f $PROJECTNAME + GroupMembers = "[$PROJECTNAME]\Group1", "[$PROJECTNAME]\Group2" + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return False" { + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Absent', + # indicating that the group member 'TESTMEMBER' does not exist. + $result.InDesiredState | Should -BeFalse + } + + + } + + # This context is used to test the creation of a new group member. + Context "Creating a new Group Member" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + $parameters.property = @{ + GroupName = "$PROJECTNAME\TESTGROUP" + GroupMembers = "[$PROJECTNAME]\Group1", "[$PROJECTNAME]\Group2" + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + # Set the 'Method' to 'Test' to test the presence of the group member. + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the group member 'TESTMEMBER' exists. + $result.InDesiredState | Should -BeTrue + } + + } + + # This context is used to test the removal of a group member. + Context "Removing a Group Member" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are removing a resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + $parameters.property = @{ + GroupName = "$PROJECTNAME\TESTGROUP" + GroupMembers = @("[$PROJECTNAME]\Group1") + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + # Set the 'Method' to 'Test' to test the presence of the group member. + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Absent', + # indicating that the group member 'TESTMEMBER' does not exist. + $result.InDesiredState | Should -BeTrue + } + + } + +} diff --git a/tests/Integration/Resources/xAzDoGroupPermission.tests.ps1 b/tests/Integration/Resources/xAzDoGroupPermission.tests.ps1 new file mode 100644 index 000000000..4b144202c --- /dev/null +++ b/tests/Integration/Resources/xAzDoGroupPermission.tests.ps1 @@ -0,0 +1,130 @@ +Describe "xAzDoGroupPermission intergration tests" -skip { + + BeforeAll { + + # Perform setup tasks here + $PROJECTNAME = 'TESTPROJECT_GIT_GROUP_PERMISSION' + $GroupName = 'TESTGROUP' + + $parameters = @{ + Name = 'xAzDoGroupPermission' + ModuleName = 'AzureDevOpsDsc' + property = @{ + GroupName = "[$PROJECTNAME]\$GroupName" + isInherited = $false + Permissions = @( + @{ + Identity = "this" + Permission = @{ + Read = 'Allow' + Write = 'Allow' + } + } + @{ + Identity = "[$PROJECTNAME]\Group1" + Permission = @{ + Read = 'Allow' + Write = 'Allow' + } + } + ) + } + } + + # + # Create a new project + + New-Project $PROJECTNAME + + # + # Create a new group + + New-Group $GroupName -ProjectName $PROJECTNAME + New-Group 'Group1' -ProjectName $PROJECTNAME + + } + + Context "Testing if the permissions exist" { + + BeforeAll { + $parameters.Method = 'Test' + } + + It "Should return False" { + $result = Invoke-DscResource @parameters + $result.InDesiredState | Should -BeFalse + } + + } + + # Create a new group + Context "Setting new Group Permissions" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + # Set the Method to 'Test' to verify that the git repository exists. + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the git repository 'TESTREPOSITORY' exists. + $result.InDesiredState | Should -BeTrue + } + + } + + # Change the permissions + Context "Changing Group Permissions" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Set the permissions to a new value + $parameters.property.Permissions = @( + @{ + Identity = "[$PROJECTNAME]\Group1" + Permission = @{ + Read = 'Allow' + Write = 'Deny' + } + } + ) + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + # Set the Method to 'Test' to verify that the git repository exists. + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the git repository 'TESTREPOSITORY' exists. + $result.InDesiredState | Should -BeTrue + } + + } + +} diff --git a/tests/Integration/Resources/xAzDoOrganizationGroup.Description.tests.ps1 b/tests/Integration/Resources/xAzDoOrganizationGroup.Description.tests.ps1 new file mode 100644 index 000000000..16da8fa0d --- /dev/null +++ b/tests/Integration/Resources/xAzDoOrganizationGroup.Description.tests.ps1 @@ -0,0 +1,153 @@ +Describe "xAzDoOrganizationGroup Integration Tests - With Description" { + + BeforeAll { + + # Perform setup tasks here + + # Define common parameters + $parameters = @{ + Name = 'xAzDoOrganizationGroup' + ModuleName = 'AzureDevOpsDsc' + } + + $GROUPNAME = 'TESTORGANIZATIONGROUP' + + } + + # This context is used to test if a organization group exists. + Context "Testing if a Organization Group Exists" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Test', which means we are testing the presence of a resource. + $parameters.Method = 'Test' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTORGANIZATIONGROUP'. + $parameters.property = @{ + GroupName = $GROUPNAME + GroupDescription = 'This is a test organization group.' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return False" { + + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that it is not in the desired state + $result.InDesiredState | Should -BeFalse + } + } + + # This context is used to test if a organization group can be created. + Context "Testing if a Organization Group Can Be Created" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTORGANIZATIONGROUP'. + $parameters.property = @{ + GroupName = $GROUPNAME + GroupDescription = 'This is a test organization group.' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that it is in the desired state + $result.InDesiredState | Should -BeTrue + } + } + + # This context is used to test if a organization group can be updated. + Context "Testing if a Organization Group Can Be Updated" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTORGANIZATIONGROUP'. + $parameters.property = @{ + GroupName = $GROUPNAME + GroupDescription = 'This is an updated test organization group.' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that it is in the desired state + $result.InDesiredState | Should -BeTrue + } + } + + # This context is used to test if a organization group can be removed. + Context "Testing if a Organization Group Can Be Removed" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTORGANIZATIONGROUP'. + $parameters.property = @{ + Ensure = 'Absent' + GroupName = $GROUPNAME + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True (since ensure is set to Absent)" { + + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that it is not in the desired state + $result.InDesiredState | Should -BeTrue + } + } + +} diff --git a/tests/Integration/Resources/xAzDoOrganizationGroup.NoDescription.tests.ps1 b/tests/Integration/Resources/xAzDoOrganizationGroup.NoDescription.tests.ps1 new file mode 100644 index 000000000..a33af6be2 --- /dev/null +++ b/tests/Integration/Resources/xAzDoOrganizationGroup.NoDescription.tests.ps1 @@ -0,0 +1,158 @@ +Describe "xAzDoOrganizationGroup Integration Tests - No Description" { + + BeforeAll { + + # Perform setup tasks here + + # Define common parameters + $parameters = @{ + Name = 'xAzDoOrganizationGroup' + ModuleName = 'AzureDevOpsDsc' + } + + $PROJECTNAME = 'TESTORGANIZATIONGROUP' + + # + # Create a new project + + New-Project $PROJECTNAME + + } + + # This context is used to test if a organization group exists. + Context "Testing if a Organization Group Exists" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Test', which means we are testing the presence of a resource. + $parameters.Method = 'Test' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTORGANIZATIONGROUP'. + $parameters.property = @{ + GroupName = 'TESTORGANIZATIONGROUP' + GroupDescription = 'This is a test organization group.' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return False" { + + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that it is not in the desired state + $result.InDesiredState | Should -BeFalse + } + } + + # This context is used to test if a organization group can be created. + Context "Testing if a Organization Group Can Be Created" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTORGANIZATIONGROUP'. + $parameters.property = @{ + GroupName = 'TESTORGANIZATIONGROUP' + GroupDescription = 'This is a test organization group.' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that it is in the desired state + $result.InDesiredState | Should -BeTrue + } + } + + # This context is used to test if a organization group can be updated. + Context "Testing if a Organization Group Can Be Updated" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTORGANIZATIONGROUP'. + $parameters.property = @{ + GroupName = 'TESTORGANIZATIONGROUP' + GroupDescription = 'This is an updated test organization group.' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that it is in the desired state + $result.InDesiredState | Should -BeTrue + } + } + + # This context is used to test if a organization group can be removed. + Context "Testing if a Organization Group Can Be Removed" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTORGANIZATIONGROUP'. + $parameters.property = @{ + Ensure = 'Absent' + GroupName = 'TESTORGANIZATIONGROUP' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True (since ensure is set to Absent)" { + + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that it is not in the desired state + $result.InDesiredState | Should -BeTrue + } + } + +} diff --git a/tests/Integration/Resources/xAzDoProject.Description.tests.ps1 b/tests/Integration/Resources/xAzDoProject.Description.tests.ps1 new file mode 100644 index 000000000..31d1796b6 --- /dev/null +++ b/tests/Integration/Resources/xAzDoProject.Description.tests.ps1 @@ -0,0 +1,152 @@ +Describe "xAzDoProject Integration Tests - With Description" { + + BeforeAll { + + # Perform setup tasks here + + # Define common parameters + $parameters = @{ + Name = 'xAzDoProject' + ModuleName = 'AzureDevOpsDsc' + } + + $PROJECTNAME = 'TESTPROJECT_DESC' + + } + + # This context is used to test if a project exists. + Context "Testing if a Project Exists" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Test', which means we are testing the presence of a resource. + $parameters.Method = 'Test' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTPROJECT'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + ProjectDescription = 'Test Description' + } + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return False" { + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Absent', + # indicating that the project 'TESTPROJECT' does not exist. + $result.InDesiredState | Should -BeFalse + } + + } + + # This context is used to test the creation of a new project. + Context "Creating a new Project" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name using the variable '$PROJECTNAME'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + } + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should be successful" { + # Set the 'Method' to 'Test' to verify that the project was successfully created. + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the project specified by '$PROJECTNAME' was successfully created. + $result.InDesiredState | Should -BeTrue + } + + } + + # This context is used to test if the description of a project can be updated. + Context "Updating the Description of an existing Project" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are updating an existing resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name using the variable '$PROJECTNAME'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + ProjectDescription = 'Updated Description' + } + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + # Set the 'Method' to 'Test' to verify that the project was successfully updated. + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the project specified by '$PROJECTNAME' was successfully updated. + $result.InDesiredState | Should -BeTrue + } + + } + + # This context is used to test the deletion of an existing project. + Context "Deleting an existing Project" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are deleting an existing resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name using the variable '$PROJECTNAME'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + Ensure = 'Absent' + } + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True (since ensure is set to Absent)" { + # Set the 'Method' to 'Test' to verify that the project was successfully deleted. + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the it has been deleted successfully. + $result.InDesiredState | Should -BeTrue + } + + } + +} diff --git a/tests/Integration/Resources/xAzDoProject.NoDescription.tests.ps1 b/tests/Integration/Resources/xAzDoProject.NoDescription.tests.ps1 new file mode 100644 index 000000000..caa0978e2 --- /dev/null +++ b/tests/Integration/Resources/xAzDoProject.NoDescription.tests.ps1 @@ -0,0 +1,116 @@ +Describe "xAzDoProject Integration Tests - No Description" { + + BeforeAll { + + # Perform setup tasks here + + # Define common parameters + $parameters = @{ + Name = 'xAzDoProject' + ModuleName = 'AzureDevOpsDsc' + } + + $PROJECTNAME = 'TESTPROJECT' + + } + + # This context is used to test if a project exists. + Context "Testing if a Project Exists" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Test', which means we are testing the presence of a resource. + $parameters.Method = 'Test' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTPROJECT'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + } + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return False" { + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Absent', + # indicating that the project 'TESTPROJECT' does not exist. + $result.InDesiredState | Should -BeFalse + } + + } + + # This context is used to test the creation of a new project. + Context "Creating a new Project" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name using the variable '$PROJECTNAME'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + } + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should be successful" { + # Set the 'Method' to 'Test' to verify that the project was successfully created. + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the project specified by '$PROJECTNAME' was successfully created. + $result.InDesiredState | Should -BeTrue + } + + } + + # This context is used to test the deletion of an existing project. + Context "Deleting an existing Project" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are deleting an existing resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name using the variable '$PROJECTNAME'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + Ensure = 'Absent' + } + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True (since ensure is set to Absent)" { + # Set the 'Method' to 'Test' to verify that the project was successfully deleted. + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the it has been deleted successfully. + $result.InDesiredState | Should -BeTrue + } + + } + +} diff --git a/tests/Integration/Resources/xAzDoProjectGroup.Description.tests.ps1 b/tests/Integration/Resources/xAzDoProjectGroup.Description.tests.ps1 new file mode 100644 index 000000000..c9b74a4a1 --- /dev/null +++ b/tests/Integration/Resources/xAzDoProjectGroup.Description.tests.ps1 @@ -0,0 +1,167 @@ +Describe "xAzDoProjectGroup Integration Tests - With Description" { + + BeforeAll { + + # + # Perform setup tasks here + + $PROJECTNAME = 'TESTPROJECT_PROJECTGROUP_DESC' + $GROUPNAME = 'TESTPROJECTGROUP' + + # Define common parameters + $parameters = @{ + Name = 'xAzDoProjectGroup' + ModuleName = 'AzureDevOpsDsc' + } + + # + # Create a new project + + New-Project $PROJECTNAME + } + + # This context is used to test if a project group exists. + Context "Testing if a Project Group Exists" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Test', which means we are testing the presence of a resource. + $parameters.Method = 'Test' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTPROJECT_PROJECTGROUP'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + GroupDescription = 'This is a test project group.' + GroupName = $GROUPNAME + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + # It shouldn't be in the desired state because the project group doesn't exist. + It "Should return False" { + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'false' + $result.InDesiredState | Should -BeFalse + } + + } + + # This context is used to test the creation of a new project group. + Context "Creating a new Project Group" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTPROJECT_PROJECTGROUP' and a group name 'TESTPROJECTGROUP'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + GroupName = $GROUPNAME + GroupDescription = 'This is a test project group.' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the project group 'TESTPROJECTGROUP' was created. + $result.InDesiredState | Should -BeTrue + } + + } + + # Test if the project description can be updated. + Context "Updating the Project Group Description" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are updating a resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTPROJECT_PROJECTGROUP' and a group name 'TESTPROJECTGROUP'. + # We also specify a new description for the project group. + $parameters.property = @{ + ProjectName = $PROJECTNAME + GroupName = $GROUPNAME + GroupDescription = 'This is an updated test project group.' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the project group 'TESTPROJECTGROUP' was updated. + $result.InDesiredState | Should -BeTrue + } + + } + + # This context is used to test the removal of a project group. + Context "Removing a Project Group" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are removing a resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TESTPROJECT_PROJECTGROUP' and a group name 'TESTPROJECTGROUP'. + $parameters.property = @{ + ProjectName = $PROJECTNAME + GroupName = $GROUPNAME + Ensure = 'Absent' + } + + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + # Set the Method + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Absent', + # indicating that the project group 'TESTPROJECTGROUP' was removed. + $result.InDesiredState | Should -BeTrue + } + + } + +} diff --git a/tests/Integration/Resources/xAzDoProjectServices.tests.ps1 b/tests/Integration/Resources/xAzDoProjectServices.tests.ps1 new file mode 100644 index 000000000..d96046609 --- /dev/null +++ b/tests/Integration/Resources/xAzDoProjectServices.tests.ps1 @@ -0,0 +1,133 @@ +Describe "xAzDoProjectServices Integration Tests" { + + BeforeAll { + + Mock Write-Verbose { param($Message) } + + # + # Perform setup tasks here + + $PROJECTNAME = 'TEST_PROJECTSERVICES' + $GROUPNAME = 'TESTPROJECTGROUP' + + # Define common parameters + $parameters = @{ + Name = 'xAzDoProjectServices' + ModuleName = 'AzureDevOpsDsc' + } + + # + # Create a new project + + New-Project $PROJECTNAME + + } + + # This context is used to test if a project services exist. + Context "Testing if a Project Services Exists" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Test', which means we are testing the presence of a resource. + $parameters.Method = 'Test' + + # Define properties for the DSC resource. + # In this case, we specify a project name 'TEST_PROJECTSERVICES'. + $properties = @{ + ProjectName = $PROJECTNAME + GitRepositories = 'Enabled' + WorkBoards = 'Enabled' + BuildPipelines = 'Enabled' + TestPlans = 'Enabled' + AzureArtifact = 'Enabled' + } + + $parameters.property = $properties + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should return True" { + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the project 'TEST_PROJECTSERVICES' exists. + $result.InDesiredState | Should -BeTrue + } + } + + # This context is used to test the creation of a new project services. + Context "Creating a new Project Services" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are creating a new resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name using the variable '$PROJECTNAME'. + $properties = @{ + ProjectName = $PROJECTNAME + GitRepositories = 'Enabled' + WorkBoards = 'Enabled' + BuildPipelines = 'Disabled' + TestPlans = 'Disabled' + AzureArtifact = 'Enabled' + } + + $parameters.property = $properties + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + } + + # This context is used to test the deletion of an existing project services. + Context "Disabling existing Project Services" { + + BeforeAll { + # Set up the parameters for the DSC resource invocation. + # 'Method' is set to 'Set', which means we are deleting an existing resource. + $parameters.Method = 'Set' + + # Define properties for the DSC resource. + # In this case, we specify a project name using the variable '$PROJECTNAME'. + $properties = @{ + ProjectName = $PROJECTNAME + GitRepositories = 'Disabled' + WorkBoards = 'Disabled' + BuildPipelines = 'Disabled' + TestPlans = 'Disabled' + AzureArtifact = 'Disabled' + } + + $parameters.property = $properties + } + + It "Should not throw any exceptions" { + # Test that invoking the DSC resource with the specified parameters does not throw any exceptions. + { Invoke-DscResource @parameters } | Should -Not -Throw + } + + It "Should be within desired state" { + + $parameters.Method = 'Test' + + # Invoke the DSC resource with the specified parameters and store the result. + $result = Invoke-DscResource @parameters + + # Verify that the 'Ensure' property in the result is 'Present', + # indicating that the project services were successfully disabled. + $result.InDesiredState | Should -BeTrue + } + + } + +} + diff --git a/tests/Integration/Supporting/API/Add-Header.ps1 b/tests/Integration/Supporting/API/Add-Header.ps1 new file mode 100644 index 000000000..dec6b7985 --- /dev/null +++ b/tests/Integration/Supporting/API/Add-Header.ps1 @@ -0,0 +1,40 @@ +Function Add-Header { + + # Dertimine the type of token. + + $headerValue = "" + + switch ($Global:DSCAZDO_AuthenticationToken.tokenType) { + + # If the token is null + {$null} { + throw "[Add-Header] Error. The authentication token is null. Please ensure that the authentication token is set." + } + {$_ -eq 'PersonalAccessToken'} { + + # + # Personal Access Token + + # Add the Personal Access Token to the header + $headerValue = "Authorization: Basic {0}" -f $Global:DSCAZDO_AuthenticationToken.Token + break + } + {$_ -eq 'ManagedIdentity'} { + + # Add the Managed Identity Token to the header + $headerValue = 'Bearer {0}' -f $Global:DSCAZDO_AuthenticationToken.Token + break + + } + default { + throw "[Add-Header] Error. The authentication token type is not supported." + } + + } + + Write-Verbose "[Add-Header] Adding Header" + + # Return the header value + return $headerValue + +} diff --git a/tests/Integration/Supporting/API/ConvertTo-Base64String.ps1 b/tests/Integration/Supporting/API/ConvertTo-Base64String.ps1 new file mode 100644 index 000000000..fb2170d50 --- /dev/null +++ b/tests/Integration/Supporting/API/ConvertTo-Base64String.ps1 @@ -0,0 +1,14 @@ +function ConvertTo-Base64String +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [String] + $InputObject + ) + + process { + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($InputObject)) + } +} diff --git a/tests/Integration/Supporting/API/Get-AzDevOpsApiVersion.ps1 b/tests/Integration/Supporting/API/Get-AzDevOpsApiVersion.ps1 new file mode 100644 index 000000000..84e5b10f7 --- /dev/null +++ b/tests/Integration/Supporting/API/Get-AzDevOpsApiVersion.ps1 @@ -0,0 +1,35 @@ + +function Get-AzDevOpsApiVersion +{ + [CmdletBinding()] + [OutputType([System.Object[]])] + param + ( + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Default + ) + + [string]$defaultApiVersion = '7.0-preview.1' + + [string[]]$apiVersions = @( + + #'4.0', # Not supported + #'5.0', # Not supported + #'5.1', # Not supported + '6.0', + '7.0-preview.1', + '7.1-preview.1', + '7.1-preview.4', + '7.2-preview.4' + + ) + + if ($Default) + { + $apiVersions = $apiVersions | + Where-Object { $_ -eq $defaultApiVersion} + } + + return $apiVersions +} diff --git a/tests/Integration/Supporting/API/Get-MIToken.ps1 b/tests/Integration/Supporting/API/Get-MIToken.ps1 new file mode 100644 index 000000000..5b213a538 --- /dev/null +++ b/tests/Integration/Supporting/API/Get-MIToken.ps1 @@ -0,0 +1,35 @@ +Function Get-MIToken { + [CmdletBinding()] + param ( + # Organization Name + [Parameter(Mandatory)] + [String] + $OrganizationName + ) + + Write-Verbose "[Get-AzManagedIdentityToken] Getting the managed identity token for the organization $OrganizationName." + + # Obtain the access token from Azure AD using the Managed Identity + + $ManagedIdentityParams = @{ + # Define the Azure instance metadata endpoint to get the access token + Uri = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=499b84ac-1321-427f-aa17-267ca6975798" + Method = 'Get' + Headers = @{ Metadata="true" } + ContentType = 'Application/json' + } + + Write-Verbose "[Get-AzManagedIdentityToken] Invoking the Azure Instance Metadata Service to get the access token." + + # Invoke the RestAPI + try { $response = Invoke-RestMethod @ManagedIdentityParams } catch { Throw $_ } + # Test the response + if ($null -eq $response.access_token) { throw "Error. Access token not returned from Azure Instance Metadata Service. Please ensure that the Azure Instance Metadata Service is available." } + + # Return the token if the verify switch is not set + return @{ + tokenType = 'ManagedIdentity' + token = $response.access_token + } + +} diff --git a/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 b/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 new file mode 100644 index 000000000..fdf55eb28 --- /dev/null +++ b/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 @@ -0,0 +1,177 @@ + +function Invoke-APIRestMethod +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject])] + param + ( + [Parameter(Mandatory=$true)] + [Alias('Uri')] + [System.String] + $ApiUri, + + [Parameter(Mandatory=$true)] + [ValidateSet('Get','Post','Patch','Put','Delete')] + [System.String] + [Alias('Method')] + $HttpMethod, + + [Parameter()] + [Hashtable] + [Alias('Headers','HttpRequestHeader')] + $HttpHeaders=@{}, + + [Parameter()] + [System.String] + [Alias('Body')] + $HttpBody, + + [Parameter()] + [System.String] + [Alias('ContentType')] + [ValidateSet('application/json','application/json-patch+json')] + $HttpContentType = 'application/json', + + [Parameter()] + [ValidateRange(0,5)] + [Int32] + $RetryAttempts = 5, + + [Parameter()] + [ValidateRange(250,10000)] + [Int32] + $RetryIntervalMs = 250, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default), + + [Parameter()] + [Switch] + $NoAuthentication + + ) + + $invokeRestMethodParameters = @{ + Uri = $ApiUri + Method = $HttpMethod + Headers = $HttpHeaders + Body = $HttpBody + ContentType = $HttpContentType + ResponseHeadersVariable = 'responseHeaders' + } + + # Remove the 'Body' and 'ContentType' if not relevant to request + if ($HttpMethod -in $('Get','Delete')) + { + $invokeRestMethodParameters.Remove('Body') + $invokeRestMethodParameters.Remove('ContentType') + } + + # Intially set this value to -1, as the first attempt does not want to be classed as a "RetryAttempt" + $CurrentNoOfRetryAttempts = -1 + # Set the Continuation Token to be False + $isContinuationToken = $false + $results = [System.Collections.ArrayList]::new() + + while ($CurrentNoOfRetryAttempts -lt $RetryAttempts) + { + + # + # Invoke the REST method. Loop until the Continuation Token is False. + + Do { + + # + # Add the Authentication Header + + # If the 'NoAuthentication' switch is NOT PRESENT and the 'Authentication' header is empty, add the authentication header + if (([String]::IsNullOrEmpty($invokeRestMethodParameters.Headers.Authentication)) -and (-not $NoAuthentication.IsPresent)) + { + $invokeRestMethodParameters.Headers.Authorization = Add-Header + } + + # + # Invoke the REST method + + try + { + # Invoke the REST method. If the 'Verbose' switch is present, set it to $false. + # This is to prevent the output from being displayed in the console. + $response = Invoke-RestMethod @invokeRestMethodParameters -Verbose:$false + + # Zero out the 'Authorization' header + $invokeRestMethodParameters.Headers.Authorization = $null + # Add the response to the results array + $null = $results.Add($response) + + # + # Test to see if there is no continuation token + if ([String]::IsNullOrEmpty($responseHeaders.'x-ms-continuationtoken')) + { + # If not, set the continuation token to False + $isContinuationToken = $false + # Update the Rate Limit information + $Global:DSCAZDO_APIRateLimit = $null + + Write-Verbose "[Invoke-APIRestMethod] No continuation token found. Breaking loop." + + return $results + + } + + # + # A continuation token is present. + + # If so, set the continuation token to True + $isContinuationToken = $true + # Update the URI to include the continuation token + $invokeRestMethodParameters.Uri = '{0}&continuationToken={1}&{2}' -f $ApiUri, $responseHeaders.'x-ms-continuationtoken', $ApiVersion + # Reset the RetryAttempts counter + $CurrentNoOfRetryAttempts = -1 + + } + catch + { + # Zero out the 'Authorization' header + $invokeRestMethodParameters.Headers.Authorization = $null + + # Check to see if it is an HTTP 429 (Too Many Requests) error + if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::TooManyRequests) + { + # If so, wait for the specified number of seconds before retrying + $retryAfter = $_.Exception.Response.Headers | ForEach-Object { if ($_.Key -eq "Retry-After") { return $_.Value } } + if ($retryAfter) + { + $retryAfter = [int]$retryAfter + Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $retryAfter seconds before retrying." + $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($retryAfter) + } else { + # If the Retry-After header is not present, wait for the specified number of milliseconds before retrying + Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $RetryIntervalMs milliseconds before retrying." + $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($RetryIntervalMs) + } + + } + + # Increment the number of retries attempted and obtain any exception message + $CurrentNoOfRetryAttempts++ + $restMethodExceptionMessage = $_.Exception.Message + + # Wait before the next attempt/retry + Start-Sleep -Milliseconds $RetryIntervalMs + + # Break the continuation token loop so that the next attempt can be made + break; + + } + + } Until (-not $isContinuationToken) + + } + + # If all retry attempts have failed, throw an exception + $errorMessage = $script:localizedData.AzDevOpsApiRestMethodException -f $MyInvocation.MyCommand, $RetryAttempts, $restMethodExceptionMessage + New-InvalidOperationException -Message $errorMessage -Throw + +} diff --git a/tests/Integration/Supporting/API/New-AuthProvider.ps1 b/tests/Integration/Supporting/API/New-AuthProvider.ps1 new file mode 100644 index 000000000..377d0ca38 --- /dev/null +++ b/tests/Integration/Supporting/API/New-AuthProvider.ps1 @@ -0,0 +1,51 @@ +# A Stripped Down version of New-AzDoAuthenticationProvider +Function New-AuthProvider { + + [CmdletBinding(DefaultParameterSetName = 'PersonalAccessToken')] + param ( + # Organization Name + [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] + [Parameter(Mandatory, ParameterSetName = 'ManagedIdentity')] + [Alias('OrgName')] + [String] + $OrganizationName, + + # Personal Access Token + [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] + [Alias('PAT')] + [String] + $PersonalAccessToken, + + # Use Managed Identity + [Parameter(ParameterSetName = 'ManagedIdentity')] + [Switch] + $useManagedIdentity + ) + + # Set the Global Variables + $Global:DSCAZDO_OrganizationName = $OrganizationName + $Global:DSCAZDO_AuthenticationToken = $null + + # + # If the parameterset is PersonalAccessToken + if ($PSCmdlet.ParameterSetName -eq 'PersonalAccessToken') { + Write-Verbose "[New-AuthProvider] Creating a new Personal Access Token with OrganizationName $OrganizationName." + $Global:DSCAZDO_AuthenticationToken = @{ + 'token' = ":{0}" -f (ConvertTo-Base64String $PersonalAccessToken) + 'type' = 'PAT' + } + } + + # + # If the parameterset is ManagedIdentity + elseif ($PSCmdlet.ParameterSetName -eq 'ManagedIdentity') { + Write-Verbose "[New-AuthProvider] Creating a new Azure Managed Identity with OrganizationName $OrganizationName." + # If the Token is not Valid. Get a new Token. + $Global:DSCAZDO_AuthenticationToken = @{ + 'token' = Get-MIToken -OrganizationName $OrganizationName + 'type' = 'ManagedIdentity' + } + } + + +} diff --git a/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 b/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 new file mode 100644 index 000000000..2740620bc --- /dev/null +++ b/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 @@ -0,0 +1,34 @@ +Function List-DevOpsGroups { + [CmdletBinding()] + [OutputType([System.Object])] + Param + ( + [Parameter(Mandatory)] + [string] + $Organization, + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + $params = @{ + Uri = "https://vssps.dev.azure.com/$Organization/_apis/graph/groups" + Method = 'Get' + } + + # + # Invoke the Rest API to get the groups + $groups = Invoke-APIRestMethod @params + + if ($null -eq $groups.value) { + return $null + } + + # + # Perform a lookup to get the group + + # + # Return the groups from the cache + return $groups.Value + +} diff --git a/tests/Integration/Supporting/APICalls/List-DevOpsProjects.ps1 b/tests/Integration/Supporting/APICalls/List-DevOpsProjects.ps1 new file mode 100644 index 000000000..7ecebca2a --- /dev/null +++ b/tests/Integration/Supporting/APICalls/List-DevOpsProjects.ps1 @@ -0,0 +1,32 @@ + +function List-DevOpsProjects +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$OrganizationName, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + $params = @{ + Uri = "https://dev.azure.com/$OrganizationName/_apis/projects" + Method = 'Get' + } + + # + # Invoke the Rest API to get the groups + $groups = Invoke-APIRestMethod @params + + if ($null -eq $groups.value) + { + return $null + } + + # + # Return the groups from the cache + return $groups.Value + +} diff --git a/tests/Integration/Supporting/APICalls/Remove-DevOpsGroup.ps1 b/tests/Integration/Supporting/APICalls/Remove-DevOpsGroup.ps1 new file mode 100644 index 000000000..01ee17dc3 --- /dev/null +++ b/tests/Integration/Supporting/APICalls/Remove-DevOpsGroup.ps1 @@ -0,0 +1,56 @@ +<# +.SYNOPSIS +Removes a group from Azure DevOps. + +.DESCRIPTION +The Remove-DevOpsGroup function is used to remove a group from Azure DevOps using the Azure DevOps REST API. + +.PARAMETER ApiUri +The mandatory parameter for the API URI. + +.PARAMETER ApiVersion +The optional parameter for the API version with a default value obtained from the Get-AzDevOpsApiVersion function. + +.PARAMETER GroupDescriptor +The optional parameter for the project scope descriptor. + +.OUTPUTS +System.Management.Automation.PSObject + +.EXAMPLE +Remove-DevOpsGroup -ApiUri "https://dev.azure.com/myorganization" -GroupDescriptor "MyGroup" + +This example removes the group with the specified group descriptor from Azure DevOps. + +#> + +Function Remove-DevOpsGroup { + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject])] + param + ( + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default), + + [Parameter(Mandatory)] + [String] + $OrganizationName, + + [Parameter()] + [String] + $GroupDescriptor + ) + + $params = @{ + Uri = "https://vssps.dev.azure.com/{0}/_apis/graph/groups/{1}?api-version={2}" -f $OrganizationName, $GroupDescriptor, $ApiVersion + Method = 'Delete' + ContentType = 'application/json' + } + + try { + return (Invoke-APIRestMethod @params) + } + catch {} + +} diff --git a/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 b/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 new file mode 100644 index 000000000..d619b8f20 --- /dev/null +++ b/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 @@ -0,0 +1,60 @@ +<# +.SYNOPSIS +Removes an Azure DevOps project. + +.DESCRIPTION +The Remove-DevOpsProject function is used to remove an Azure DevOps project from the specified organization. + +.PARAMETER Organization +The name or URL of the Azure DevOps organization. + +.PARAMETER ProjectId +The ID or name of the project to be removed. + +.EXAMPLE +Remove-DevOpsProject -Organization "MyOrganization" -ProjectId "MyProject" + +This example removes the Azure DevOps project with the ID "MyProject" from the organization "MyOrganization". + +#> + +function Remove-DevOpsProject +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$Organization, + + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String] + $ProjectId, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion | Select-Object -Last 1) + + ) + + Write-Verbose "[Remove-DevOpsProject] Started." + + # Define the API version to use + $params = @{ + Uri = "https://dev.azure.com/{0}/_apis/projects/{1}?api-version={2}" -f $Organization, $ProjectId, $ApiVersion + Method = "DELETE" + } + + Write-Verbose "[Remove-DevOpsProject] Removing project $ProjectId from Azure DevOps organization $Organization" + + try + { + # Invoke the Azure DevOps REST API to create the project + $response = Invoke-APIRestMethod @params + # Output the response which contains the created project details + return $response + } catch + { + Write-Error "[Remove-DevOpsProject] Failed to create the Azure DevOps project: $_" + } + +} diff --git a/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 b/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 new file mode 100644 index 000000000..57901cb70 --- /dev/null +++ b/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 @@ -0,0 +1,73 @@ + + +Function New-Project { + param( + [string]$ProjectName + ) + + # + # Create a new project + + $projectParams = @{ + Name = 'xAzDoProject' + ModuleName = 'AzureDevOpsDsc' + Method = 'Set' + property = @{ + ProjectName = $PROJECTNAME + } + } + + # Invoke the DSC resource to create a new project. + $null = Invoke-DscResource @projectParams + +} + +Function New-Repository { + param( + [string]$ProjectName, + [string]$RepositoryName + ) + + # + # Create a new repository + + $parameters = @{ + Name = 'xAzDoGitRepository' + ModuleName = 'AzureDevOpsDsc' + Method = 'Set' + property = @{ + ProjectName = $PROJECTNAME + RepositoryName = $RepositoryName + } + } + + # Invoke the DSC resource to create a new project. + $null = Invoke-DscResource @parameters + +} + + + +Function New-Group { + param( + [string]$ProjectName, + [string]$GroupName + ) + + # + # Create a new group + + $groupParams = @{ + Name = 'xAzDoProjectGroup' + ModuleName = 'AzureDevOpsDsc' + Method = 'Set' + property = @{ + ProjectName = $PROJECTNAME + GroupName = $GroupName + } + } + + # Create that group + $null = Invoke-DscResource @groupParams + +} diff --git a/tests/Integration/Supporting/Initalize-TestFramework.ps1 b/tests/Integration/Supporting/Initalize-TestFramework.ps1 new file mode 100644 index 000000000..78d18ce5d --- /dev/null +++ b/tests/Integration/Supporting/Initalize-TestFramework.ps1 @@ -0,0 +1,41 @@ +param( + [Parameter(Mandatory)] + [String]$TestFrameworkConfigurationPath +) + +Import-Module DscResource.Common.psd1 +Import-Module AzureDevOpsDsc.Common.psd1 +Import-Module AzureDevOpsDsc.psd1 + +# +# Test Framework Configuration +if (-not (Test-Path $TestFrameworkConfigurationPath)) +{ + throw "[Initialize-TestFramework] Test Framework Configuration file not found at $TestFrameworkConfigurationPath" +} + +# +# Attempt to load the configuration +$TestFrameworkConfiguration = Get-Content $TestFrameworkConfigurationPath | ConvertFrom-Json + +# Confirm the Organization +if (-not $TestFrameworkConfiguration.Organization) +{ + throw "[Initialize-TestFramework] Organization not specified in the Test Framework Configuration" +} + +# Confirm the Authentication Type +if ($TestFrameworkConfiguration.AuthenticationType -eq 'PAT') +{ + # Authenticate with a Personal Access Token + #New-AuthProvider -OrganizationName $TestFrameworkConfiguration.Organization -PersonalAccessToken $TestFrameworkConfiguration.PATToken + New-AzDoAuthenticationProvider -OrganizationName $TestFrameworkConfiguration.Organization -PersonalAccessToken $TestFrameworkConfiguration.PATToken +} elseif ($TestFrameworkConfiguration.AuthenticationType -eq 'ManagedIdentity') { + # Authenticate with a Managed Identity + #New-AuthProvider -OrganizationName $TestFrameworkConfiguration.Organization -useManagedIdentity + New-AzDoAuthenticationProvider -OrganizationName $TestFrameworkConfiguration.Organization -useManagedIdentity +} else { + throw "[Initialize-TestFramework] Invalid Authentication Type: $($TestFrameworkConfiguration.AuthenticationType)" +} + + diff --git a/tests/Integration/Supporting/Teardown.ps1 b/tests/Integration/Supporting/Teardown.ps1 new file mode 100644 index 000000000..c9aaf3689 --- /dev/null +++ b/tests/Integration/Supporting/Teardown.ps1 @@ -0,0 +1,45 @@ +[CmdletBinding()] +param ( + [Parameter()] + [Switch] + $ClearAll, + + [Parameter()] + [Switch] + $ClearOrganizationGroups, + + [Parameter()] + [Switch] + $ClearProjects, + + [Parameter()] + [String] + $OrganizationName + +) + +$Global:DSCAZDO_AuthenticationToken = Get-MIToken -OrganizationName $OrganizationName + +# +# Remove Projects +if ($ClearAll -or $ClearProjects) +{ + # List all projects and remove them + List-DevOpsProjects -OrganizationName $OrganizationName | ForEach-Object { + Remove-DevOpsProject -ProjectId $_.id -Organization $OrganizationName + } +} + +# +# Remove Organization Groups + +if ($ClearAll -or $ClearOrganizationGroups) +{ + # List all groups and remove them + List-DevOpsGroups -Organization $OrganizationName | Where-Object { + ($_.DisplayName -notlike "Project*") -and ($_.DisplayName -notlike "Security*") -and ($_.DisplayName -notlike "Service*") -and ($_.DisplayName -notlike "Team*") -and ($_.DisplayName -notlike "Enterprise*") + } | ForEach-Object { + Remove-DevOpsGroup -GroupDescriptor $_.descriptor -OrganizationName $OrganizationName + } +} + diff --git a/tests/Integration/TestFrameworkConfiguration.json b/tests/Integration/TestFrameworkConfiguration.json new file mode 100644 index 000000000..3dddec1f9 --- /dev/null +++ b/tests/Integration/TestFrameworkConfiguration.json @@ -0,0 +1,4 @@ +{ + "Organization" : "akkodistestorg", + "AuthenticationType" : "ManagedIdentity" +} diff --git a/tests/Unit/Classes/API/007.APIRateLimit.tests.ps1 b/tests/Unit/Classes/API/007.APIRateLimit.tests.ps1 new file mode 100644 index 000000000..f4ecdeba9 --- /dev/null +++ b/tests/Unit/Classes/API/007.APIRateLimit.tests.ps1 @@ -0,0 +1,72 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($Global:ClassesLoaded -eq $null) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the classes + $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } + . $preInitialize.FullName -RepositoryPath $RepositoryRoot +} + +Describe 'APIRateLimit' { + Context 'Constructor with HashTable parameter' { + It 'should initialize properties correctly when given a valid hashtable' { + $validHashTable = @{ + 'Retry-After' = 10 + 'X-RateLimit-Remaining' = 100 + 'X-RateLimit-Reset' = 1609459200 # Unix time for 2021-01-01 00:00:00 UTC + } + + $apiRateLimit = [APIRateLimit]::new($validHashTable) + + $apiRateLimit.retryAfter | Should -Be 10 + $apiRateLimit.XRateLimitRemaining | Should -Be 100 + $apiRateLimit.XRateLimitReset | Should -Be 1609459200 + } + + It 'should throw an error when given an invalid hashtable' { + $invalidHashTable = @{ + 'Retry-After' = 10 + 'X-RateLimit-Remaining' = 100 + # Missing 'X-RateLimit-Reset' + } + + { [APIRateLimit]::new($invalidHashTable) } | Should -Throw "The APIRateLimitObj is not valid." + } + } + + Context 'Constructor with retryAfter parameter' { + It 'should initialize retryAfter property correctly' { + $retryAfterValue = 5 + $apiRateLimit = [APIRateLimit]::new($retryAfterValue) + + $apiRateLimit.retryAfter | Should -Be $retryAfterValue + } + } + + Context 'isValid method' { + It 'should return true for a valid hashtable' { + $validHashTable = @{ + 'Retry-After' = 10 + 'X-RateLimit-Remaining' = 100 + 'X-RateLimit-Reset' = 1609459200 + } + $apiRateLimit = [APIRateLimit]::new($validHashTable) + $result = $apiRateLimit.isValid($validHashTable) + + $result | Should -Be $true + } + + It 'should return false for an invalid hashtable' { + $invalidHashTable = @{ + 'Retry-After' = 10 + 'X-RateLimit-Remaining' = 100 + # Missing 'X-RateLimit-Reset' + } + {[APIRateLimit]::new($invalidHashTable)} | Should -Throw '*The APIRateLimitObj is not valid*' + } + } +} diff --git a/tests/Unit/Classes/Authentication/001.AuthenticationToken.tests.ps1 b/tests/Unit/Classes/Authentication/001.AuthenticationToken.tests.ps1 new file mode 100644 index 000000000..508b0d0d8 --- /dev/null +++ b/tests/Unit/Classes/Authentication/001.AuthenticationToken.tests.ps1 @@ -0,0 +1,91 @@ +# Requires -Module Pester -Version 5.0.0 + +# Test if the class is defined +if ($Global:ClassesLoaded -eq $null) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the classes + $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } + . $preInitialize.FullName -RepositoryPath $RepositoryRoot +} + +Describe 'AuthenticationToken Class' { + Context 'ConvertFromSecureString Method' { + It 'Should convert a SecureString to a String correctly' { + # Arrange + $secureString = ConvertTo-SecureString "TestPassword" -AsPlainText -Force + $authToken = [AuthenticationToken]::new() + + # Act + $result = $authToken.ConvertFromSecureString($secureString) + + # Assert + $result | Should -Be "TestPassword" + } + } + + Context 'TestCallStack Method' { + It 'Should return true if the calling function is found in the call stack' { + # Arrange + function Test-CallStackFunction + { + $authToken = [AuthenticationToken]::new() + return $authToken.TestCallStack('Test-CallStackFunction') + } + + # Act + $result = Test-CallStackFunction + + # Assert + $result | Should -Be $true + } + + It 'Should return false if the calling function is not found in the call stack' { + # Arrange + $authToken = [AuthenticationToken]::new() + + # Act + $result = $authToken.TestCallStack('NonExistentFunction') + + # Assert + $result | Should -Be $false + } + } + + Context 'TestCaller Method' { + It 'Should throw an exception if called from an unauthorized function' { + # Arrange + $authToken = [AuthenticationToken]::new() + + # Act & Assert + { $authToken.TestCaller() } | Should -Throw "*The Get() method can only be called*" + } + } + + Context 'Get Method' { + It 'Should return the access token when called from an authorized function' { + # Arrange + function Invoke-AzDevOpsApiRestMethod + { + $authToken = [AuthenticationToken]::new() + $authToken.access_token = ConvertTo-SecureString "TestToken" -AsPlainText -Force + return $authToken.Get() + } + + # Act + $result = Invoke-AzDevOpsApiRestMethod + + # Assert + $result | Should -Be "TestToken" + } + + It 'Should throw an exception when called from an unauthorized function' { + # Arrange + $authToken = [AuthenticationToken]::new() + + # Act & Assert + { $authToken.Get() } | Should -Throw "*The Get() method can only be called*" + } + } +} diff --git a/tests/Unit/Classes/Authentication/002.PersonalAccessToken.tests.ps1 b/tests/Unit/Classes/Authentication/002.PersonalAccessToken.tests.ps1 new file mode 100644 index 000000000..1776c1a38 --- /dev/null +++ b/tests/Unit/Classes/Authentication/002.PersonalAccessToken.tests.ps1 @@ -0,0 +1,85 @@ +# Requires -Module Pester -Version 5.0.0 + +# Test if the class is defined +if ($Global:ClassesLoaded -eq $null) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the classes + $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } + . $preInitialize.FullName -RepositoryPath $RepositoryRoot +} + +Describe 'PersonalAccessToken Class' { + Context 'Constructor with String Parameter' { + It 'Should initialize with a string personal access token' { + # Arrange + $personalAccessToken = "TestToken" + + # Act + $pat = [PersonalAccessToken]::new($personalAccessToken) + + # Assert + $pat.tokenType | Should -Be 'PersonalAccessToken' + $pat.ConvertFromSecureString($pat.access_token) | Should -Be "OlRlc3RUb2tlbg==" + } + } + + Context 'Constructor with SecureString Parameter' { + It 'Should initialize with a secure string personal access token' { + # Arrange + $secureStringPAT = ConvertTo-SecureString "TestSecureToken" -AsPlainText -Force + + # Act + $pat = [PersonalAccessToken]::new($secureStringPAT) + + # Assert + $pat.tokenType | Should -Be 'PersonalAccessToken' + $pat.access_token | Should -Be $secureStringPAT + } + } + + Context 'isExpired Method' { + It 'Should always return false' { + # Arrange + $pat = [PersonalAccessToken]::new("TestToken") + + # Act + $result = $pat.isExpired() + + # Assert + $result | Should -Be $false + } + } +} + +Describe 'New-PersonalAccessToken Function' { + It 'Should create a new PersonalAccessToken object with a string token' { + # Arrange + $personalAccessToken = "TestToken" + + # Act + $pat = New-PersonalAccessToken -PersonalAccessToken $personalAccessToken + + # Assert + $pat | Should -BeOfType [PersonalAccessToken] + $pat.ConvertFromSecureString($pat.access_token) | Should -Be "OlRlc3RUb2tlbg==" + } + + It 'Should create a new PersonalAccessToken object with a secure string token' { + # Arrange + $secureStringPAT = ConvertTo-SecureString "TestSecureToken" -AsPlainText -Force + + # Act + $pat = New-PersonalAccessToken -SecureStringPersonalAccessToken $secureStringPAT + + # Assert + $pat | Should -BeOfType [PersonalAccessToken] + $pat.access_token | Should -Be $secureStringPAT + } + + It 'Should throw an error if no token is provided' { + # Act & Assert + { New-PersonalAccessToken } | Should -Throw "Error. A Personal Access Token or SecureString Personal Access Token must be provided." + } +} diff --git a/tests/Unit/Classes/Authentication/003.ManagedIdentityToken.tests.ps1 b/tests/Unit/Classes/Authentication/003.ManagedIdentityToken.tests.ps1 new file mode 100644 index 000000000..f918c25de --- /dev/null +++ b/tests/Unit/Classes/Authentication/003.ManagedIdentityToken.tests.ps1 @@ -0,0 +1,136 @@ +# Requires -Module Pester -Version 5.0.0 + +# Test if the class is defined +if ($Global:ClassesLoaded -eq $null) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the classes + $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } + . $preInitialize.FullName -RepositoryPath $RepositoryRoot +} + + +Describe 'ManagedIdentityToken Class' { + Context 'Constructor with PSCustomObject Parameter' { + + BeforeAll { + $epochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) + } + + It 'Should initialize with a valid ManagedIdentityTokenObj' { + # Arrange + + $managedIdentityTokenObj = [PSCustomObject]@{ + access_token = "TestAccessToken" + expires_on = ($epochStart.AddMinutes(10) - [datetime]::UnixEpoch).TotalSeconds + expires_in = 600 + resource = "https://resource.url" + token_type = "Bearer" + } + + # Act + $mit = [ManagedIdentityToken]::new($managedIdentityTokenObj) + + # Assert + $mit.tokenType | Should -Be 'ManagedIdentity' + $mit.ConvertFromSecureString($mit.access_token) | Should -Be "TestAccessToken" + $mit.expires_on | Should -Be $epochStart.AddMinutes(10) + $mit.expires_in | Should -Be 600 + $mit.resource | Should -Be "https://resource.url" + $mit.token_type | Should -Be "Bearer" + } + + It 'Should throw an error with an invalid ManagedIdentityTokenObj' { + # Arrange + $invalidManagedIdentityTokenObj = [PSCustomObject]@{ + access_token = "TestAccessToken" + expires_on = ($epochStart.AddMinutes(10) - [datetime]::UnixEpoch).TotalSeconds + expires_in = 600 + resource = "https://resource.url" + # Missing token_type + } + + # Act & Assert + { [ManagedIdentityToken]::new($invalidManagedIdentityTokenObj) } | Should -Throw '*The ManagedIdentityTokenObj is not valid*' + } + } + + Context 'isExpired Method' { + It 'Should return true if the token is expired' { + # Arrange + $expiredTokenObj = [PSCustomObject]@{ + access_token = "TestAccessToken" + expires_on = ($epochStart.AddMinutes(-10) - [datetime]::UnixEpoch).TotalSeconds + expires_in = -600 + resource = "https://resource.url" + token_type = "Bearer" + } + $mit = [ManagedIdentityToken]::new($expiredTokenObj) + + # Act + $result = $mit.isExpired() + + # Assert + $result | Should -Be $true + } + + It 'Should return false if the token is not expired' { + # Arrange + $validTokenObj = [PSCustomObject]@{ + access_token = "TestAccessToken" + expires_on = 1820701735 + expires_in = 600 + resource = "https://resource.url" + token_type = "Bearer" + } + $mit = [ManagedIdentityToken]::new($validTokenObj) + + # Act + $result = $mit.isExpired() + + # Assert + $result | Should -Be $false + } + } + +} + +Describe 'New-ManagedIdentityToken Function' { + It 'Should create a new ManagedIdentityToken object with a valid PSCustomObject' { + # Arrange + $epochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) + $managedIdentityTokenObj = [PSCustomObject]@{ + access_token = "TestAccessToken" + expires_on = ($epochStart.AddMinutes(10) - [datetime]::UnixEpoch).TotalSeconds + expires_in = 600 + resource = "https://resource.url" + token_type = "Bearer" + } + + # Act + $mit = New-ManagedIdentityToken -ManagedIdentityTokenObj $managedIdentityTokenObj + + # Assert + $mit | Should -BeOfType [ManagedIdentityToken] + $mit.ConvertFromSecureString($mit.access_token) | Should -Be "TestAccessToken" + $mit.expires_on | Should -Be $epochStart.AddMinutes(10) + $mit.expires_in | Should -Be 600 + $mit.resource | Should -Be "https://resource.url" + $mit.token_type | Should -Be "Bearer" + } + + It 'Should throw an error if the ManagedIdentityTokenObj is invalid' { + # Arrange + $invalidManagedIdentityTokenObj = [PSCustomObject]@{ + access_token = "TestAccessToken" + expires_on = ($epochStart.AddMinutes(10) - [datetime]::UnixEpoch).TotalSeconds + expires_in = 600 + resource = "https://resource.url" + # Missing token_type + } + + # Act & Assert + { New-ManagedIdentityToken -ManagedIdentityTokenObj $invalidManagedIdentityTokenObj } | Should -Throw "*The ManagedIdentityTokenObj is not valid*" + } +} diff --git a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/AzDevOpsApiDscResourceBase.Initialization.Tests.ps1 b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/AzDevOpsApiDscResourceBase.Initialization.Tests.ps1 deleted file mode 100644 index b9e289e79..000000000 --- a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/AzDevOpsApiDscResourceBase.Initialization.Tests.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -# Initialize tests for module function 'Classes' -. $PSScriptRoot\..\Classes.TestInitialization.ps1 - -# Note: Use of this functionality seems to pre-load the module and classes which subsquent tests can use -# which works around difficulty of referencing classes in 'source' directory when code coverage is -# using the dynamically/build-defined, 'output' directory. -InModuleScope 'AzureDevOpsDsc' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - -} diff --git a/tests/Unit/Classes/Classes.BeforeAll.ps1 b/tests/Unit/Classes/Classes.BeforeAll.ps1 new file mode 100644 index 000000000..173a40707 --- /dev/null +++ b/tests/Unit/Classes/Classes.BeforeAll.ps1 @@ -0,0 +1,42 @@ +param( + [Parameter(Mandatory)] + [String] + $RepositoryPath +) + +$ClassesDirectory = "$RepositoryPath\source\Classes" +$EnumsDirectory = "$RepositoryPath\source\Enum" +$Global:ClassesLoaded = $true + +# +# Load all the Enums + +Get-ChildItem -LiteralPath $EnumsDirectory -File | ForEach-Object { + Write-Verbose "Dot Sourcing $($_.FullName)" + . $_.FullName +} + +# +# Load all the Classes + +Get-ChildItem -LiteralPath $ClassesDirectory -File | ForEach-Object { + + Write-Verbose "Dot Sourcing $($_.FullName)" + # Read the file and remove [DscResource()] attribute + $file = Get-Command $_.FullName + # Remove [DscResource()] attribute + $content = $file.ScriptContents -replace '\[DscResource\(\)\]', '' + # Convert the string array into ScriptBlock + $scriptBlock = [ScriptBlock]::Create($content) + # Dot source the script block + . $scriptBlock + +} + +# +# Load all the Helper Functions + +Get-ChildItem -LiteralPath "$RepositoryPath\source\Modules\AzureDevOpsDsc.Common\Api\Functions\Private\Helper" -File -Recurse -Filter *.ps1 | ForEach-Object { + Write-Verbose "Dot Sourcing $($_.FullName)" + . $_.FullName +} diff --git a/tests/Unit/Classes/Classes.TestInitialization.ps1 b/tests/Unit/Classes/Classes.TestInitialization.ps1 deleted file mode 100644 index aeea1dc63..000000000 --- a/tests/Unit/Classes/Classes.TestInitialization.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -<# - .SYNOPSIS - Automated unit test for classes in AzureDevOpsDsc. -#> - -Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '\..\Modules\TestHelpers\CommonTestHelper.psm1') -Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '\..\Modules\TestHelpers\CommonTestCases.psm1') - -$script:dscModuleName = 'AzureDevOpsDsc' -$script:dscModule = Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1 -$script:dscModuleFile = $($script:dscModule.ModuleBase +'\'+ $script:dscModuleName + ".psd1") -Get-Module -Name $script:dscModuleName -All | - Remove-Module $script:dscModuleName -Force -ErrorAction SilentlyContinue - -$script:subModuleName = 'AzureDevOpsDsc.Common' -Import-Module -Name $script:dscModuleFile -Force - -Get-Module -Name $script:subModuleName -All | - Remove-Module -Force -ErrorAction SilentlyContinue -$script:subModulesFolder = Join-Path -Path $script:dscModule.ModuleBase -ChildPath 'Modules' -$script:subModuleFile = Join-Path $script:subModulesFolder "$($script:subModuleName)/$($script:subModuleName).psd1" -Import-Module -Name $script:subModuleFile -Force #-Verbose diff --git a/tests/Unit/Classes/ManagedIdentity/APIRateLimit.tests.ps1 b/tests/Unit/Classes/ManagedIdentity/APIRateLimit.tests.ps1 deleted file mode 100644 index 8307e59d0..000000000 --- a/tests/Unit/Classes/ManagedIdentity/APIRateLimit.tests.ps1 +++ /dev/null @@ -1,87 +0,0 @@ -<# -.SYNOPSIS - Test suite for the APIRateLimit class. - -.DESCRIPTION - This test suite validates the functionality of the APIRateLimit class, ensuring it properly handles valid and invalid inputs. -#> - -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc' { - - Describe "APIRateLimit Class Tests" { - - It "Throws an exception when initialized with an invalid HashTable (missing keys)" { - # Arrange - $invalidHashTable = @{ 'Retry-After' = 120 } - # Act / Assert - { [APIRateLimit]::new($invalidHashTable) } | Should -Throw "The APIRateLimitObj is not valid." - } - - It "Does not throw an exception when initialized with a valid HashTable" { - # Arrange - $validHashTable = @{ - 'Retry-After' = 120 - 'X-RateLimit-Remaining' = 10 - 'X-RateLimit-Reset' = 1583000000 - } - # Act / Assert - { [APIRateLimit]::new($validHashTable) } | Should -Not -Throw - } - - It "Correctly sets the properties when initialized with a valid HashTable" { - # Arrange - $validHashTable = @{ - 'Retry-After' = 120 - 'X-RateLimit-Remaining' = 10 - 'X-RateLimit-Reset' = 1583000000 - } - $expectedEpochTime = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc).AddSeconds(1583000000) - # Act - $apiRateLimit = [APIRateLimit]::new($validHashTable) - # Assert - $apiRateLimit.retryAfter | Should -Be 120 - $apiRateLimit.XRateLimitRemaining | Should -Be 10 - $apiRateLimit.XRateLimitReset | Should -Be 1583000000 - } - - It "Initializes with only retryAfter parameter correctly" { - # Arrange - $retryAfterValue = 300 - # Act - $apiRateLimit = [APIRateLimit]::new($retryAfterValue) - # Assert - $apiRateLimit.retryAfter | Should -Be $retryAfterValue - $apiRateLimit.XRateLimitRemaining | Should -Be 0 - $apiRateLimit.XRateLimitReset | Should -Be 0 - } - - It "isValid method returns false when HashTable is missing keys" { - # Arrange - $incompleteHashTable = @{ 'Retry-After' = 120 } - $apiRateLimit = [APIRateLimit]::new(0) - # Act - $result = $apiRateLimit.isValid($incompleteHashTable) - # Assert - $result | Should -Be $false - } - - It "isValid method returns true when HashTable has all required keys" { - # Arrange - $completeHashTable = @{ - 'Retry-After' = 120 - 'X-RateLimit-Remaining' = 10 - 'X-RateLimit-Reset' = 1583000000 - } - $apiRateLimit = [APIRateLimit]::new(0) - # Act - $result = $apiRateLimit.isValid($completeHashTable) - # Assert - $result | Should -Be $true - } - } - -} -# Note: To execute this test suite, save it to a file named 'APIRateLimit.Tests.ps1' and run it using Pester. diff --git a/tests/Unit/Classes/ManagedIdentity/ManagedIdentityToken.tests.ps1 b/tests/Unit/Classes/ManagedIdentity/ManagedIdentityToken.tests.ps1 deleted file mode 100644 index 9512b78d1..000000000 --- a/tests/Unit/Classes/ManagedIdentity/ManagedIdentityToken.tests.ps1 +++ /dev/null @@ -1,96 +0,0 @@ -# Initialize tests for module function - -. $PSScriptRoot\..\Classes.TestInitialization.ps1 - -<# -.SYNOPSIS - Test suite for the ManagedIdentityToken class. - -.DESCRIPTION - This test suite validates the functionality of the ManagedIdentityToken class, ensuring it handles various scenarios correctly. -#> - -InModuleScope 'AzureDevOpsDsc' { - - Describe "ManagedIdentityToken Class Tests" { - - It "Throws an exception when invalid token object is provided" { - { [ManagedIdentityToken]::new(@{}) } | Should -Throw "The ManagedIdentityTokenObj is not valid." - } - - It "Creates a ManagedIdentityToken object with valid properties" { - # Arrange - $tokenProperties = @{ - access_token = "test_access_token" - expires_on = 3600 # Assuming this is seconds since epoch - expires_in = 3600 - resource = "https://my.resource.com" - token_type = "Bearer" - } - # Act - $tokenObject = [ManagedIdentityToken]::new((New-Object PSCustomObject -Property $tokenProperties)) - # Assert - $tokenObject.access_token | Should -Not -BeNullOrEmpty - $tokenObject.expires_on | Should -BeOfType [DateTime] - $tokenObject.expires_in | Should -Be 3600 - $tokenObject.resource | Should -Be "https://my.resource.com" - $tokenObject.token_type | Should -Be "Bearer" - } - - It "Determines if a token is expired" { - # Arrange - $tokenProperties = @{ - access_token = "test_access_token" - expires_on = (Get-Date).AddSeconds(-20).ToUniversalTime().Subtract([datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc)).TotalSeconds - expires_in = 3600 - resource = "https://my.resource.com" - token_type = "Bearer" - } - $tokenObject = [ManagedIdentityToken]::new((New-Object PSCustomObject -Property $tokenProperties)) - # Act / Assert - $tokenObject.isExpired() | Should -BeTrue - } - - It "Gets the access token when called from an allowed method" { - Mock Get-PSCallStack { - return @( - @{Command="Invoke-AzDevOpsApiRestMethod"} - ) - } - # Arrange - $tokenProperties = @{ - access_token = "test_access_token" - expires_on = 3600 # Assuming this is seconds since epoch - expires_in = 3600 - resource = "https://my.resource.com" - token_type = "Bearer" - } - $tokenObject = [ManagedIdentityToken]::new((New-Object PSCustomObject -Property $tokenProperties)) - # Act - $accessToken = $tokenObject.Get() - # Assert - $accessToken | Should -Be "test_access_token" - } - - It "Throws an exception when Get method is called from a disallowed method" { - Mock Get-PSCallStack { - return @( - @{Command="Write-Host"} - ) - } - # Arrange - $tokenProperties = @{ - access_token = "test_access_token" - expires_on = 3600 # Assuming this is seconds since epoch - expires_in = 3600 - resource = "https://my.resource.com" - token_type = "Bearer" - } - $tokenObject = [ManagedIdentityToken]::new((New-Object PSCustomObject -Property $tokenProperties)) - # Act / Assert - try { $tokenObject.Get() } catch { $exception = $_ } - $exception | Should -Be '[ManagedIdentityToken] The Get() method can only be called within Invoke-AzDevOpsApiRestMethod.' - } - } - -} diff --git a/tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 b/tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 new file mode 100644 index 000000000..e2e7935ae --- /dev/null +++ b/tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 @@ -0,0 +1,59 @@ +# Import the module containing the xAzDoGroupPermission class +# Describe block for xAzDoGroupPermission tests +Describe 'xAzDoGroupPermission Tests' { + + # Test case to check if the class can be instantiated + Context 'Instantiation' { + It 'Should create an instance of the xAzDoGroupPermission class' { + $groupPermission = [xAzDoGroupPermission]::new() + $groupPermission | Should -Not -BeNullOrEmpty + $groupPermission | Should -BeOfType 'xAzDoGroupPermission' + } + } + + # Test case to check default values + Context 'Default Values' { + It 'Should have default value for isInherited as $true' { + $groupPermission = [xAzDoGroupPermission]::new() + $groupPermission.isInherited | Should -Be $true + } + } + + # Test case to check property assignments + Context 'Property Assignments' { + It 'Should allow setting and getting GroupName property' { + $groupPermission = [xAzDoGroupPermission]::new() + $groupPermission.GroupName = 'TestGroup' + $groupPermission.GroupName | Should -Be 'TestGroup' + } + + It 'Should allow setting and getting Permissions property' { + $groupPermission = [xAzDoGroupPermission]::new() + $permissions = @( + @{ Permission = 'Read'; Allow = $true }, + @{ Permission = 'Write'; Allow = $false } + ) + $groupPermission.Permissions = $permissions + $groupPermission.Permissions | Should -Be $permissions + } + } + + # Test case for Get method + Context 'Get Method' { + It 'Should return current state properties' { + $groupPermission = [xAzDoGroupPermission]::new() + $groupPermission.GroupName = 'TestGroup' + $groupPermission.isInherited = $false + $groupPermission.Permissions = @( + @{ Permission = 'Read'; Allow = $true } + ) + + $currentState = $groupPermission.Get() + $currentState.GroupName | Should -Be 'TestGroup' + $currentState.isInherited | Should -Be $false + $currentState.Permissions | Should -Be @( + @{ Permission = 'Read'; Allow = $true } + ) + } + } +} diff --git a/tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 new file mode 100644 index 000000000..1c6863743 --- /dev/null +++ b/tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 @@ -0,0 +1,67 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($Global:ClassesLoaded -eq $null) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the classes + $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } + . $preInitialize.FullName -RepositoryPath $RepositoryRoot +} + +Describe 'xAzDoOrganizationGroup' { + # Mocking AzDevOpsDscResourceBase class since it's not provided + Class AzDevOpsDscResourceBase { + [void] Construct() {} + } + + Context 'Constructor' { + It 'should initialize properties correctly when given valid parameters' { + $organizationGroup = [xAzDoOrganizationGroup]::new() + $organizationGroup.GroupName = "MyGroup" + $organizationGroup.GroupDescription = "This is my group." + + $organizationGroup.GroupName | Should -Be "MyGroup" + $organizationGroup.GroupDescription | Should -Be "This is my group." + } + } + + Context 'GetDscResourcePropertyNamesWithNoSetSupport Method' { + It 'should return an empty array' { + $organizationGroup = [xAzDoOrganizationGroup]::new() + + $result = $organizationGroup.GetDscResourcePropertyNamesWithNoSetSupport() + + $result | Should -Be @() + } + } + + Context 'GetDscCurrentStateProperties Method' { + It 'should return properties with Ensure set to Absent if CurrentResourceObject is null' { + $organizationGroup = [xAzDoOrganizationGroup]::new() + + $result = $organizationGroup.GetDscCurrentStateProperties($null) + + $result.Ensure | Should -Be 'Absent' + } + + It 'should return current state properties from CurrentResourceObject' { + $organizationGroup = [xAzDoOrganizationGroup]::new() + $currentResourceObject = [PSCustomObject]@{ + GroupName = "MyGroup" + GroupDescription = "This is my group" + Ensure = "Present" + LookupResult = @{ Status = "Found" } + } + + $result = $organizationGroup.GetDscCurrentStateProperties($currentResourceObject) + + $result.GroupName | Should -Be "MyGroup" + $result.GroupDescription | Should -Be "This is my group" + $result.Ensure | Should -Be "Present" + $result.LookupResult.Status | Should -Be "Found" + } + } +} diff --git a/tests/Unit/Classes/Resources/020.xAzDevOpsProject.tests.ps1 b/tests/Unit/Classes/Resources/020.xAzDevOpsProject.tests.ps1 new file mode 100644 index 000000000..a298f7224 --- /dev/null +++ b/tests/Unit/Classes/Resources/020.xAzDevOpsProject.tests.ps1 @@ -0,0 +1,71 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($Global:ClassesLoaded -eq $null) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the classes + $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } + . $preInitialize.FullName -RepositoryPath $RepositoryRoot +} + +Describe 'xAzDevOpsProject' { + # Mocking AzDevOpsDscResourceBase class since it's not provided + Class AzDevOpsDscResourceBase { + [String]$Pat + [String]$ApiUri + [String]$Ensure + [PSObject] GetDscCurrentStateProperties() { return $null } + } + + Context 'Constructor' { + It 'should initialize properties correctly when given valid parameters' { + $project = [xAzDevOpsProject]::new() + $project.ProjectId = "12345" + $project.ProjectName = "TestProject" + $project.ProjectDescription = "This is a test project" + $project.SourceControlType = "Git" + + $project.ProjectId | Should -Be "12345" + $project.ProjectName | Should -Be "TestProject" + $project.ProjectDescription | Should -Be "This is a test project" + $project.SourceControlType | Should -Be "Git" + } + } + + Context 'GetDscResourcePropertyNamesWithNoSetSupport Method' { + It 'should return SourceControlType as property with no set support' { + $project = [xAzDevOpsProject]::new() + $result = $project.GetDscResourcePropertyNamesWithNoSetSupport() + + $result | Should -Contain "SourceControlType" + } + } + + Context 'GetDscCurrentStateProperties Method' { + + It 'should return correct properties when CurrentResourceObject is not null' { + $project = [xAzDevOpsProject]::new() + $currentResourceObject = [PSCustomObject]@{ + id = "12345" + name = "TestProject" + description = "This is a test project" + capabilities = @{ + versioncontrol = @{ + sourceControlType = "Git" + } + } + } + + $result = $project.GetDscCurrentStateProperties($currentResourceObject) + + $result.ProjectId | Should -Be "12345" + $result.ProjectName | Should -Be "TestProject" + $result.ProjectDescription | Should -Be "This is a test project" + $result.SourceControlType | Should -Be "Git" + $result.Ensure | Should -Be "Present" + } + } +} diff --git a/tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 b/tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 new file mode 100644 index 000000000..09b125306 --- /dev/null +++ b/tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 @@ -0,0 +1,89 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($Global:ClassesLoaded -eq $null) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the classes + $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } + . $preInitialize.FullName -RepositoryPath $RepositoryRoot +} + +Describe 'xAzDoProjectGroup' { + BeforeAll { + # Mock functions that interact with external resources + function Get-xAzDoProjectGroup + { + param ( + [string]$GroupName, + [string]$GroupDescription, + [string]$ProjectName + ) + # Return a mock object representing the current state + return @{ + GroupName = $GroupName + GroupDescription = $GroupDescription + ProjectName = $ProjectName + Ensure = 'Present' + } + } + + function New-xAzDoProjectGroup + { + param ( + [string]$ProjectName, + [string]$GroupName, + [string]$GroupDescription, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "New project group created: $GroupName in project $ProjectName" + } + + function Update-xAzDoProjectGroup + { + param ( + [string]$ProjectName, + [string]$GroupName, + [string]$GroupDescription, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "Project group updated: $GroupName in project $ProjectName" + } + + function Remove-xAzDoProjectGroup + { + param ( + [string]$ProjectName, + [string]$GroupName, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "Project group removed: $GroupName in project $ProjectName" + } + } + + Context 'When getting the current state of a project group' { + It 'Should return the current state properties' { + # Arrange + $projectGroup = [xAzDoProjectGroup]::new() + $projectGroup.ProjectName = "MyProject" + $projectGroup.GroupName = "MyGroup" + $projectGroup.GroupDescription = "This is my project group." + + # Act + $currentState = $projectGroup.Get() + + # Assert + $currentState.GroupName | Should -Be "MyGroup" + $currentState.ProjectName | Should -Be "MyProject" + $currentState.GroupDescription | Should -Be "This is my project group." + } + } +} diff --git a/tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 b/tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 new file mode 100644 index 000000000..0f72be87c --- /dev/null +++ b/tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 @@ -0,0 +1,82 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($Global:ClassesLoaded -eq $null) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the classes + $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } + . $preInitialize.FullName -RepositoryPath $RepositoryRoot +} + +Describe 'xAzDoGroupMember' { + BeforeAll { + # Mock functions that interact with external resources + function Get-xAzDoGroupMember + { + param ( + [string]$GroupName, + [string[]]$GroupMembers + ) + # Return a mock object representing the current state + return @{ + GroupName = $GroupName + GroupMembers = $GroupMembers + Ensure = 'Present' + } + } + + function New-xAzDoGroupMember + { + param ( + [string]$GroupName, + [string[]]$GroupMembers, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "New group member added to: $GroupName" + } + + function Update-xAzDoGroupMember + { + param ( + [string]$GroupName, + [string[]]$GroupMembers, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "Group members updated in: $GroupName" + } + + function Remove-xAzDoGroupMember + { + param ( + [string]$GroupName, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "Group members removed from: $GroupName" + } + } + + Context 'When getting the current state of group members' { + It 'Should return the current state properties' { + # Arrange + $groupMember = [xAzDoGroupMember]::new() + $groupMember.GroupName = "MyGroup" + $groupMember.GroupMembers = @("User1", "User2") + + # Act + $currentState = $groupMember.Get() + + # Assert + $currentState.GroupName | Should -Be "MyGroup" + $currentState.GroupMembers | Should -Be @("User1", "User2") + } + } +} diff --git a/tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 b/tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 new file mode 100644 index 000000000..0b3ce7efb --- /dev/null +++ b/tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 @@ -0,0 +1,87 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($Global:ClassesLoaded -eq $null) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the classes + $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } + . $preInitialize.FullName -RepositoryPath $RepositoryRoot +} + +Describe 'xAzDoGitRepository' { + BeforeAll { + # Mock functions that interact with external resources + function Get-xAzDoGitRepository + { + param ( + [string]$ProjectName, + [string]$GitRepositoryName + ) + # Return a mock object representing the current state + return @{ + ProjectName = $ProjectName + GitRepositoryName = $GitRepositoryName + SourceRepository = 'https://github.com/MyUser/MyRepository.git' + Ensure = 'Present' + } + } + + function New-xAzDoGitRepository + { + param ( + [string]$ProjectName, + [string]$GitRepositoryName, + [string]$SourceRepository, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "New Git repository created: $ProjectName/$GitRepositoryName" + } + + function Update-xAzDoGitRepository + { + param ( + [string]$ProjectName, + [string]$GitRepositoryName, + [string]$SourceRepository, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "Git repository updated: $ProjectName/$GitRepositoryName" + } + + function Remove-xAzDoGitRepository + { + param ( + [string]$ProjectName, + [string]$GitRepositoryName, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "Git repository removed: $ProjectName/$GitRepositoryName" + } + } + + Context 'When getting the current state of a Git repository' { + It 'Should return the current state properties' { + # Arrange + $gitRepository = [xAzDoGitRepository]::new() + $gitRepository.ProjectName = "MyProject" + $gitRepository.RepositoryName = "MyRepository" + + # Act + $currentState = $gitRepository.Get() + + # Assert + $currentState.ProjectName | Should -Be "MyProject" + $currentState.GitRepositoryName | Should -Be "MyRepository" + $currentState.SourceRepository | Should -Be 'https://github.com/MyUser/MyRepository.git' + } + } +} diff --git a/tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 b/tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 new file mode 100644 index 000000000..57e3e6de9 --- /dev/null +++ b/tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 @@ -0,0 +1,91 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($Global:ClassesLoaded -eq $null) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the classes + $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } + . $preInitialize.FullName -RepositoryPath $RepositoryRoot +} + +Describe 'xAzDoGitPermission' { + BeforeAll { + # Mock functions that interact with external resources + function Get-xAzDoGitPermission + { + param ( + [string]$ProjectName, + [string]$RepositoryName + ) + # Return a mock object representing the current state + return @{ + ProjectName = $ProjectName + RepositoryName = $RepositoryName + isInherited = $true + Permissions = @('Read', 'Contribute') + Ensure = 'Present' + } + } + + function New-xAzDoGitPermission + { + param ( + [string]$ProjectName, + [string]$RepositoryName, + [boolean]$isInherited, + [hashtable[]]$Permissions, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "New Git permissions set for: $ProjectName/$RepositoryName" + } + + function Update-xAzDoGitPermission + { + param ( + [string]$ProjectName, + [string]$RepositoryName, + [boolean]$isInherited, + [hashtable[]]$Permissions, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "Git permissions updated for: $ProjectName/$RepositoryName" + } + + function Remove-xAzDoPermission + { + param ( + [string]$ProjectName, + [string]$RepositoryName, + [string]$Pat, + [string]$ApiUri + ) + # Mock implementation + Write-Output "Git permissions removed from: $ProjectName/$RepositoryName" + } + } + + Context 'When getting the current state of Git permissions' { + It 'Should return the current state properties' { + # Arrange + $gitPermission = [xAzDoGitPermission]::new() + $gitPermission.ProjectName = "MyProject" + $gitPermission.RepositoryName = "MyRepository" + + # Act + $currentState = $gitPermission.Get() + + # Assert + $currentState.ProjectName | Should -Be "MyProject" + $currentState.RepositoryName | Should -Be "MyRepository" + $currentState.isInherited | Should -Be $true + $currentState.Permissions | Should -Be @('Read', 'Contribute') + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common.Functions.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common.Functions.Tests.ps1 deleted file mode 100644 index 51cae7660..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common.Functions.Tests.ps1 +++ /dev/null @@ -1,211 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - -return - -InModuleScope $script:subModuleName { - - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:publicCommandNames = $($(Get-Command -Module $script:subModuleName).Name) - - [hashtable[]]$testCasesValidCommandParameterSetNames = $script:publicCommandNames | ForEach-Object { - - $CommandName = $_ - $ParameterSetName = '__AllParameterSets' - $ParameterSetTestCases = $(Get-ParameterSetTestCase -CommandName $_ -ParameterSetName '__AllParameterSets' -TestCaseName 'Valid') - - $ParameterSetTestCases | ForEach-Object { - [hashtable]$testCase = $_ - $testCase.Add('CommandName',$CommandName) - $testCase.Add('ParameterSetName',$ParameterSetName) - $testCase.Add('ParameterNames',$_.ParameterSetValues.Keys) - - $testCase - } - } - - [hashtable[]]$testCasesValidCommandParameterSetNameValidParameterValues = $testCasesValidCommandParameterSetNames | Where-Object { $_.ParameterNames.Count -gt 0 } | ForEach-Object { - - $commandName = $_.CommandName - $parameterNames = $_.ParameterNames - - # Note: Exclude any parameter sets that do not have any parameters and ensure only - # looping through the test cases for the 'CommandName' being looped through in outer loop - $testCasesValidCommandParameterSetNames | Where-Object { $_.ParameterNames.Count -gt 0 -and $_.CommandName -eq $commandName } | ForEach-Object { - - $testCase = $_ - - $parameterNames | ForEach-Object { - - $parameterName = $_ - $parameterValues = $(Get-TestCaseValue -ScopeName $parameterName -TestCaseName 'Valid' -First 1) - - $parameterValues | ForEach-Object { - - $parameterValue = $_ - - if ($testCase.ParameterSetValues.ContainsKey($parameterName)) # Only want to generate new records if 'ParameterName' is in the set of 'ParameterSetValues' keys - { - $newTestCase = @{} - $testCase.Keys | ForEach-Object { - $newTestCase[$_] = $testCase[$_] - } - $newTestCase.Remove('ParameterSetValues') - $newTestCase.Add('ParameterSetValues',@{}) - $testCase.ParameterSetValues.Keys | ForEach-Object { - $newTestCase.ParameterSetValues[$_] = $testCase.ParameterSetValues[$_] - } - $newTestCase.ParameterSetValues[$parameterName] = $parameterValue - $newTestCase.Add('ParameterValue',$parameterValue) - $newTestCase.Add('ParameterName',$parameterName) - - $newTestCase - } - } - } - } - } - - - - [hashtable[]]$testCasesInvalidCommandParameterSetNames = $script:publicCommandNames | ForEach-Object { - - $CommandName = $_ - $ParameterSetName = '__AllParameterSets' - $ParameterSetTestCases = $(Get-ParameterSetTestCase -CommandName $_ -ParameterSetName '__AllParameterSets' -TestCaseName 'Invalid') - - $ParameterSetTestCases | ForEach-Object { - [hashtable]$testCase = $_ - $testCase.Add('CommandName',$CommandName) - $testCase.Add('ParameterSetName',$ParameterSetName) - $testCase.Add('ParameterNames',$_.Keys) - - $testCase - } - } - - [hashtable[]]$testCasesValidCommandParameterSetNameInvalidParameterValues = $testCasesValidCommandParameterSetNames | Where-Object { $_.ParameterNames.Count -gt 0 } | ForEach-Object { - - $commandName = $_.CommandName - $parameterNames = $_.ParameterNames - - # Note: Exclude any parameter sets that do not have any parameters and ensure only - # looping through the test cases for the 'CommandName' being looped through in outer loop - $testCasesValidCommandParameterSetNames | Where-Object { $_.ParameterNames.Count -gt 0 -and $_.CommandName -eq $commandName } | ForEach-Object { - - $testCase = $_ - - $parameterNames | ForEach-Object { - - $parameterName = $_ - $parameterValues = $(Get-TestCaseValue -ScopeName $parameterName -TestCaseName 'Invalid' -First 1) - - $parameterValues | ForEach-Object { - - if ($testCase.ParameterSetValues.ContainsKey($parameterName)) # Only want to generate new records if 'ParameterName' is in the set of 'ParameterSetValues' keys - { - $parameterValue = $_ - - $newTestCase = @{} - $testCase.Keys | ForEach-Object { - $newTestCase[$_] = $testCase[$_] - } - $newTestCase.Remove('ParameterSetValues') - $newTestCase.Add('ParameterSetValues',@{}) - $testCase.ParameterSetValues.Keys | ForEach-Object { - $newTestCase.ParameterSetValues[$_] = $testCase.ParameterSetValues[$_] - } - $newTestCase.ParameterSetValues[$parameterName] = $parameterValue - $newTestCase.Add('ParameterValue',$parameterValue) - $newTestCase.Add('ParameterName',$parameterName) - - $newTestCase - } - } - } - } - } - - - Describe "$subModuleName\AzureDevOpsDsc.Common\*\Functions" { - - - Context "When validating function/command parameter sets" { - - BeforeEach { - - Mock Invoke-AzDevOpsApiRestMethod { - return @{ - id = '14c15b78-b85d-401f-8095-504c57bbd79e' - } - } - - Mock Start-Sleep {} - #Mock New-InvalidOperationException {} # Don't mock this. Want exception to be thrown by it. - } - - Context "When invoking function/command with 'Valid', parameter set values" { - - It "Should not throw - '' - '' - " -TestCases $testCasesValidCommandParameterSetNames { - param([string]$CommandName, [Hashtable]$ParameterSetValues) - - Mock -CommandName $CommandName -MockWith {} - { & $CommandName @ParameterSetValues } | Should -Not -Throw - } - - It "Should not throw - '' - '' - ('' = '')" -TestCases $testCasesValidCommandParameterSetNameValidParameterValues { - param([string]$CommandName, [Hashtable]$ParameterSetValues) - - Mock -CommandName $CommandName -MockWith {} - { & $CommandName @ParameterSetValues } | Should -Not -Throw - } - } - - - Context "When invoking function/command with 'Invalid', parameter set values" { - - Context "When 'IsValid' parameter name is not present" { - - It "Should throw - '' - '' - " -TestCases $($testCasesInvalidCommandParameterSetNames | Where-Object { $_.ParameterSetValuesKey -notlike '*IsValid*' }) { - param([string]$CommandName, [Hashtable]$ParameterSetValues) - - Mock -CommandName $CommandName -MockWith {} - { & $CommandName @ParameterSetValues } | Should -Throw - } - - It "Should throw - '' - '' - " -TestCases $($testCasesInvalidCommandParameterSetNames | Where-Object { $_.ParameterSetValuesKey -notlike '*IsValid*' }){ - param([string]$CommandName, [Hashtable]$ParameterSetValues) - - Mock -CommandName $CommandName -MockWith {} - { & $CommandName @ParameterSetValues } | Should -Throw - } - } - - Context "When 'IsValid' parameter name is present" { - - # Don't want this to throw an exception - Typically they need to return a $false return value if input parameters are invalid. - - It "Should not throw - '' - '' - ('' = '')" -TestCases $($testCasesValidCommandParameterSetNameInvalidParameterValues | Where-Object { $_.ParameterSetValuesKey -like '*IsValid*' }) { - param([string]$CommandName, [Hashtable]$ParameterSetValues) - - Mock -CommandName $CommandName -MockWith {} - { & $CommandName @ParameterSetValues } | Should -Not -Throw - } - - It "Should not throw - '' - '' - ('' = '')" -TestCases $($testCasesValidCommandParameterSetNameInvalidParameterValues | Where-Object { $_.ParameterSetValuesKey -like '*IsValid*' }) { - param([string]$CommandName, [Hashtable]$ParameterSetValues) - - Mock -CommandName $CommandName -MockWith {} - { & $CommandName @ParameterSetValues } | Should -Not -Throw - } - - } - - - } - } - - } - -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common.Tests.Initialization.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common.Tests.Initialization.ps1 deleted file mode 100644 index aeea1dc63..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common.Tests.Initialization.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -<# - .SYNOPSIS - Automated unit test for classes in AzureDevOpsDsc. -#> - -Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '\..\Modules\TestHelpers\CommonTestHelper.psm1') -Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '\..\Modules\TestHelpers\CommonTestCases.psm1') - -$script:dscModuleName = 'AzureDevOpsDsc' -$script:dscModule = Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1 -$script:dscModuleFile = $($script:dscModule.ModuleBase +'\'+ $script:dscModuleName + ".psd1") -Get-Module -Name $script:dscModuleName -All | - Remove-Module $script:dscModuleName -Force -ErrorAction SilentlyContinue - -$script:subModuleName = 'AzureDevOpsDsc.Common' -Import-Module -Name $script:dscModuleFile -Force - -Get-Module -Name $script:subModuleName -All | - Remove-Module -Force -ErrorAction SilentlyContinue -$script:subModulesFolder = Join-Path -Path $script:dscModule.ModuleBase -ChildPath 'Modules' -$script:subModuleFile = Join-Path $script:subModulesFolder "$($script:subModuleName)/$($script:subModuleName).psd1" -Import-Module -Name $script:subModuleFile -Force #-Verbose diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.tests.ps1 new file mode 100644 index 000000000..e7fb7f907 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.tests.ps1 @@ -0,0 +1,62 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Get-DevOpsACL" -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-DevOpsACL.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + # Mock the required functions + Mock -CommandName Get-AzDevOpsApiVersion { return "6.0" } + Mock -CommandName Invoke-AzDevOpsApiRestMethod + Mock -CommandName Add-CacheItem + Mock -CommandName Export-CacheObject + + } + + It "should call Invoke-AzDevOpsApiRestMethod with correct parameters" { + $orgName = "TestOrg" + $secDescId = "abc123" + $apiVersion = "6.0" + + Get-DevOpsACL -OrganizationName $orgName -SecurityDescriptorId $secDescId + + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Times 1 -Exactly -ParameterFilter { + $Uri -eq "https://dev.azure.com/$orgName/_apis/accesscontrollists/$secDescId?api-version=$apiVersion" + $Method -eq "GET" + } + } + + It "should handle null response from REST API and return null" { + Mock Invoke-AzDevOpsApiRestMethod { return @{ value = $null } } + + $result = Get-DevOpsACL -OrganizationName "TestOrg" -SecurityDescriptorId "abc123" + + $result | Should -BeNull + Assert-MockCalled Add-CacheItem -Times 0 -Exactly -Scope It + Assert-MockCalled Export-CacheObject -Times 0 -Exactly -Scope It + + } + + It "should cache and return ACL list if response is not empty" { + $mockACLList = @{ count = 1; value = @("aclEntry1", "aclEntry2") } + + Mock Invoke-AzDevOpsApiRestMethod { return $mockACLList } + + $result = Get-DevOpsACL -OrganizationName "TestOrg" -SecurityDescriptorId "abc123" + + $result | Should -HaveCount 2 + Assert-MockCalled Add-CacheItem -Times 1 -Exactly -Scope It + Assert-MockCalled Export-CacheObject -Times 1 -Exactly -Scope It + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.tests.ps1 new file mode 100644 index 000000000..b8248e392 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.tests.ps1 @@ -0,0 +1,82 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-DevOpsDescriptorIdentity' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-DevOpsDescriptorIdentity.tests.ps1' + } + + # Load the functions to test + + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + $OrganizationName = "MyOrg" + $SubjectDescriptor = "subject:abcd1234" + $Descriptor = "descriptor:abcd1234" + $ApiVersion = "5.0" + + Mock -CommandName Get-AzDevOpsApiVersion { return "5.0" } + Mock -CommandName Invoke-AzDevOpsApiRestMethod + + } + + Context 'With Default Parameter Set and SubjectDescriptor' { + It 'Calls Invoke-AzDevOpsApiRestMethod with correct parameters' { + Get-DevOpsDescriptorIdentity -OrganizationName $OrganizationName -SubjectDescriptor $SubjectDescriptor + + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly 1 -Scope It -ParameterFilter { + $Uri -match "subjectDescriptors=$SubjectDescriptor" -and + $Uri -match "_apis/identities" -and + $Method -eq 'Get' + } + } + } + + Context 'With Descriptors Parameter Set' { + It 'Calls Invoke-AzDevOpsApiRestMethod with correct parameters' { + Get-DevOpsDescriptorIdentity -OrganizationName $OrganizationName -Descriptor $Descriptor + + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly 1 -Scope It -ParameterFilter { + $Uri -match "descriptors=$Descriptor" -and + $Uri -match "_apis/identities" -and + $Method -eq 'Get' + } + } + } + + Context 'Handles empty response' { + It 'Returns $null when identity value is $null' { + Mock -CommandName Invoke-AzDevOpsApiRestMethod { return @{ value = $null; count = 0 } } + + $result = Get-DevOpsDescriptorIdentity -OrganizationName $OrganizationName -SubjectDescriptor $SubjectDescriptor + + $result | Should -BeNullOrEmpty + } + + It 'Returns $null when count is greater than 1' { + Mock -CommandName Invoke-AzDevOpsApiRestMethod { return @{ value = @('identity1', 'identity2'); count = 2 } } + + $result = Get-DevOpsDescriptorIdentity -OrganizationName $OrganizationName -SubjectDescriptor $SubjectDescriptor + + $result | Should -BeNullOrEmpty + } + } + + Context 'Handles valid response' { + It 'Returns identity value when count is 1' { + $expectedValue = @('identity1') + Mock -CommandName Invoke-AzDevOpsApiRestMethod { return @{ value = $expectedValue; count = 1 } } + + $result = Get-DevOpsDescriptorIdentity -OrganizationName $OrganizationName -SubjectDescriptor $SubjectDescriptor + + $result | Should -Be $expectedValue + } + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 new file mode 100644 index 000000000..32e02e967 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 @@ -0,0 +1,70 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Remove-xAzDoPermission' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoPermission.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { '5.1' } + + } + + Context 'When invoked' { + It 'Should call Invoke-AzDevOpsApiRestMethod with correct parameters' { + + # Arrange + $OrganizationName = 'ExampleOrg' + $SecurityNamespaceID = '00000000-0000-0000-0000-000000000000' + $TokenName = 'ExampleToken' + $ApiVersion = '5.1' + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return $true + } + Mock -CommandName Write-Error + + # Act + $result = Remove-xAzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -TokenName $TokenName -ApiVersion $ApiVersion + + # Assert + $result | Should -BeNullOrEmpty + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly 1 -ParameterFilter { + $Uri -eq "https://dev.azure.com/$OrganizationName/_apis/securitynamespaces/$SecurityNamespaceID/descriptors/$TokenName?api-version=$ApiVersion" + $Method -eq 'DELETE' + } + Assert-MockCalled -CommandName Write-Error -Exactly 0 + + } + + It 'Should handle exceptions and write error message' { + + # Arrange + $OrganizationName = 'ExampleOrg' + $SecurityNamespaceID = '00000000-0000-0000-0000-000000000000' + $TokenName = 'ExampleToken' + $ApiVersion = '5.1' + + Mock -CommandName Invoke-AzDevOpsApiRestMethod + Mock -CommandName Write-Error + + # Act + $result = Remove-xAzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -TokenName $TokenName -ApiVersion $ApiVersion + $result | Should -BeNullOrEmpty + + # Assert + Assert-MockCalled -CommandName Write-Error -Exactly 1 + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly 1 + } + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 new file mode 100644 index 000000000..33eab2655 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 @@ -0,0 +1,75 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Set-xAzDoPermission Tests' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoPermission.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion { return '6.0-preview.1' } + Mock -CommandName Invoke-AzDevOpsApiRestMethod { return $null } + + $OrganizationName = "TestOrg" + $SecurityNamespaceID = "TestNamespace" + $SerializedACLs = @{some = "data"} + $ApiVersion = "5.0" + + } + + + Context 'When Mandatory Parameters are provided' { + It 'Should call Invoke-AzDevOpsApiRestMethod with correct parameters' { + $expectedUri = "https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}" -f $OrganizationName, $SecurityNamespaceID, $ApiVersion + + Set-xAzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -SerializedACLs $SerializedACLs -ApiVersion $ApiVersion + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly 1 -ParameterFilter { + $Uri -eq $expectedUri + $Method -eq 'POST' + $Body -eq $SerializedACLs + } + } + } + + Context 'When ApiVersion is not provided' { + It 'Should call ExampleFunction and get default ApiVersion' { + $expectedUri = "https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}" -f $OrganizationName, $SecurityNamespaceID, "6.0-preview.1" + + Set-xAzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -SerializedACLs $SerializedACLs + + Assert-MockCalled -CommandName Get-AzDevOpsApiVersion -Exactly 1 + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly 1 -ParameterFilter { + $Uri -eq $expectedUri + $Method -eq 'POST' + $Body -eq $SerializedACLs + } + } + } + + Context 'When an exception occurs' { + + It 'Should catch and log the error' { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { throw "API call failed" } + Mock -CommandName Write-Error + + $result = Set-xAzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -SerializedACLs $SerializedACLs -ApiVersion $ApiVersion + + $result | Should -BeNullOrEmpty + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly 1 + Assert-MockCalled -CommandName Write-Error -Exactly -Times 1 + + } + } +} + + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.tests.ps1 new file mode 100644 index 000000000..2548a0f3c --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.tests.ps1 @@ -0,0 +1,76 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'List-DevOpsGitRepository' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'List-DevOpsGitRepository.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return '6.0' } + Mock -CommandName Invoke-AzDevOpsApiRestMethod + } + + It 'Returns repositories when API provides data' { + $mockResult = @{ + value = @( + @{name = 'Repo1'; id = '1'}, + @{name = 'Repo2'; id = '2'} + ) + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { return $mockResult } + + $result = List-DevOpsGitRepository -OrganizationName 'org' -ProjectName 'proj' + + $result | Should -HaveCount 2 + $result[0].name | Should -Be 'Repo1' + $result[1].name | Should -Be 'Repo2' + } + + It 'Returns null when API does not provide data' { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { + return @{ value = $null } + } + + $result = List-DevOpsGitRepository -OrganizationName 'org' -ProjectName 'proj' + $result | Should -Be $null + + } + + It 'Uses default API version if not provided' { + $result = List-DevOpsGitRepository -OrganizationName 'org' -ProjectName 'proj' + + Assert-MockCalled Get-AzDevOpsApiVersion -Exactly 1 + } + + It 'Uses provided API version' { + $customApiVersion = '5.1' + + $result = List-DevOpsGitRepository -OrganizationName 'org' -ProjectName 'proj' -ApiVersion $customApiVersion + + Assert-MockCalled Get-AzDevOpsApiVersion -Exactly 0 + } + + It 'Calls the REST API with correct parameters' { + $dummyOrg = 'dummyOrg' + $dummyProj = 'dummyProj' + + List-DevOpsGitRepository -OrganizationName $dummyOrg -ProjectName $dummyProj + + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $Uri -eq "https://dev.azure.com/$dummyOrg/$dummyProj/_apis/git/repositories" -and + $Method -eq 'Get' + } + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.tests.ps1 new file mode 100644 index 000000000..a2d80307a --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.tests.ps1 @@ -0,0 +1,78 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'List-DevOpsGroupMembers' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'List-DevOpsGroupMembers.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return '6.0-preview.1' } + + } + + Context 'When called with mandatory parameters' { + + # Inject expected parameters + It 'Should return group members' { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ + value = @( + @{principalName = 'user1@domain.com'} + @{principalName = 'user2@domain.com'} + ) + } + } + + # Inject expected parameters + $result = List-DevOpsGroupMembers -Organization 'MyOrg' -GroupDescriptor 'MyGroup' + $result | Should -Not -BeNullOrEmpty + + # Validate the result + $result[0].principalName | Should -Be 'user1@domain.com' + $result[1].principalName | Should -Be 'user2@domain.com' + + } + } + + Context 'When no members are found' { + + It 'Should return null' { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { + return @{ value = $null } + } + + $result = List-DevOpsGroupMembers -Organization 'MyOrg' -GroupDescriptor 'MyGroup' + + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly 1 + $result | Should -BeNullOrEmpty + } + + } + + Context 'When optional ApiVersion parameter is provided' { + + It 'Should ignore Get-AzDevOpsApiVersion and use provided ApiVersion' { + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return '5.0' } + Mock -CommandName Invoke-AzDevOpsApiRestMethod { return $null } + + # Inject expected ApiVersion + $null = List-DevOpsGroupMembers -Organization 'MyOrg' -GroupDescriptor 'MyGroup' -ApiVersion '6.0-preview.1' + Assert-MockCalled Get-AzDevOpsApiVersion -Exactly 0 -ParameterFilter { + $Uri -eq 'https://dev.azure.com/MyOrg/_apis/graph/groups/MyGroup/members?api-version=6.0-preview.1' + } + } + + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.tests.ps1 new file mode 100644 index 000000000..10dee9de8 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.tests.ps1 @@ -0,0 +1,78 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'List-DevOpsGroups' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'List-DevOpsGroups.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return '6.0-preview' } + + } + + Context "When calling List-DevOpsGroups" { + + BeforeAll { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ + value = @( + @{ + displayName = 'Group1' + }, + @{ + displayName = 'Group2' + } + ) + } + } + } + + It 'should call Invoke-AzDevOpsApiRestMethod' { + List-DevOpsGroups -Organization 'myOrg' + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly 1 + } + + It 'should call Get-AzDevOpsApiVersion if no ApiVersion is specified' { + List-DevOpsGroups -Organization 'myOrg' + Assert-MockCalled Get-AzDevOpsApiVersion -Exactly 1 + } + + It 'should not call Get-AzDevOpsApiVersion if ApiVersion is specified' { + List-DevOpsGroups -Organization 'myOrg' -ApiVersion '5.1' + Assert-MockCalled Get-AzDevOpsApiVersion -Exactly 0 -ParameterFilter { + $Uri -eq 'https://dev.azure.com/myOrg/_apis/graph/groups?api-version=5.1' + } + } + + It 'should return group data' { + $result = List-DevOpsGroups -Organization 'myOrg' + $result.Count | Should -Be 2 + $result[0].displayName | Should -Be 'Group1' + $result[1].displayName | Should -Be 'Group2' + } + + } + + Context "When no groups are found" { + + BeforeAll { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { return @{ value = $null } } + } + + It 'should return null if no groups are found' { + $result = List-DevOpsGroups -Organization 'myOrg' + $result | Should -BeNullOrEmpty + } + + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.tests.ps1 new file mode 100644 index 000000000..84e2774a2 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.tests.ps1 @@ -0,0 +1,90 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'List-DevOpsProcess' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'List-DevOpsProcess.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return "6.0" } + + } + + # Test cases + Context 'When called with mandatory parameters' { + + # Validate the call + It 'should call Invoke-AzDevOpsApiRestMethod' { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ value = @() } + } + + List-DevOpsProcess -Organization "MyOrganization" + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $apiUri -eq "https://dev.azure.com/MyOrganization/_apis/process/processes?api-version=6.0" -and + $Method -eq 'Get' + } -Times 1 + } + + it "should change the url when the api-version changes" { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ value = @() } + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return "6.1" } + + List-DevOpsProcess -Organization "MyOrganization" + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $apiUri -eq "https://dev.azure.com/MyOrganization/_apis/process/processes?api-version=6.1" -and + $Method -eq 'Get' + } -Times 1 + } + + # Validate the call + It 'should return the process groups' { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ + value = @( + @{ id = "1"; name = "Agile" } + @{ id = "2"; name = "Scrum" } + ) + } + } + + # Validate the result + $result = List-DevOpsProcess -Organization "MyOrganization" + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 2 + + # Validate the first process + $result.id[0] | Should -Be "1" + $result.name[0] | Should -Be "Agile" + $result.id[1] | Should -Be "2" + $result.name[1] | Should -Be "Scrum" + + } + } + + Context 'When no processes are returned' { + + It 'should return $null' { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { return @{ value = $null } } + + $result = List-DevOpsProcess -Organization "MyOrganization" + $result | Should -Be $null + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.tests.ps1 new file mode 100644 index 000000000..7d0f9fd2e --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.tests.ps1 @@ -0,0 +1,49 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "List-DevOpsProjects" -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'List-DevOpsProjects.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { + return "6.0" + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ + value = @( + @{ name = "Project1"; id = "123" }, + @{ name = "Project2"; id = "456" } + ) + } + } + } + + It "Returns project list when called with valid organization name" { + $result = List-DevOpsProjects -OrganizationName "TestOrg" + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 2 + $result[0].name | Should -Be "Project1" + $result[1].name | Should -Be "Project2" + } + + It "Returns null when no projects found" { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ value = @() } + } + + $result = List-DevOpsProjects -OrganizationName "TestOrg" + $result | Should -BeNullOrEmpty + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.tests.ps1 new file mode 100644 index 000000000..f96d04fbf --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.tests.ps1 @@ -0,0 +1,67 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "List-DevOpsSecurityNamespaces" -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'List-DevOpsSecurityNamespaces.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { + return @{ + value = @( + @{ + namespaceId = "testNamespace1" + description = "Test Namespace 1" + }, + @{ + namespaceId = "testNamespace2" + description = "Test Namespace 2" + } + ) + } + } + + } + + It "Should call Invoke-AzDevOpsApiRestMethod" { + $organizationName = "TestOrganization" + + List-DevOpsSecurityNamespaces -OrganizationName $organizationName + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $apiUri -eq "https://dev.azure.com/$organizationName/_apis/securitynamespaces/" -and + $Method -eq 'Get' + } -Times 1 + } + + It "Should return the namespaces value when present" { + $organizationName = "TestOrganization" + + $result = List-DevOpsSecurityNamespaces -OrganizationName $organizationName + $result | Should -HaveCount 2 + $result[0].namespaceId | Should -Be "testNamespace1" + $result[1].namespaceId | Should -Be "testNamespace2" + } + + It 'Should return $null when there are no namespaces' { + Mock Invoke-AzDevOpsApiRestMethod { + return @{ + value = $null + } + } + + $organizationName = "TestOrganization" + + $result = List-DevOpsSecurityNamespaces -OrganizationName $organizationName + $result | Should -BeNullOrEmpty + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.tests.ps1 new file mode 100644 index 000000000..35c5a8712 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.tests.ps1 @@ -0,0 +1,85 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'List-DevOpsServicePrinciples' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'List-DevOpsServicePrinciples.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion { + return '6.0-preview.1' + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { + return @{ + value = @( + @{ + id = 'sp1' + displayName = 'Service Principal 1' + }, + @{ + id = 'sp2' + displayName = 'Service Principal 2' + } + ) + } + } + + } + + Context 'When called with valid OrganizationName' { + It 'Returns a list of service principals' { + $result = List-DevOpsServicePrinciples -OrganizationName 'MyOrg' + + $result | Should -Not -BeNullOrEmpty + $result | Should -HaveCount 2 + $result[0].id | Should -Be 'sp1' + $result[0].displayName | Should -Be 'Service Principal 1' + $result[1].id | Should -Be 'sp2' + $result[1].displayName | Should -Be 'Service Principal 2' + } + + It 'Uses the default API version if not provided' { + List-DevOpsServicePrinciples -OrganizationName 'MyOrg' + + Assert-MockCalled Get-AzDevOpsApiVersion -Exactly 1 -Scope It + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $Uri -eq 'https://vssps.dev.azure.com/MyOrg/_apis/graph/serviceprincipals' -and + $Method -eq 'Get' + } -Exactly 1 -Scope It + } + + It 'Uses the provided API version if specified' { + List-DevOpsServicePrinciples -OrganizationName 'MyOrg' -ApiVersion '5.0-preview.1' + + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $Uri -eq 'https://vssps.dev.azure.com/MyOrg/_apis/graph/serviceprincipals' -and + $Method -eq 'Get' + } -Exactly 1 -Scope It + } + } + + Context 'When API returns null' { + + It 'Returns null' { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { + return @{ value = $null } + } + + $result = List-DevOpsServicePrinciples -OrganizationName 'MyOrg' + $result | Should -BeNull + + } + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.tests.ps1 new file mode 100644 index 000000000..dc0f4019a --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.tests.ps1 @@ -0,0 +1,75 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'List-UserCache' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'List-UserCache.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion { return "5.0-preview.1" } + Mock -CommandName Invoke-AzDevOpsApiRestMethod + + } + + Context 'when called with valid OrganizationName' { + It 'should call Invoke-AzDevOpsApiRestMethod with correct parameters' { + $OrganizationName = 'TestOrg' + $ApiVersion = '5.0-preview.1' + $response = @{ + value = @( + @{ displayName = 'User1' } + @{ displayName = 'User2' } + ) + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { return $response } + + $result = List-UserCache -OrganizationName $OrganizationName -ApiVersion $ApiVersion + + $expectedUri = "https://vssps.dev.azure.com/$OrganizationName/_apis/graph/users" + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Times 1 -Exactly -Scope It -ParameterFilter { + $Uri -eq $expectedUri -and + $Method -eq 'Get' + } + + $result.Count | Should -Be 2 + $result[0].displayName | Should -Be 'User1' + $result[1].displayName | Should -Be 'User2' + } + } + + Context 'when API returns null' { + It 'should return null' { + $OrganizationName = 'TestOrg' + $ApiVersion = '5.0-preview.1' + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { return @{ value = $null } } + + $result = List-UserCache -OrganizationName $OrganizationName -ApiVersion $ApiVersion + + $result | Should -BeNullOrEmpty + } + } + + Context 'when ApiVersion is not provided' { + It 'should call Get-AzDevOpsApiVersion' { + $OrganizationName = 'TestOrg' + + Mock -CommandName Get-AzDevOpsApiVersion { return "5.0-preview.1" } + + $result = List-UserCache -OrganizationName $OrganizationName + + Assert-MockCalled -CommandName Get-AzDevOpsApiVersion -Exactly 1 + } + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.tests.ps1 new file mode 100644 index 000000000..171ca704c --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.tests.ps1 @@ -0,0 +1,57 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'New-GitRepository Tests' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-GitRepository.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod + + } + + Context 'When creating a repository successfully' { + + It 'should invoke the REST method with correct parameters' { + $mockApiUri = "https://dev.azure.com/org" + $mockProject = [PSCustomObject]@{ name = 'TestProject'; id = '12345' } + $mockRepoName = "TestRepo" + $mockApiVersion = "5.0" + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + [PSCustomObject]@{ name = $mockRepoName } + } -Verifiable -ParameterFilter { $Body -match "TestRepo" } + + $result = New-GitRepository -ApiUri $mockApiUri -Project $mockProject -RepositoryName $mockRepoName -ApiVersion $mockApiVersion + + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly 1 + $result.name | Should -Be $mockRepoName + } + } + + Context 'When failing to create a repository' { + + It 'should throw an error and write an error message' { + $mockApiUri = "https://dev.azure.com/org" + $mockProject = [PSCustomObject]@{ name = 'TestProject'; id = '12345' } + $mockRepoName = "TestRepo" + $mockApiVersion = "5.0" + + Mock -CommandName Write-Error -Verifiable + + { New-GitRepository -ApiUri $mockApiUri -Project $mockProject -RepositoryName $mockRepoName -ApiVersion $mockApiVersion } | Should -Not -Throw + + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly 1 + } + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.tests.ps1 new file mode 100644 index 000000000..11f972140 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.tests.ps1 @@ -0,0 +1,63 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Remove-GitRepository" -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-GitRepository.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return "5.0" } + Mock -CommandName Invoke-AzDevOpsApiRestMethod + + } + + It "Should call Invoke-AzDevOpsApiRestMethod with correct parameters" { + $ApiUri = "https://dev.azure.com/organization" + $Project = [PSCustomObject]@{ name = "SampleProject" } + $Repository = [PSCustomObject]@{ id = "123"; Name = "SampleRepo" } + + Remove-GitRepository -ApiUri $ApiUri -Project $Project -Repository $Repository + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly -Times 1 -ParameterFilter { + $ApiUri -eq "https://dev.azure.com/organization/SampleProject/_apis/git/repositories/123?api-version=5.0" -and + $Method -eq 'Delete' + } + } + + It "Should use the provided ApiVersion if specified" { + $ApiUri = "https://dev.azure.com/organization" + $Project = [PSCustomObject]@{ name = "SampleProject" } + $Repository = [PSCustomObject]@{ id = "123"; Name = "SampleRepo" } + $ApiVersion = "6.0" + + Remove-GitRepository -ApiUri $ApiUri -Project $Project -Repository $Repository -ApiVersion $ApiVersion + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly -Times 1 -ParameterFilter { + $ApiUri -eq "https://dev.azure.com/organization/SampleProject/_apis/git/repositories/123?api-version=6.0" -and + $Method -eq 'Delete' + } + } + + It "Should handle and display the error if Invoke-AzDevOpsApiRestMethod throws an exception" { + $ApiUri = "https://dev.azure.com/organization" + $Project = [PSCustomObject]@{ name = "SampleProject" } + $Repository = [PSCustomObject]@{ id = "123"; Name = "SampleRepo" } + + Mock -CommandName Write-Error -Verifiable + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { throw "API Error" } + + { Remove-GitRepository -ApiUri $ApiUri -Project $Project -Repository $Repository } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly -Times 1 + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.tests.ps1 new file mode 100644 index 000000000..1e273e257 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.tests.ps1 @@ -0,0 +1,78 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "New-DevOpsGroup" -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-DevOpsGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ + displayName = $GroupName + description = $GroupDescription + id = "mock-id" + } + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return "6.0" } + } + + Context "When required parameters are provided" { + It 'Creates a new group successfully' { + $ApiUri = "https://dev.azure.com/myorganization" + $GroupName = "MyGroup" + + $result = New-DevOpsGroup -ApiUri $ApiUri -GroupName $GroupName + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly -Times 1 + $result.displayName | Should -Be $GroupName + } + } + + Context "When optional parameters are provided" { + It 'Creates a new group successfully with description' { + $ApiUri = "https://dev.azure.com/myorganization" + $GroupName = "MyGroup" + $GroupDescription = "A sample group" + + $result = New-DevOpsGroup -ApiUri $ApiUri -GroupName $GroupName -GroupDescription $GroupDescription + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly -Times 1 + $result.description | Should -Be $GroupDescription + } + + It 'Creates a new group successfully with project scope descriptor' { + $ApiUri = "https://dev.azure.com/myorganization" + $GroupName = "MyGroup" + $ProjectScopeDescriptor = "vstfs:///Classification/TeamProject/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + + $result = New-DevOpsGroup -ApiUri $ApiUri -GroupName $GroupName -ProjectScopeDescriptor $ProjectScopeDescriptor + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly -Times 1 + $result.displayName | Should -Be $GroupName + } + } + + Context "When an exception is thrown" { + BeforeAll { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { throw "API call failed" } + Mock -CommandName Write-Error -Verifiable + } + + It 'Handles the error and writes an error message' { + $ApiUri = "https://dev.azure.com/myorganization" + $GroupName = "MyGroup" + + { New-DevOpsGroup -ApiUri $ApiUri -GroupName $GroupName } | Should -Not -Throw + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.tests.ps1 new file mode 100644 index 000000000..c1990d6e3 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.tests.ps1 @@ -0,0 +1,70 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Remove-DevOpsGroup' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-DevOpsGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { "6.0" } + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + [PSCustomObject]@{ success = $true } + } + + $Uri = "https://dev.azure.com/myorganization" + $GroupDescriptor = "MyGroup" + $ApiVersion = "6.0" + } + + Context 'When all mandatory parameters are provided' { + It 'should make a DELETE request to Azure DevOps API' { + + $params = @{ + Uri = '{0}/_apis/graph/groups/{1}?api-version={2}' -f $Uri, $GroupDescriptor, $ApiVersion + Method = 'Delete' + } + + $result = Remove-DevOpsGroup -ApiUri $Uri -GroupDescriptor $GroupDescriptor + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly 1 -ParameterFilter { + $ApiUri -eq $params.Uri -and + $Method -eq $params.Method + } + $result.success | Should -Be $true + } + } + + Context 'When ApiVersion parameter is not provided' { + It 'should use the default ApiVersion' { + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { "6.0" } + + $result = Remove-DevOpsGroup -ApiUri $Uri -GroupDescriptor $GroupDescriptor + + Assert-MockCalled -CommandName Get-AzDevOpsApiVersion -Exactly -Times 1 + $result.success | Should -Be $true + } + } + + Context 'When an error occurs during the API call' { + BeforeAll { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + throw "API call failed" + } + + Mock -CommandName Write-Error -Verifiable + } + + It 'should catch and log the error' { + { Remove-DevOpsGroup -ApiUri $Uri -GroupDescriptor $GroupDescriptor } | Should -Not -Throw + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.tests.ps1 new file mode 100644 index 000000000..777d13548 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.tests.ps1 @@ -0,0 +1,63 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Set-DevOpsGroup' -Tags "Unit", "API" { + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-DevOpsGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return '6.0-preview.1' } + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { return [PSCustomObject]@{displayName = 'MyGroup'; description = 'Updated group description'} } + + } + + Context 'Default ParameterSet' { + It 'Should invoke the API with correct parameters and update the group' { + + $params = @{ + ApiUri = "https://dev.azure.com/contoso" + GroupName = "MyGroup" + GroupDescription = "Updated group description" + GroupDescriptor = "some-group-descriptor" + } + + $result = Set-DevOpsGroup @params + + $result.displayName | Should -Be 'MyGroup' + $result.description | Should -Be 'Updated group description' + Assert-MockCalled Get-AzDevOpsApiVersion -Exactly 1 + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly 1 -Scope It -ParameterFilter { + $ApiUri -eq "https://dev.azure.com/contoso/_apis/graph/groups/some-group-descriptor?api-version=6.0-preview.1" -and + $Method -eq 'Patch' + } + } + } + + Context 'ProjectScope ParameterSet' { + It 'Should invoke the API with correct parameters and update the group within project scope' { + $params = @{ + ApiUri = "https://dev.azure.com/contoso" + GroupName = "MyGroup" + GroupDescription = "Updated group description" + ProjectScopeDescriptor = "some-project-scope" + } + $result = Set-DevOpsGroup @params + + $result.displayName | Should -Be 'MyGroup' + $result.description | Should -Be 'Updated group description' + Assert-MockCalled Get-AzDevOpsApiVersion -Exactly 1 + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly 1 -Scope It -ParameterFilter { + $ApiUri -eq "https://dev.azure.com/contoso/_apis/graph/groups?scopeDescriptor=some-project-scope&api-version=6.0-preview.1" -and + $Method -eq 'Patch' + } + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.tests.ps1 new file mode 100644 index 000000000..cde060147 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.tests.ps1 @@ -0,0 +1,91 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "New-DevOpsGroupMember Tests" -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-DevOpsGroupMember.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return "3.0-preview" } + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ + success = $true + message = "Member added successfully" + } + } + Mock -CommandName Write-Verbose + Mock -CommandName Write-Error + } + + Context "When adding a new member to a DevOps group" { + + It "should call Get-AzDevOpsApiVersion if ApiVersion is not provided" { + $GroupIdentity = [PSCustomObject]@{ descriptor = "group-descriptor" } + $MemberIdentity = [PSCustomObject]@{ descriptor = "member-descriptor" } + $ApiUri = "https://dev.azure.com/organization" + + New-DevOpsGroupMember -GroupIdentity $GroupIdentity -MemberIdentity $MemberIdentity -ApiUri $ApiUri + + Assert-MockCalled Get-AzDevOpsApiVersion -Exactly 1 + } + + It "should not call Get-AzDevOpsApiVersion if ApiVersion is provided" { + $GroupIdentity = [PSCustomObject]@{ descriptor = "group-descriptor" } + $MemberIdentity = [PSCustomObject]@{ descriptor = "member-descriptor" } + $ApiVersion = "6.0" + $ApiUri = "https://dev.azure.com/organization" + + New-DevOpsGroupMember -GroupIdentity $GroupIdentity -MemberIdentity $MemberIdentity -ApiUri $ApiUri -ApiVersion $ApiVersion + + Assert-MockCalled Get-AzDevOpsApiVersion -Exactly 0 + } + + It "should call Invoke-AzDevOpsApiRestMethod with correct parameters" { + $GroupIdentity = [PSCustomObject]@{ descriptor = "group-descriptor" } + $MemberIdentity = [PSCustomObject]@{ descriptor = "member-descriptor" } + $ApiVersion = "6.0" + $ApiUri = "https://dev.azure.com/organization" + $expectedUri = "$ApiUri/_apis/graph/memberships/$($MemberIdentity.descriptor)/$($GroupIdentity.descriptor)?api-version=$ApiVersion" + + New-DevOpsGroupMember -GroupIdentity $GroupIdentity -MemberIdentity $MemberIdentity -ApiUri $ApiUri -ApiVersion $ApiVersion + + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly 1 -ParameterFilter { + $ApiUri -eq $expectedUri -and + $Method -eq "PUT" + } + } + + It "should write a verbose message if member is added successfully" { + $GroupIdentity = [PSCustomObject]@{ descriptor = "group-descriptor" } + $MemberIdentity = [PSCustomObject]@{ descriptor = "member-descriptor" } + $ApiUri = "https://dev.azure.com/organization" + + New-DevOpsGroupMember -GroupIdentity $GroupIdentity -MemberIdentity $MemberIdentity -ApiUri $ApiUri + + Assert-MockCalled Write-Verbose -Times 3 + } + + It "should write an error message if adding a member to the group fails" { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + throw "API call failed" + } + + $GroupIdentity = [PSCustomObject]@{ descriptor = "group-descriptor" } + $MemberIdentity = [PSCustomObject]@{ descriptor = "member-descriptor" } + $ApiUri = "https://dev.azure.com/organization" + + New-DevOpsGroupMember -GroupIdentity $GroupIdentity -MemberIdentity $MemberIdentity -ApiUri $ApiUri + + Assert-MockCalled Write-Error -Exactly 1 + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.tests.ps1 new file mode 100644 index 000000000..f0a9efe26 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.tests.ps1 @@ -0,0 +1,65 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Remove-DevOpsGroupMember' -Tags "Unit", "API" { + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-DevOpsGroupMember.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return '6.0-preview' } + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { return $null } + } + + Context 'When all parameters are valid' { + It 'Removes a member from the group' { + $group = [PSCustomObject]@{ descriptor = 'group-descriptor' } + $member = [PSCustomObject]@{ descriptor = 'member-descriptor' } + $apiUri = 'https://dev.azure.com/myorg' + + Remove-DevOpsGroupMember -GroupIdentity $group -MemberIdentity $member -ApiUri $apiUri + + Assert-MockCalled Get-AzDevOpsApiVersion -Times 1 + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $ApiUri -eq 'https://dev.azure.com/myorg/_apis/graph/memberships/member-descriptor/group-descriptor?api-version=6.0-preview' -and + $Method -eq 'DELETE' + } -Times 1 + } + } + + Context 'When API call fails' { + It 'Handles the error gracefully' { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { throw 'API call failed' } + Mock -CommandName Write-Error -Verifiable + + $group = [PSCustomObject]@{ descriptor = 'group-descriptor' } + $member = [PSCustomObject]@{ descriptor = 'member-descriptor' } + $apiUri = 'https://dev.azure.com/myorg' + + { Remove-DevOpsGroupMember -GroupIdentity $group -MemberIdentity $member -ApiUri $apiUri } | Should -Not -Throw + } + } + + Context 'When ApiVersion parameter is provided' { + It 'Uses the specified ApiVersion' { + $group = [PSCustomObject]@{ descriptor = 'group-descriptor' } + $member = [PSCustomObject]@{ descriptor = 'member-descriptor' } + $apiUri = 'https://dev.azure.com/myorg' + $apiVersion = '6.0' + + Remove-DevOpsGroupMember -GroupIdentity $group -MemberIdentity $member -ApiUri $apiUri -ApiVersion $apiVersion + + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $ApiUri -eq "https://dev.azure.com/myorg/_apis/graph/memberships/$($member.descriptor)/$($group.descriptor)?api-version=$apiVersion" -and + $Method -eq 'DELETE' + } -Times 1 + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.tests.ps1 new file mode 100644 index 000000000..71755fd34 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.tests.ps1 @@ -0,0 +1,89 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'New-DevOpsProject' -Tags "Unit", "API" { + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-DevOpsProject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + @{ + id = "1234" + name = "MyProject" + description = "This is a new project" + visibility = "private" + } + } + } + + Context 'Creates a new Azure DevOps project with valid parameters' { + + It 'Should call Invoke-AzDevOpsApiRestMethod with correct parameters and return the project details' { + $organization = "myorg" + $projectName = "MyProject" + $projectDescription = "This is a new project" + $sourceControlType = "Git" + $processTemplateId = "6b724908-ef14-45cf-84f8-768b5384da45" + $visibility = "private" + $apiVersion = "6.0" + + $result = New-DevOpsProject -Organization $organization -ProjectName $projectName -Description $projectDescription -SourceControlType $sourceControlType -ProcessTemplateId $processTemplateId -Visibility $visibility -ApiVersion $apiVersion + + $result | Should -Not -BeNullOrEmpty + $result.name | Should -Be $projectName + $result.description | Should -Be $projectDescription + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Times 1 -Exactly -Scope It -ParameterFilter { + $params = $_ + $APIUri -eq "https://dev.azure.com/$organization/_apis/projects?api-version=$apiVersion" -and + $Method -eq "POST" -and + $Body -ne $null + } + + } + } + + Context 'Handles errors gracefully' { + BeforeEach { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { throw "API call failed" } -Verifiable + Mock -CommandName Write-Error + } + + It 'Should return an error message if API call fails' { + { + New-DevOpsProject -Organization "myorg" -ProjectName "MyProject" -Description "This is a new project" -SourceControlType "Git" -ProcessTemplateId "6b724908-ef14-45cf-84f8-768b5384da45" -Visibility "private" -ApiVersion "6.0" + } | Should -Not -Throw + + } + } + + Context 'Validates parameters correctly' { + + It 'Should validate ProjectName using Test-AzDevOpsProjectName' { + + # Mock the Test-AzDevOpsProjectName function to return $true + Mock -CommandName Test-AzDevOpsProjectName -MockWith { $true } + + $organization = "myorg" + $projectName = "MyProject" + $projectDescription = "This is a new project" + $sourceControlType = "Git" + $processTemplateId = "6b724908-ef14-45cf-84f8-768b5384da45" + $visibility = "private" + $apiVersion = "6.0" + + $result = New-DevOpsProject -Organization $organization -ProjectName $projectName -Description $projectDescription -SourceControlType $sourceControlType -ProcessTemplateId $processTemplateId -Visibility $visibility -ApiVersion $apiVersion + + Assert-MockCalled -CommandName Test-AzDevOpsProjectName -Times 1 + + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.tests.ps1 new file mode 100644 index 000000000..f105177c9 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.tests.ps1 @@ -0,0 +1,50 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Remove-DevOpsProject" -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-DevOpsProject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return "5.1-preview.1" } + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith {} + + } + + Context "When removing a project" { + + It "Should call Invoke-AzDevOpsApiRestMethod with correct parameters" { + $org = "MyOrganization" + $projectId = "MyProject" + $apiVersion = "5.1-preview.1" + + Remove-DevOpsProject -Organization $org -ProjectId $projectId + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly 1 -ParameterFilter { + $ApiUri -eq "https://dev.azure.com/MyOrganization/_apis/projects/MyProject?api-version=5.1-preview.1" -and + $Method -eq "DELETE" + } + } + + } + + Context "When an exception occurs" { + BeforeEach { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { throw "API call failed" } + Mock -CommandName Write-Error -Verifiable + } + + It "Should write an error message" { + { Remove-DevOpsProject -Organization "MyOrganization" -ProjectId "MyProject" } | Should -Not -Throw + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.tests.ps1 new file mode 100644 index 000000000..c92b343d2 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.tests.ps1 @@ -0,0 +1,105 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Update-DevOpsProject" -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Update-DevOpsProject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { + return @{ statusCode = 200; content = "Success" } + } + + } + + Context "When all mandatory parameters are provided" { + + It "Should call Invoke-AzDevOpsApiRestMethod with correct parameters" { + + $organization = "TestOrg" + $projectId = "TestProject" + $apiVersion = "6.0" + + $params = @{ + Organization = $organization + ProjectId = $projectId + ApiVersion = $apiVersion + } + + $result = Update-DevOpsProject @params + + # Assert that the mock was called with expected parameters + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly -Scope It -ParameterFilter { + $ApiUri -eq "https://dev.azure.com/TestOrg/_apis/projects/TestProject?api-version=6.0" -and + $Method -eq 'PATCH' + } + + # Assert that the result is as expected + $result.statusCode | Should -Be 200 + } + } + + Context "When optional parameters are provided" { + + It "Should include optional parameters in the request body" { + $organization = "TestOrg" + $projectId = "TestProject" + $projectDescription = "Test Description" + $visibility = "private" + $apiVersion = "6.0" + + $params = @{ + Organization = $organization + ProjectId = $projectId + ProjectDescription = $projectDescription + Visibility = $visibility + ApiVersion = $apiVersion + } + + $result = Update-DevOpsProject @params + + # Assert that the mock was called with expected parameters + Assert-MockCalled Invoke-AzDevOpsApiRestMethod -Exactly -Scope It -ParameterFilter { + $Body -match '"description": "Test Description"' -and + $Body -match '"visibility": "private"' + } + + # Assert that the result is as expected + $result.statusCode | Should -Be 200 + } + } + + Context "When an error occurs during API call" { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod { + throw "API call failed" + } + + It "Should catch the error and write an error message" { + + Mock -CommandName Write-Error -Verifiable + + $organization = "TestOrg" + $projectId = "TestProject" + $apiVersion = "6.0" + + $params = @{ + Organization = $organization + ProjectId = $projectId + ApiVersion = $apiVersion + } + + { Update-DevOpsProject @params } | Should -Not -Throw + + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.tests.ps1 new file mode 100644 index 000000000..2d2f5630c --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.tests.ps1 @@ -0,0 +1,111 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Wait-DevOpsProject" -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Wait-DevOpsProject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ status = 'wellFormed' } + } + + Mock -CommandName Write-Error + + } + + Context "When project is created successfully" { + + It "Should detect the project has been created successfully and exit the loop" { + $organizationName = "TestOrg" + $projectURL = "https://dev.azure.com/TestOrg/TestProject" + $apiVersion = "6.0" + + $params = @{ + OrganizationName = $organizationName + ProjectURL = $projectURL + ApiVersion = $apiVersion + } + + { Wait-DevOpsProject @params } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Time 1 + } + } + + Context "When project creation fails" { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ status = 'failed'; message = 'Creation failed' } + } + + It "Should detect the failure and write an error message" { + $organizationName = "TestOrg" + $projectURL = "https://dev.azure.com/TestOrg/TestProject" + $apiVersion = "6.0" + + $params = @{ + OrganizationName = $organizationName + ProjectURL = $projectURL + ApiVersion = $apiVersion + } + + { Wait-DevOpsProject @params } | Should -Not -Throw + + Assert-MockCalled -CommandName Write-Error + } + } + + Context "When project creation times out" { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ status = 'creating' } + } + + It "Should time out after 10 attempts and write an error message" { + $organizationName = "TestOrg" + $projectURL = "https://dev.azure.com/TestOrg/TestProject" + $apiVersion = "6.0" + + $params = @{ + OrganizationName = $organizationName + ProjectURL = $projectURL + ApiVersion = $apiVersion + } + + { Wait-DevOpsProject @params } | Should -Not -Throw + + Assert-MockCalled -CommandName Write-Error + } + } + + Context "When project creation status is not set" { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ status = 'notSet'; message = 'Status not set' } + } + + It "Should detect the status is not set and write an error message" { + + $organizationName = "TestOrg" + $projectURL = "https://dev.azure.com/TestOrg/TestProject" + $apiVersion = "6.0" + + $params = @{ + OrganizationName = $organizationName + ProjectURL = $projectURL + ApiVersion = $apiVersion + } + + { Wait-DevOpsProject @params } | Should -Not -Throw + + Assert-MockCalled -CommandName Write-Error + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.tests.ps1 new file mode 100644 index 000000000..d822ce133 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.tests.ps1 @@ -0,0 +1,83 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-ProjectServiceStatus' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-ProjectServiceStatus.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { + return '6.0-preview.1' + } + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return [pscustomobject]@{ + state = 'enabled' + } + } + + } + + Context 'When all parameters are valid' { + It 'Should return the state of the service as enabled' { + $organization = 'TestOrg' + $projectId = 'TestProjectId' + $serviceName = 'TestServiceName' + + $result = Get-ProjectServiceStatus -Organization $organization -ProjectId $projectId -ServiceName $serviceName + + $result.state | Should -Be 'enabled' + } + + It 'Should call Invoke-AzDevOpsApiRestMethod with correct parameters' { + $organization = 'TestOrg' + $projectId = 'TestProjectId' + $serviceName = 'TestServiceName' + + $result = Get-ProjectServiceStatus -Organization $organization -ProjectId $projectId -ServiceName $serviceName + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly -Times 1 + } + } + + Context 'When service state is undefined' { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return [pscustomobject]@{ + state = 'undefined' + } + } + + It 'Should treat undefined state as enabled' { + $organization = 'TestOrg' + $projectId = 'TestProjectId' + $serviceName = 'TestServiceName' + + $result = Get-ProjectServiceStatus -Organization $organization -ProjectId $projectId -ServiceName $serviceName + + $result.state | Should -Be 'enabled' + } + } + + Context 'When an error occurs during API call' { + + It 'Should write an error message' { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { throw "API Error" } + Mock -CommandName Write-Error -Verifiable + + $organization = 'TestOrg' + $projectId = 'TestProjectId' + $serviceName = 'TestServiceName' + + { Get-ProjectServiceStatus -Organization $organization -ProjectId $projectId -ServiceName $serviceName } | Should -Not -Throw + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.tests.ps1 new file mode 100644 index 000000000..665d2c257 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.tests.ps1 @@ -0,0 +1,80 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Set-ProjectServiceStatus' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-ProjectServiceStatus.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { + return '6.0' + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ + state = 'Enabled' + } + } + + } + + It 'should call Invoke-AzDevOpsApiRestMethod with correct parameters' { + + $Organization = 'TestOrg' + $ProjectId = 'TestProjId' + $ServiceName = 'Git' + $Body = @{ + state = 'Enabled' + } + $ApiVersion = '6.0' + + + $expectedUri = 'https://dev.azure.com/TestOrg/_apis/FeatureManagement/FeatureStates/host/project/TestProjId/Git?api-version=6.0' + + Set-ProjectServiceStatus -Organization $Organization -ProjectId $ProjectId -ServiceName $ServiceName -Body $Body -ApiVersion $ApiVersion + + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $Uri -eq $expectedUri -and + $Method -eq 'PATCH' + } -Exactly -Times 1 + } + + It 'should return the state of the service if the API call is successful' { + $Organization = 'TestOrg' + $ProjectId = 'TestProjId' + $ServiceName = 'Git' + $Body = @{ + state = 'Enabled' + } + + $result = Set-ProjectServiceStatus -Organization $Organization -ProjectId $ProjectId -ServiceName $ServiceName -Body $Body + + $result | Should -Be 'Enabled' + } + + It 'should return error message when API call fails' { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + throw "API call failed" + } + + Mock -CommandName Write-Error -Verifiable + + $Organization = 'TestOrg' + $ProjectId = 'TestProjId' + $ServiceName = 'Git' + $Body = @{ + state = 'Enabled' + } + + { Set-ProjectServiceStatus -Organization $Organization -ProjectId $ProjectId -ServiceName $ServiceName -Body $Body } | Should -Not -Throw + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.tests.ps1 new file mode 100644 index 000000000..676afe2e1 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.tests.ps1 @@ -0,0 +1,52 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-DevOpsSecurityDescriptor Tests' -Tags "Unit", "API" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-DevOpsSecurityDescriptor.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ value = 'MockedResponse' } + } + + $ProjectId = 'TestProjectId' + $Organization = 'TestOrganization' + $ApiVersion = '6.0' + + } + + It 'should retrieve the security descriptor for a project' { + $response = Get-DevOpsSecurityDescriptor -ProjectId $ProjectId -Organization $Organization -ApiVersion $ApiVersion + $response | Should -Be 'MockedResponse' + } + + It 'should call Invoke-AzDevOpsApiRestMethod once' { + Get-DevOpsSecurityDescriptor -ProjectId $ProjectId -Organization $Organization -ApiVersion $ApiVersion + Assert-MockCalled -CommandName 'Invoke-AzDevOpsApiRestMethod' -Exactly 1 -Scope It + } + + It 'should call Invoke-AzDevOpsApiRestMethod with correct parameters' { + Get-DevOpsSecurityDescriptor -ProjectId $ProjectId -Organization $Organization -ApiVersion $ApiVersion + Assert-MockCalled -CommandName 'Invoke-AzDevOpsApiRestMethod' -Exactly 1 -Scope It -ParameterFilter { + $ApiUri -eq "https://vssps.dev.azure.com/TestOrganization/_apis/graph/descriptors/TestProjectId?api-version=6.0" -and + $Method -eq 'GET' + } + } + + It 'should handle errors gracefully' { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { throw "API Error" } + Mock -CommandName Write-Error -Verifiable + + { Get-DevOpsSecurityDescriptor -ProjectId $ProjectId -Organization $Organization -ApiVersion $ApiVersion } | Should -Not -Throw + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.tests.ps1 new file mode 100644 index 000000000..4114161b3 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.tests.ps1 @@ -0,0 +1,87 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Add-AuthenticationHTTPHeader" -Tags "Unit", "Authentication" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Add-AuthenticationHTTPHeader.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Update-AzManagedIdentity + + } + + BeforeEach { + # Reset the global variables before each test + $Global:DSCAZDO_AuthenticationToken = $null + $Global:DSCAZDO_OrganizationName = "TestOrg" + } + + It "Throws an error when the token is null" { + $Global:DSCAZDO_AuthenticationToken = @{ + tokenType = $null + } + { Add-AuthenticationHTTPHeader } | Should -Throw '*The authentication token is null*' + } + + It "Returns header for PersonalAccessToken" { + $Global:DSCAZDO_AuthenticationToken = [PSCustomObject]@{ + tokenType = 'PersonalAccessToken' + } + $Global:DSCAZDO_AuthenticationToken | Add-Member -MemberType ScriptMethod -Name Get -Value { return "dummyPAT" } + + $result = Add-AuthenticationHTTPHeader + $result | Should -Be "Authorization: Basic dummyPAT" + } + + It "Returns header for ManagedIdentity when token is not expired" { + $Global:DSCAZDO_AuthenticationToken = @{ + tokenType = 'ManagedIdentity' + } + $Global:DSCAZDO_AuthenticationToken | Add-Member -MemberType ScriptMethod -Name Get -Value { return "dummyPAT" } + $Global:DSCAZDO_AuthenticationToken | Add-Member -MemberType ScriptMethod -Name isExpired -Value { return $false } + + $result = Add-AuthenticationHTTPHeader + $result | Should -Be "Bearer dummyPAT" + } + + It "Updates and returns header for ManagedIdentity when token is expired" { + $Global:DSCAZDO_AuthenticationToken = @{ + tokenType = 'ManagedIdentity' + } + $Global:DSCAZDO_AuthenticationToken | Add-Member -MemberType ScriptMethod -Name Get -Value { return "dummyPAT" } + $Global:DSCAZDO_AuthenticationToken | Add-Member -MemberType ScriptMethod -Name isExpired -Value { return $true } + + + # Mock Update-AzManagedIdentity cmdlet + Mock -CommandName Update-AzManagedIdentity -MockWith { + $obj = [PSCustomObject]@{ + tokenType = 'ManagedIdentity' + } + $obj | Add-Member -MemberType ScriptMethod -Name Get -Value { return "newMIToken" } + $obj | Add-Member -MemberType ScriptMethod -Name isExpired -Value { return $false } + + return $obj + } + + $result = Add-AuthenticationHTTPHeader + $result | Should -Be "Bearer newMIToken" + } + + It "Throws an error for unsupported token type" { + $Global:DSCAZDO_AuthenticationToken = @{ + tokenType = 'UnsupportedToken' + Get = { return "dummyToken" } + } + { Add-AuthenticationHTTPHeader } | Should -Throw '*The authentication token type is not supported*' + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.tests.ps1 new file mode 100644 index 000000000..f18fb4007 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.tests.ps1 @@ -0,0 +1,95 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Get-AzManagedIdentityToken Tests" -Tags "Unit", "Authentication" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-AzManagedIdentityToken.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + # Import the enums + Import-Enums | ForEach-Object { + . $_.FullName + } + + # Import the classes + . (Get-ClassFilePath '001.AuthenticationToken') + . (Get-ClassFilePath '002.PersonalAccessToken') + . (Get-ClassFilePath '003.ManagedIdentityToken') + + + Mock -CommandName Test-AzDevOpsApiHttpRequestHeader -MockWith { + return $true + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { + return "6.0-preview.1" + } + + Mock -CommandName New-ManagedIdentityToken -MockWith { + return @{ + AccessToken = "fake-access-token" + Expiry = (Get-Date).AddHours(1) + } + } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ + access_token = "fake-access-token" + } + } + + Mock -CommandName Test-AzToken -MockWith { return $true } + + } + + Context "When Verify switch is not set" { + It "should return managed identity token" { + $result = Get-AzManagedIdentityToken -OrganizationName "Contoso" + $result.AccessToken | Should -Be "fake-access-token" + } + } + + Context "When Verify switch is set" { + It "should return managed identity token after verification" { + $result = Get-AzManagedIdentityToken -OrganizationName "Contoso" -Verify + $result.AccessToken | Should -Be "fake-access-token" + } + + It "should throw error if Token verification fails" { + Mock -CommandName Test-AzToken -MockWith { + return $false + } -ParameterFilter { + $true + } + + { + Get-AzManagedIdentityToken -OrganizationName "Contoso" -Verify + } | Should -Throw "Error. Failed to call the Azure DevOps API." + } + } + + Context "When access token is not returned from Azure Instance Metadata Service" { + + BeforeAll { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + Throw "MOCK ERROR" + } + } + + It "should throw error" { + { + Get-AzManagedIdentityToken -OrganizationName "Contoso" + } | Should -Throw + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.tests.ps1 new file mode 100644 index 000000000..97c07e131 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.tests.ps1 @@ -0,0 +1,65 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Update-AzManagedIdentity" -Tags "Unit", "Authentication" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Update-AzManagedIdentity.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + # Import the enums + Import-Enums | ForEach-Object { + . $_.FullName + } + + # Import the classes + . (Get-ClassFilePath '001.AuthenticationToken') + . (Get-ClassFilePath '002.PersonalAccessToken') + . (Get-ClassFilePath '003.ManagedIdentityToken') + + + Mock -CommandName Get-AzManagedIdentityToken -MockWith {} + + } + + Context "When the Global Organization Name is not set" { + It "Throws an error" { + $Global:DSCAZDO_OrganizationName = $null + { Update-AzManagedIdentity } | Should -Throw '*Organization Name is not set*' + } + } + + Context "When the Global Organization Name is set" { + BeforeEach { + $Global:DSCAZDO_OrganizationName = "Contoso" + $Global:DSCAZDO_AuthenticationToken = "oldToken" + } + + It "Clears the existing token" { + Update-AzManagedIdentity + $Global:DSCAZDO_AuthenticationToken | Should -BeNullOrEmpty + } + + It "Calls Get-AzManagedIdentityToken with the correct organization name" { + Mock -CommandName Get-AzManagedIdentityToken -MockWith { return "newToken" } + + Update-AzManagedIdentity + Assert-MockCalled -CommandName Get-AzManagedIdentityToken -Times 1 -Exactly -ParameterFilter { $OrganizationName -eq "Contoso" } + } + + It "Sets the Global Authentication Token to the new token" { + Mock -CommandName Get-AzManagedIdentityToken -MockWith { return "newToken" } + + Update-AzManagedIdentity + $Global:DSCAZDO_AuthenticationToken | Should -Be "newToken" + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.tests.ps1 new file mode 100644 index 000000000..d33a7d349 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.tests.ps1 @@ -0,0 +1,109 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Set-AzPersonalAccessToken" -Tags "Unit", "Authentication" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-AzPersonalAccessToken.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + # Import the enums + Import-Enums | ForEach-Object { + . $_.FullName + } + + # Import the classes + . (Get-ClassFilePath '001.AuthenticationToken') + . (Get-ClassFilePath '002.PersonalAccessToken') + . (Get-ClassFilePath '003.ManagedIdentityToken') + + Mock -CommandName New-PersonalAccessToken -MockWith { return } + Mock -CommandName Test-AzToken -MockWith { return $true } + + } + + Context "with PersonalAccessToken parameter set" { + It "should call New-PersonalAccessToken with the correct arguments" { + $token = "testToken" + $orgName = "testOrg" + + Set-AzPersonalAccessToken -OrganizationName $orgName -PersonalAccessToken $token + + Assert-MockCalled -CommandName New-PersonalAccessToken -Exactly 1 -ParameterFilter { + $PersonalAccessToken -eq $token + } + } + + It "should return the token when Verify switch is not set" { + $token = "testToken" + Mock -CommandName New-PersonalAccessToken -MockWith { return $token } + + $result = Set-AzPersonalAccessToken -OrganizationName "testOrg" -PersonalAccessToken $token + $result | Should -Be $token + } + + It "should verify the connection when Verify switch is set" { + $pattoken = "testToken" + $orgName = "testOrg" + + Mock -CommandName Test-AzToken -MockWith { return $true } + Mock -CommandName New-PersonalAccessToken -MockWith { return 'testToken' } + + $result = Set-AzPersonalAccessToken -OrganizationName $orgName -PersonalAccessToken $pattoken -Verify + + Assert-MockCalled -CommandName Test-AzToken -Exactly 1 -ParameterFilter { + $pattoken -eq $token + } + $result | Should -Be $pattoken + } + } + + Context "with SecureStringPersonalAccessToken parameter set" { + It "should call New-PersonalAccessToken with the correct arguments" { + + Mock -CommandName Test-AzToken -MockWith { return $true } + Mock -CommandName New-PersonalAccessToken -MockWith { + return (ConvertTo-SecureString -String "secureTestToken" -AsPlainText -Force) + } + + $secureToken = ConvertTo-SecureString -String "secureTestToken" -AsPlainText -Force + $orgName = "testOrg" + + $result = Set-AzPersonalAccessToken -OrganizationName $orgName -SecureStringPersonalAccessToken $secureToken + Assert-MockCalled -CommandName New-PersonalAccessToken -Exactly 1 -ParameterFilter { + $SecureStringPersonalAccessToken -ne $null + } + $result | Should -BeOfType [SecureString] + + } + + It "should verify the connection when Verify switch is set" { + $secureToken = ConvertTo-SecureString -String "secureTestToken" -AsPlainText -Force + $orgName = "testOrg" + + Mock -CommandName Test-AzToken -MockWith { return $true } + Mock -CommandName New-PersonalAccessToken -MockWith { + return (ConvertTo-SecureString -String "secureTestToken" -AsPlainText -Force) + } + + $result = Set-AzPersonalAccessToken -OrganizationName $orgName -SecureStringPersonalAccessToken $secureToken -Verify + + Assert-MockCalled -CommandName New-PersonalAccessToken -Exactly 1 -ParameterFilter { + $SecureStringPersonalAccessToken -ne $null + } + + Assert-MockCalled -CommandName Test-AzToken -Exactly 1 + + $result | Should -BeOfType [SecureString] + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.tests.ps1 new file mode 100644 index 000000000..dbbe19b93 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.tests.ps1 @@ -0,0 +1,65 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Test-AzToken' -Tags "Unit", "Authentication" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-AzToken.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Test-AzDevOpsApiHttpRequestHeader -MockWith { + return $true + } + + $GLOBAL:DSCAZDO_OrganizationName = "TestOrg" + + } + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + Context 'When token is valid' { + It 'Should return true' { + # Mocking the Managed Identity token object + $mockToken = [PSCustomObject]@{} + $mockToken | Add-Member -MemberType ScriptMethod -Name Get -Value { + return "valid_token" + } + + # Mocking the Invoke-AzDevOpsApiRestMethod cmdlet + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return "valid_token" + } + + $result = Test-AzToken -Token $mockToken + $result | Should -Be $true + } + } + + Context 'When token is invalid' { + It 'Should return false' { + # Mocking the Managed Identity token object + $mockToken = [PSCustomObject]@{} + $mockToken | Add-Member -MemberType ScriptMethod -Name Get -Value { + return "invalid_token" + } + + # Mocking the Invoke-AzDevOpsApiRestMethod cmdlet to throw an exception + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + throw "Unauthorized" + } + + $result = Test-AzToken -Token $mockToken + $result | Should -Be $false + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.tests.ps1 new file mode 100644 index 000000000..2ab4399e3 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.tests.ps1 @@ -0,0 +1,129 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Add-CacheItem" -Tags "Unit", "Cache" { + + AfterAll { + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + } + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Add-CacheItem.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + # Mock dependencies + Mock -CommandName Get-CacheObject -MockWith { return @() } + + } + + Context "when adding a new cache item" { + + BeforeEach { + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + } + + It "should retrieve the current cache" { + Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project' + + Assert-MockCalled -CommandName Get-CacheObject -Exactly 1 -ParameterFilter { + $CacheType -eq 'Project' + } + } + + It "should create a new cache if the current cache is empty" { + Mock -CommandName Get-CacheObject -MockWith { return @() } + Mock -CommandName Write-Verbose + Mock -CommandName Set-Variable -Verifiable + + Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project' + + Assert-MockCalled -CommandName Write-Verbose -Exactly 1 -ParameterFilter { + $Message -eq '[Add-CacheItem] Cache is empty. Creating new cache.' + } + + } + + It "should add a new cache item with the correct key and value" { + Mock -CommandName Get-CacheObject -MockWith { return @() } + Mock -CommandName Write-Verbose + + Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project' + + $cache = Get-Variable -Name "AzDoProject" -Scope Global -ValueOnly + $cache[0].Key | Should -Be 'MyKey' + $cache[0].Value | Should -Be 'MyValue' + } + } + + Context "when the cache already contains the key" { + + BeforeEach { + Mock -CommandName Get-CacheObject -MockWith { + return $Global:AzDoProject + } + } + + AfterEach { + Remove-Variable -Name "AzDoProject" -Scope Global + } + + It "should remove the existing cache item" { + Mock -CommandName Write-Verbose + Mock -CommandName Write-Warning + Mock -CommandName Remove-CacheItem -Verifiable -MockWith { + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + } + + Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project' + Add-CacheItem -Key 'MyKey' -Value 'NewValue' -Type 'Project' + + $cache = Get-Variable -Name "AzDoProject" -Scope Global -ValueOnly + $cache[0].Key | Should -Be 'MyKey' + $cache[0].Value | Should -Be 'NewValue' + + } + + It "should add the new cache item after removing the old one" { + Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project' + + $cache = Get-Variable -Name "AzDoProject" -Scope Global -ValueOnly + $cache[0].Key | Should -Be 'MyKey' + $cache[0].Value | Should -Be 'MyValue' + } + + It "should suppress the warning if SuppressWarning switch is present" { + Mock -CommandName Write-Warning + Mock -CommandName Write-Verbose + + Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project' -SuppressWarning + Add-CacheItem -Key 'MyKey' -Value 'NewValue' -Type 'Project' -SuppressWarning + + Assert-MockCalled -CommandName Write-Warning -Exactly 0 + Assert-MockCalled -CommandName Write-Verbose -Times 3 + + } + + It "should display a warning if SuppressWarning switch is not present" { + Mock -CommandName Write-Warning + + Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project' + Add-CacheItem -Key 'MyKey' -Value 'NewValue' -Type 'Project' + + Assert-MockCalled -CommandName Write-Warning -Exactly 1 + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.tests.ps1 new file mode 100644 index 000000000..f6fbbebc9 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.tests.ps1 @@ -0,0 +1,108 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'AzDoAPI_0_ProjectCache' -Tags "Unit", "Cache" { + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath '0.ProjectCache.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName List-DevOpsProjects -MockWith { + param ($Organization) + return @( + [pscustomobject]@{ Id = 1; Name = 'Project1' }, + [pscustomobject]@{ Id = 2; Name = 'Project2' } + ) + } + + Mock -CommandName Get-DevOpsSecurityDescriptor -MockWith { + param ($ProjectId, $Organization) + return "SecurityDescriptor for Project $ProjectId" + } + + Mock -CommandName Add-CacheItem -MockWith { + param ($Key, $Value, $Type) + } + + Mock -CommandName Export-CacheObject -MockWith { + param ($CacheType, $Content) + } + + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return '6.0' } + + } + + Context 'When OrganizationName is provided' { + + It 'should call List-DevOpsProjects with the correct parameters' { + AzDoAPI_0_ProjectCache -OrganizationName 'MyOrganization' + + Assert-MockCalled List-DevOpsProjects -Exactly -Times 1 + } + + It 'should add projects to the cache' { + AzDoAPI_0_ProjectCache -OrganizationName 'MyOrganization' + + Assert-MockCalled Add-CacheItem -Exactly -Times 2 -ParameterFilter { + ($Key -eq 'Project1' -and $Value.Name -eq 'Project1') -or + ($Key -eq 'Project2' -and $Value.Name -eq 'Project2') + } + } + + It 'should export the cache' { + AzDoAPI_0_ProjectCache -OrganizationName 'MyOrganization' + + Assert-MockCalled Export-CacheObject -Exactly -Times 1 -ParameterFilter { + ($CacheType -eq 'LiveProjects') -and + ($Content -eq $global:AzDoLiveProjects) + } + } + } + + Context 'When OrganizationName is not provided' { + + Mock -CommandName Write-Verbose -MockWith { + param ($Message) + } + + BeforeAll { + $Global:DSCAZDO_OrganizationName = 'GlobalOrganization' + } + + It 'should use the global variable for organization name' { + AzDoAPI_0_ProjectCache + + Assert-MockCalled List-DevOpsProjects -Exactly -Times 1 + } + } + + Context 'Error handling' { + + It 'should handle errors during API call' { + Mock -CommandName List-DevOpsProjects -MockWith { throw "API Error" } + Mock -CommandName Write-Error -Verifiable + + { AzDoAPI_0_ProjectCache -OrganizationName 'MyOrganization' } | Should -Not -Throw + Assert-VerifiableMock + } + + It 'should handle errors during cache export' { + Mock -CommandName Export-CacheObject -MockWith { throw "Export failed" } + Mock -CommandName Write-Error -Verifiable + + { AzDoAPI_0_ProjectCache -OrganizationName 'MyOrganization' } | Should -Not -Throw + Assert-VerifiableMock + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.tests.ps1 new file mode 100644 index 000000000..4022b499f --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.tests.ps1 @@ -0,0 +1,105 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'AzDoAPI_1_GroupCache' -Tags "Unit", "Cache" { + + AfterAll { + Remove-Variable -Name "AzDoProject" -Scope Global -ErrorAction SilentlyContinue + Remove-Variable -Name "AzDoLiveGroups" -Scope Global -ErrorAction SilentlyContinue + } + + BeforeAll { + + # Set the Organization + $null = Remove-Variable -Name "DSCAZDO_OrganizationName" -Scope Global -ErrorAction SilentlyContinue + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath '1.GroupCache.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName List-DevOpsGroups -MockWith { + return @( + [pscustomobject]@{ PrincipalName = 'Group1'; Id = 1 }, + [pscustomobject]@{ PrincipalName = 'Group2'; Id = 2 } + ) + } + + Mock -CommandName Add-CacheItem + Mock -CommandName Export-CacheObject + + } + + Context 'When OrganizationName is provided' { + + It 'should call List-DevOpsGroups with the correct parameters' { + AzDoAPI_1_GroupCache -OrganizationName 'MyOrganization' + + Assert-MockCalled List-DevOpsGroups -Exactly -Times 1 -Scope It -ParameterFilter { + $Organization -eq 'MyOrganization' + } + } + + It 'should add groups to the cache' { + AzDoAPI_1_GroupCache -OrganizationName 'MyOrganization' + + Assert-MockCalled Add-CacheItem -Exactly -Times 2 -Scope It -ParameterFilter { + ($Key -eq 'Group1' -and $Value.PrincipalName -eq 'Group1') -or + ($Key -eq 'Group2' -and $Value.PrincipalName -eq 'Group2') + } + } + + It 'should export the cache' { + AzDoAPI_1_GroupCache -OrganizationName 'MyOrganization' + + Assert-MockCalled Export-CacheObject -Exactly -Times 1 -Scope It -ParameterFilter { + $CacheType -eq 'LiveGroups' + } + } + } + + Context 'When OrganizationName is not provided' { + + Mock -CommandName Write-Verbose -MockWith { + param ($Message) + } + + BeforeAll { + $Global:DSCAZDO_OrganizationName = 'GlobalOrganization' + } + + It 'should use the global variable for organization name' { + AzDoAPI_1_GroupCache + + Assert-MockCalled List-DevOpsGroups -Exactly -Times 1 -Scope It -ParameterFilter { + $Organization -eq 'GlobalOrganization' + } + } + } + + Context 'Error handling' { + + It 'should handle errors during API call' { + Mock -CommandName List-DevOpsGroups -MockWith { throw "API Error" } + Mock -CommandName Write-Error -Verifiable + + { AzDoAPI_1_GroupCache -OrganizationName 'MyOrganization' } | Should -Not -Throw + Assert-VerifiableMock + } + + It 'should handle errors during cache export' { + Mock -CommandName Export-CacheObject -MockWith { throw "Export failed" } + Mock -CommandName Write-Error -Verifiable + + { AzDoAPI_1_GroupCache -OrganizationName 'MyOrganization' } | Should -Not -Throw + Assert-VerifiableMock + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.tests.ps1 new file mode 100644 index 000000000..27dc50814 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.tests.ps1 @@ -0,0 +1,84 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'AzDoAPI_2_UserCache' -Tags "Unit", "Cache" { + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath '2.UserCache.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + + Mock -CommandName List-UserCache -MockWith { + return @( + [PSCustomObject]@{ PrincipalName = 'user1@example.com' }, + [PSCustomObject]@{ PrincipalName = 'user2@example.com' } + ) + } + + Mock -CommandName Add-CacheItem + Mock -CommandName Export-CacheObject + + } + + Context 'when organization name parameter is provided' { + + It 'should call List-UserCache with correct parameters' { + AzDoAPI_2_UserCache -OrganizationName 'TestOrg' + Assert-MockCalled -CommandName List-UserCache -Exactly -Times 1 + } + + It 'should add users to cache' { + AzDoAPI_2_UserCache -OrganizationName 'TestOrg' + + Assert-MockCalled -CommandName Add-CacheItem -Exactly -Times 2 + Assert-MockCalled -CommandName Add-CacheItem -ParameterFilter { + $Key -eq 'user1@example.com' + } + Assert-MockCalled -CommandName Add-CacheItem -ParameterFilter { + $Key -eq 'user2@example.com' + } + } + + It 'should export the cache' { + AzDoAPI_2_UserCache -OrganizationName 'TestOrg' + + Assert-MockCalled -CommandName Export-CacheObject -Exactly -Times 1 + } + } + + Context 'when organization name parameter is not provided' { + + BeforeEach { + $Global:DSCAZDO_OrganizationName = 'GlobalTestOrg' + } + + It 'should use the global organization name' { + AzDoAPI_2_UserCache + Assert-MockCalled -CommandName List-UserCache -Exactly -Times 1 + } + } + + Context 'when an error occurs' { + + BeforeAll { + Mock -CommandName Write-Error -Verifiable + Mock -CommandName List-UserCache -MockWith { throw "API Error" } + } + + It 'should catch and handle the error' { + { AzDoAPI_2_UserCache -OrganizationName 'TestOrg' } | Should -Not -Throw + Assert-VerifiableMock + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.tests.ps1 new file mode 100644 index 000000000..a922156e6 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.tests.ps1 @@ -0,0 +1,99 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'AzDoAPI_3_GroupMemberCache' -Tags "Unit", "Cache" { + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath '3.GroupMemberCache.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-CacheObject -MockWith { + if ($CacheType -eq 'LiveGroups') + { + return @( + [PSCustomObject]@{ Key = 'Group1'; Value = [PSCustomObject]@{ descriptor = 'desc1'; PrincipalName = 'Group1' } }, + [PSCustomObject]@{ Key = 'Group2'; Value = [PSCustomObject]@{ descriptor = 'desc2'; PrincipalName = 'Group2' } } + ) + } elseif ($CacheType -eq 'LiveUsers') { + return @( + [PSCustomObject]@{ descriptor = 'desc1'; PrincipalName = 'user1@example.com' }, + [PSCustomObject]@{ descriptor = 'desc2'; PrincipalName = 'user2@example.com' } + ) + } + } + + Mock -CommandName List-DevOpsGroupMembers -MockWith { + param ( + [string]$Organization, + [string]$GroupDescriptor + ) + return [PSCustomObject]@{ memberDescriptor = @('desc1', 'desc2') } + } + + Mock -CommandName Add-CacheItem -MockWith {} + Mock -CommandName Export-CacheObject -MockWith {} + + } + + Context 'when organization name parameter is provided' { + + It 'should call List-DevOpsGroupMembers with correct parameters' { + AzDoAPI_3_GroupMemberCache -OrganizationName 'TestOrg' + + Assert-MockCalled -CommandName List-DevOpsGroupMembers -Exactly -Times 2 -Scope It -ParameterFilter { + $Organization -eq 'TestOrg' + } + } + + It 'should add group members to cache' { + AzDoAPI_3_GroupMemberCache -OrganizationName 'TestOrg' + + Assert-MockCalled -CommandName Add-CacheItem -Exactly -Times 2 + } + + It 'should export the cache' { + AzDoAPI_3_GroupMemberCache -OrganizationName 'TestOrg' + + Assert-MockCalled -CommandName Export-CacheObject -Exactly -Times 1 + } + } + + Context 'when organization name parameter is not provided' { + + BeforeEach { + $Global:DSCAZDO_OrganizationName = 'GlobalTestOrg' + } + + It 'should use the global organization name' { + AzDoAPI_3_GroupMemberCache + + Assert-MockCalled -CommandName List-DevOpsGroupMembers -Exactly -Times 2 -ParameterFilter { + $Organization -eq 'GlobalTestOrg' + } + } + } + + Context 'when an error occurs' { + + BeforeAll { + Mock -CommandName List-DevOpsGroupMembers -MockWith { throw "API Error" } + Mock -CommandName Write-Error -Verifiable + } + + It 'should catch and handle the error' { + { AzDoAPI_3_GroupMemberCache -OrganizationName 'TestOrg' } | Should -Not -Throw + Assert-VerifiableMock + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.tests.ps1 new file mode 100644 index 000000000..b52aa6b40 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.tests.ps1 @@ -0,0 +1,109 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "AzDoAPI_4_GitRepositoryCache Tests" -Tags "Unit", "Cache" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath '4.GitRepositoryCache.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-CacheObject + Mock -CommandName List-DevOpsGitRepository + Mock -CommandName Add-CacheItem + Mock -CommandName Export-CacheObject + + } + + Context "When \ is passed" { + It "Should call Get-CacheObject with LiveProjects" { + AzDoAPI_4_GitRepositoryCache -OrganizationName "TestOrg" + Assert-MockCalled -CommandName Get-CacheObject -Exactly -Times 1 -ParameterFilter { $CacheType -eq 'LiveProjects' } + } + + It "Should call List-DevOpsGitRepository for each project" { + $mockProjects = @( + [PSCustomObject]@{ Value = @{ Name = "TestProject1" } }, + [PSCustomObject]@{ Value = @{ Name = "TestProject2" } } + ) + Mock -CommandName Get-CacheObject -MockWith { $mockProjects } + AzDoAPI_4_GitRepositoryCache -OrganizationName "TestOrg" + Assert-MockCalled -CommandName List-DevOpsGitRepository -Exactly -Times 2 + } + + It "Should call Add-CacheItem for each repository" { + $mockProjects = @( + [PSCustomObject]@{ Value = @{ Name = "TestProject1" } }, + [PSCustomObject]@{ Value = @{ Name = "TestProject2" } } + ) + $mockRepos = @( + [PSCustomObject]@{ Name = "Repo1" }, + [PSCustomObject]@{ Name = "Repo2" } + ) + Mock -CommandName Get-CacheObject -MockWith { $mockProjects } + Mock -CommandName List-DevOpsGitRepository -MockWith { $mockRepos } + AzDoAPI_4_GitRepositoryCache -OrganizationName "TestOrg" + Assert-MockCalled -CommandName Add-CacheItem -Exactly -Times 4 + } + + It "Should call Export-CacheObject once" { + AzDoAPI_4_GitRepositoryCache -OrganizationName "TestOrg" + Assert-MockCalled -CommandName Export-CacheObject -Exactly -Times 1 + } + + It "Should log verbose messages" { + $ProgressPreference='SilentlyContinue' + Mock -CommandName Write-Verbose -Verifiable + AzDoAPI_4_GitRepositoryCache -OrganizationName "TestOrg" -Verbose + Assert-VerifiableMock + } + + It "Should catch and log errors" { + + $mockProjects = @( + [PSCustomObject]@{ Value = @{ Name = "TestProject1" } }, + [PSCustomObject]@{ Value = @{ Name = "TestProject2" } } + ) + + Mock -CommandName Write-Error -Verifiable + Mock -CommandName List-DevOpsGitRepository -MockWith { throw "Mocked Error" } + Mock -CommandName Get-CacheObject -MockWith { $mockProjects } + + { AzDoAPI_4_GitRepositoryCache -OrganizationName "TestOrg" } | Should -Not -Throw + + Assert-VerifiableMock + } + + } + + Context "When \ is not passed" { + BeforeAll { $Global:DSCAZDO_OrganizationName = "GlobalOrg" } + + It "Should use global variable for organization name" { + $mockProjects = @( + [PSCustomObject]@{ Value = @{ Name = "TestProject1" } }, + [PSCustomObject]@{ Value = @{ Name = "TestProject2" } } + ) + $mockRepos = @( + [PSCustomObject]@{ Name = "Repo1" }, + [PSCustomObject]@{ Name = "Repo2" } + ) + + Mock -CommandName Get-CacheObject -MockWith { $mockProjects } + Mock -CommandName List-DevOpsGitRepository -MockWith { $mockRepos } + + AzDoAPI_4_GitRepositoryCache + Assert-MockCalled -CommandName List-DevOpsGitRepository -Times 1 -ParameterFilter { $OrganizationName -eq "GlobalOrg" } + + } + + AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.tests.ps1 new file mode 100644 index 000000000..829e13091 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.tests.ps1 @@ -0,0 +1,55 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'AzDoAPI_5_PermissionsCache Tests' -Tags "Unit", "Cache" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath '5.PermissionsCache.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName List-DevOpsSecurityNamespaces -MockWith { + return @( + [PSCustomObject]@{ name = 'Namespace1'; namespaceId = 1; writePermission = $true; readPermission = $true; dataspaceCategory = 'category1'; actions = @('Action1','Action2') }, + [PSCustomObject]@{ name = 'Namespace2'; namespaceId = 2; writePermission = $false; readPermission = $true; dataspaceCategory = 'category2'; actions = @('Action3','Action4') } + ) + } + + Mock -CommandName Add-CacheItem + Mock -CommandName Export-CacheObject + + $global:DSCAZDO_OrganizationName = "DefaultOrg" + + } + + It 'Uses provided OrganizationName parameter' { + AzDoAPI_5_PermissionsCache -OrganizationName 'TestOrg' + Assert-VerifiableMock + } + + It 'Uses global OrganizationName when parameter is not provided' { + AzDoAPI_5_PermissionsCache + Assert-MockCalled -CommandName List-DevOpsSecurityNamespaces -ParameterFilter { $OrganizationName -eq $global:DSCAZDO_OrganizationName } + } + + It 'Adds each security namespace to cache correctly' { + AzDoAPI_5_PermissionsCache -OrganizationName 'TestOrg' + + Assert-MockCalled -CommandName Add-CacheItem -Times 2 + Assert-MockCalled -CommandName Add-CacheItem -ParameterFilter { $Key -eq 'Namespace1' -and $Type -eq 'SecurityNamespaces' } + Assert-MockCalled -CommandName Add-CacheItem -ParameterFilter { $Key -eq 'Namespace2' -and $Type -eq 'SecurityNamespaces' } + } + + It 'Exports cache correctly' { + AzDoAPI_5_PermissionsCache -OrganizationName 'TestOrg' + + Assert-MockCalled -CommandName Export-CacheObject -ParameterFilter { $CacheType -eq 'SecurityNameSpaces' -and $Depth -eq 5 } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.tests.ps1 new file mode 100644 index 000000000..54cee242f --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.tests.ps1 @@ -0,0 +1,74 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'AzDoAPI_6_ServicePrinciple' -Tags "Unit", "Cache" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath '6.ServicePrinciple.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName List-DevOpsServicePrinciples -MockWith { + return @([PSCustomObject]@{ displayName = 'SP1' }, [PSCustomObject]@{ displayName = 'SP2' }) + } + Mock -CommandName Add-CacheItem + Mock -CommandName Export-CacheObject + + $Global:DSCAZDO_OrganizationName = 'MyOrganization' + } + + It 'should call List-DevOpsServicePrinciples with provided organization name' { + AzDoAPI_6_ServicePrinciple -OrganizationName 'TestOrgName' + + Assert-MockCalled -CommandName List-DevOpsServicePrinciples -Exactly 1 -ParameterFilter { + $OrganizationName -eq 'TestOrgName' + } + } + + It 'should call List-DevOpsServicePrinciples with global organization name if none provided' { + AzDoAPI_6_ServicePrinciple + + Assert-MockCalled -CommandName List-DevOpsServicePrinciples -Exactly 1 -ParameterFilter { + $OrganizationName -eq $Global:DSCAZDO_OrganizationName + } + } + + It 'should add returned service principals to cache' { + AzDoAPI_6_ServicePrinciple -OrganizationName 'TestOrgName' + + Assert-MockCalled -CommandName Add-CacheItem -Exactly 2 + Assert-MockCalled -CommandName Add-CacheItem -ParameterFilter { + $Key -eq 'SP1' -and + $Value.displayName -eq 'SP1' -and + $Type -eq 'LiveServicePrinciples' + } + Assert-MockCalled -CommandName Add-CacheItem -ParameterFilter { + $Key -eq 'SP2' -and + $Value.displayName -eq 'SP2' -and + $Type -eq 'LiveServicePrinciples' + } + } + + It 'should export cache object after adding service principals' { + AzDoAPI_6_ServicePrinciple -OrganizationName 'TestOrgName' + + Assert-MockCalled -CommandName Export-CacheObject -Exactly 1 -ParameterFilter { $CacheType -eq 'LiveServicePrinciples' } + } + + It 'should write an error message if an exception occurs' { + Mock -CommandName List-DevOpsServicePrinciples -MockWith { throw 'API Error' } + Mock -CommandName Write-Error -Verifiable + + { AzDoAPI_6_ServicePrinciple -OrganizationName 'TestOrgName' } | Should -Not -Throw + + Assert-MockCalled -CommandName Write-Error -Exactly 1 + Assert-VerifiableMock + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.tests.ps1 new file mode 100644 index 000000000..f5e1e3160 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.tests.ps1 @@ -0,0 +1,144 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "AzDoAPI_7_IdentitySubjectDescriptors" -Tags "Unit", "Cache" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath '7.IdentitySubjectDescriptors.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDoCacheObjects -MockWith { + @('LiveGroups', 'LiveUsers', 'LiveServicePrinciples') + } + + Mock -CommandName Get-CacheObject -MockWith { + + switch ($CacheType) + { + 'LiveGroups' { + return ( + [PSCustomObject]@{ + Key = 'mockKey' + Value = @{ + descriptor = 'mockDescriptorGroup' + } + } + ) + } + 'LiveUsers' { + return ( + [PSCustomObject]@{ + Key = 'mockKey' + Value = @{ + descriptor = 'mockDescriptorUser' + } + } + ) + } + 'LiveServicePrinciples' { + return ( + [PSCustomObject]@{ + Key = 'mockKey' + Value = @{ + descriptor = 'mockDescriptorServicePrinciple' + } + } + ) + } + } + } + + Mock -CommandName Get-DevOpsDescriptorIdentity -MockWith { + @{ + id = 'mockId' + descriptor = 'mockDescriptor' + subjectDescriptor = 'mockSubjectDescriptor' + providerDisplayName = 'mockProvider' + isActive = $true + isContainer = $false + } + } + + Mock -CommandName Add-CacheItem + Mock -CommandName Export-CacheObject + + } + + BeforeEach { + $Global:DSCAZDO_OrganizationName = 'mockOrg' + } + + It "should use a global variable if OrganizationName parameter is not provided" { + + + $result = AzDoAPI_7_IdentitySubjectDescriptors + + Assert-MockCalled -CommandName Get-CacheObject + Assert-MockCalled -CommandName Get-DevOpsDescriptorIdentity + Assert-MockCalled -CommandName Add-CacheItem + Assert-MockCalled -CommandName Export-CacheObject + } + + It "should use OrganizationName parameter if provided" { + $result = AzDoAPI_7_IdentitySubjectDescriptors -OrganizationName 'testOrg' + + Assert-MockCalled -CommandName Get-CacheObject + Assert-MockCalled -CommandName Get-DevOpsDescriptorIdentity + Assert-MockCalled -CommandName Add-CacheItem + Assert-MockCalled -CommandName Export-CacheObject + } + + It "should call Get-CacheObject for each cache type" { + $result = AzDoAPI_7_IdentitySubjectDescriptors -OrganizationName 'testOrg' + Assert-MockCalled -CommandName Get-CacheObject -Times 3 + } + + It "should add members to each cache object" { + + Mock -CommandName Get-DevOpsDescriptorIdentity -MockWith { + @{ + id = 'mockId' + descriptor = 'mockDescriptor' + subjectDescriptor = 'mockSubjectDescriptor' + providerDisplayName = 'mockProvider' + isActive = $true + isContainer = $false + } + } + + $mockGroup = @{ + Key = 'mockKey' + Value = [PSCustomObject]@{ + descriptor = 'mockDescriptorGroup' + } + } + + $result = AzDoAPI_7_IdentitySubjectDescriptors -OrganizationName 'testOrg' + + Assert-MockCalled -CommandName Get-DevOpsDescriptorIdentity -ParameterFilter { + $SubjectDescriptor -eq 'mockDescriptorGroup' + } + + $cacheItemArgs = @{ + Key = 'mockKey' + Value = $mockGroup + Type = 'LiveGroups' + SuppressWarning = $true + } + + Assert-MockCalled -CommandName Add-CacheItem -Times 1 -ParameterFilter { + $Key -eq $cacheItemArgs.Key -and + $Type -eq $cacheItemArgs.Type -and + $SuppressWarning -eq $cacheItemArgs.SuppressWarning + } + + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.tests.ps1 new file mode 100644 index 000000000..40ee59a23 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.tests.ps1 @@ -0,0 +1,79 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'AzDoAPI_8_ProjectProcessTemplates' -Tags "Unit", "Cache" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath '8.ProcessTemplates.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + $mockOrganizationName = 'TestOrg' + $mockProcesses = @( + [PSCustomObject]@{ name = 'Process1' }, + [PSCustomObject]@{ name = 'Process2' } + ) + + $script:global:DSCAZDO_OrganizationName = 'DefaultOrg' + + Mock -CommandName List-DevOpsProcess -MockWith { + param($param) $mockProcesses + } + + Mock -CommandName Add-CacheItem + Mock -CommandName Export-CacheObject + + } + + Context 'When OrganizationName parameter is provided' { + It 'should call List-DevOpsProcess with provided OrganizationName' { + AzDoAPI_8_ProjectProcessTemplates -OrganizationName $mockOrganizationName + + Assert-MockCalled List-DevOpsProcess -ParameterFilter { $Organization -eq $mockOrganizationName } -Exactly 1 + } + + It 'should add processes to cache' { + AzDoAPI_8_ProjectProcessTemplates -OrganizationName $mockOrganizationName + + $mockProcesses | ForEach-Object { + Assert-MockCalled Add-CacheItem -ParameterFilter { + $Key -eq $_.name -and + $Value -eq $_ -and + $Type -eq 'LiveProcesses' + } -Exactly 1 + } + } + + It 'should export cache' { + AzDoAPI_8_ProjectProcessTemplates -OrganizationName $mockOrganizationName + + Assert-MockCalled Export-CacheObject -ParameterFilter { $CacheType -eq 'LiveProcesses' } + } + } + + Context 'When OrganizationName parameter is not provided' { + It 'should call List-DevOpsProcess with global OrganizationName' { + AzDoAPI_8_ProjectProcessTemplates + + Assert-MockCalled List-DevOpsProcess -ParameterFilter { $Organization -eq $global:DSCAZDO_OrganizationName } -Exactly 1 + } + } + + Context 'When an error occurs during function execution' { + It 'should catch and handle the error' { + Mock -CommandName List-DevOpsProcess -MockWith { throw 'An error occurred' } + Mock -CommandName Write-Error -Verifiable + + AzDoAPI_8_ProjectProcessTemplates -OrganizationName $mockOrganizationName + + Assert-VerifiableMock + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.tests.ps1 new file mode 100644 index 000000000..c0eec1940 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.tests.ps1 @@ -0,0 +1,133 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Export-CacheObject" -Tags "Unit", "Cache" { + + AfterAll { + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + } + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Export-CacheObject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + # Mock dependencies + Mock -CommandName Get-AzDoCacheObjects -MockWith { return @('Project', 'Team', 'Group', 'SecurityDescriptor') } -Verifiable + Mock -CommandName Test-Path -MockWith { param ($Path) return $false } -Verifiable + Mock -CommandName New-Item -MockWith { param ($Path, $ItemType) } -Verifiable + Mock -CommandName Export-Clixml -MockWith { param ($InputObject, $Depth, $LiteralPath) } -Verifiable + + } + + BeforeEach { + $env:AZDODSC_CACHE_DIRECTORY = "C:\MockPath\AzDoCache" + } + + AfterEach { + Remove-Variable -Name AZDODSC_CACHE_DIRECTORY -ErrorAction SilentlyContinue + } + + Context "when the environment variable AZDODSC_CACHE_DIRECTORY is not set" { + BeforeEach { + $env:AZDODSC_CACHE_DIRECTORY = $null + } + + It "should throw an error" { + { Export-CacheObject -CacheType 'Project' -Content @() } | Should -Throw "The environment variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the variable to the path of the cache directory." + } + } + + Context "when the environment variable AZDODSC_CACHE_DIRECTORY is set" { + BeforeEach { + $env:AZDODSC_CACHE_DIRECTORY = "C:\Temp\AzDoCache" + } + + It "should create the cache directory if it does not exist" { + Mock -CommandName Test-Path -MockWith { param ($Path) return $false } -Verifiable + Mock -CommandName New-Item -Verifiable + + Export-CacheObject -CacheType 'Project' -Content @() + + Assert-MockCalled -CommandName New-Item -Exactly 1 -ParameterFilter { + $Path -eq "C:\Temp\AzDoCache\Cache" -and + $ItemType -eq "Directory" + } + } + + It "should not create the cache directory if it already exists" { + Mock -CommandName Test-Path -MockWith { param ($Path) return $true } -Verifiable + Mock -CommandName New-Item + + Export-CacheObject -CacheType 'Project' -Content @() + + Assert-MockCalled -CommandName New-Item -Exactly 0 -Scope It + } + + It "should save content to the cache file" { + + Mock -CommandName Export-Clixml -Verifiable + + $content = @( + [PSCustomObject]@{ Name = 'Project1'; Id = 1 }, + [PSCustomObject]@{ Name = 'Project2'; Id = 2 } + ) + + Export-CacheObject -CacheType 'Project' -Content $content + Assert-MockCalled -CommandName Export-Clixml -Times 1 + + } + + It "should use the default depth value of 3" { + + Mock -CommandName Export-Clixml + + $content = @( + [PSCustomObject]@{ Name = 'Project1'; Id = 1 }, + [PSCustomObject]@{ Name = 'Project2'; Id = 2 } + ) + + Export-CacheObject -CacheType 'Project' -Content $content + + Assert-MockCalled -CommandName Export-Clixml -Times 1 -ParameterFilter { + $Depth -eq 3 -and + $LiteralPath -eq "C:\Temp\AzDoCache\Cache\Project.clixml" + } + } + + It "should use the specified depth value" { + + Mock -CommandName Export-Clixml + + $content = @( + [PSCustomObject]@{ Name = 'Project1'; Id = 1 }, + [PSCustomObject]@{ Name = 'Project2'; Id = 2 } + ) + + Export-CacheObject -CacheType 'Project' -Content $content -Depth 5 + + Assert-MockCalled -CommandName Export-Clixml -Times 1 -ParameterFilter { + $Depth -eq 5 -and + $LiteralPath -eq "C:\Temp\AzDoCache\Cache\Project.clixml" + } + } + } + + Context "when invalid CacheType is provided" { + It "should throw an error" { + { Export-CacheObject -CacheType 'InvalidType' -Content @() } | Should -Throw + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.tests.ps1 new file mode 100644 index 000000000..33772d05b --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.tests.ps1 @@ -0,0 +1,68 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Find-CacheItem.Tests.ps1 + +# Import the module or the script containing the function +# Import-Module 'Path\To\Your\Module.psd1' or . .\Path\To\YourScript.ps1 + +Describe 'Find-CacheItem' -Tags "Unit", "Cache" { + + AfterAll { + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + } + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Find-CacheItem.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + $testCacheList = @( + [PSCustomObject]@{ Name = 'Item1'; Value = @{ Name = 'Value1' } }, + [PSCustomObject]@{ Name = 'Item2'; Value = @{ Name = 'Value2' } }, + [PSCustomObject]@{ Name = 'MyCacheItem'; Value = @{ Name = 'MyValue' } } + ) + + } + + Context 'When a matching item exists' { + + It 'Returns the matching item' { + + $filter = { $_.Value.Name -eq 'MyValue' } + $result = $testCacheList | Find-CacheItem -Filter $filter + $result | Should -HaveCount 1 + $result.Name | Should -Be 'MyCacheItem' + } + + } + + Context 'When no matching item exists' { + It 'Returns an empty array' { + $filter = { $_.Name -eq 'NonExistentItem' } + $result = $testCacheList | Find-CacheItem -Filter $filter + $result | Should -BeNullOrEmpty + } + } + + Context 'With an empty cache list' { + It 'Returns an empty array' { + $emptyCacheList = @() + $filter = { $_.Name -eq 'AnyItem' } + $result = $emptyCacheList | Find-CacheItem -Filter $filter + $result | Should -BeNullOrEmpty + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.tests.ps1 new file mode 100644 index 000000000..c2b787b82 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.tests.ps1 @@ -0,0 +1,93 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-CacheItem' -Tags "Unit", "Cache" { + + AfterAll { + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + } + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-CacheItem.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + Mock -CommandName Get-AzDoCacheObjects -MockWith { return @('Type1', 'Type2') } + Mock -CommandName Get-CacheObject + + } + + Context 'Valid Cache Item' { + It 'Returns cache item value' { + $expectedValue = 'TestValue' + + Mock -CommandName Get-CacheObject -MockWith { + + $list = [System.Collections.Generic.List[CacheItem]]::New() + $listItem = [CacheItem]::New('MyKey', $expectedValue) + $list.Add($listItem) + + return $list + } + + $result = Get-CacheItem -Key 'MyKey' -Type 'Type1' + $result | Should -Be $expectedValue + } + } + + Context 'Cache item does not exist' { + It 'Returns $null when cache item is not found' { + Mock -CommandName Get-CacheObject -MockWith { + + $list = [System.Collections.Generic.List[CacheItem]]::New() + $listItem = [CacheItem]::New('MyKey', $expectedValue) + $list.Add($listItem) + + return $list + } + + $result = Get-CacheItem -Key 'NonExistentKey' -Type 'Type1' + $result | Should -Be $null + } + } + + Context 'Error handling' { + It 'Logs error to verbose stream and returns $null' { + Mock -CommandName Write-Verbose + Mock -CommandName Get-CacheObject -MockWith { throw 'Test exception' } + + $result = { Get-CacheItem -Key 'MyKey' -Type 'Type1' } | Should -Not -Throw + $result | Should -Be $null + + Assert-MockCalled -CommandName Write-Verbose -Exactly 1 + } + } + + Context 'Using Filter' { + It 'Applies provided filter to cache items' { + $filteredValue = 'FilteredValue' + + Mock -CommandName Get-CacheObject -MockWith { + $list = [System.Collections.Generic.List[CacheItem]]::New() + $list.Add([CacheItem]::New('OtherKey', 'OtherValue')) + $list.Add([CacheItem]::New('MyKey', $filteredValue)) + return $list + } + + $result = Get-CacheItem -Key 'MyKey' -Type 'Type1' -Filter { $_.Value -eq $filteredValue } + $result | Should -Be $filteredValue + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.tests.ps1 new file mode 100644 index 000000000..53872183d --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.tests.ps1 @@ -0,0 +1,59 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-CacheObject Tests' -Tags "Unit", "Cache" { + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-CacheObject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + $ENV:AZDODSC_CACHE_DIRECTORY = "C:\MockCacheDirectory" + + Mock -CommandName Get-AzDoCacheObjects -MockWith { return @('Project', 'Team', 'Group', 'SecurityDescriptor') } + Mock -CommandName Import-CacheObject + + } + + AfterAll { + + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + + if ($originalEnvironment) { + Set-Variable -Name "ENV" -Value $originalEnvironment -Scope Global + } + } + + It 'Should throw error if environment variable is not set' { + $ENV:AZDODSC_CACHE_DIRECTORY = $null + { Get-CacheObject -CacheType 'Project' } | Should -Throw "The environment variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the variable to the path of the cache directory." + } + + It 'Should return cache object from memory if available' { + $env:AZDODSC_CACHE_DIRECTORY = "C:\MockCacheDirectory" + Set-Variable -Name "AzDoProject" -Value "ProjectCache" -Scope Global + $result = Get-CacheObject -CacheType 'Project' + $result | Should -Be "ProjectCache" + Remove-Variable -Name "AzDoProject" -Scope Global -ErrorAction SilentlyContinue + } + + It 'Should import cache object if not available in memory' { + $env:AZDODSC_CACHE_DIRECTORY = "C:\MockCacheDirectory" + Mock -CommandName Import-CacheObject -MockWith { return "ImportedProjectCache" } + + $result = Get-CacheObject -CacheType 'Project' + $result | Should -Be "ImportedProjectCache" + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.tests.ps1 new file mode 100644 index 000000000..ad76010a7 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.tests.ps1 @@ -0,0 +1,82 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Import-CacheObject Tests" -Tags "Unit", "Cache" { + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Import-CacheObject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + # Mock the necessary Commands and Variables + Mock -CommandName Get-AzDoCacheObjects -MockWith { return @('Project', 'Team', 'Group', 'SecurityDescriptor') } + Mock -CommandName Test-Path -MockWith { return $true } + Mock -CommandName Import-Clixml -MockWith { return @([PSCustomObject]@{ Key = 'Key1'; Value = 'Value1' }, [PSCustomObject]@{ Key = 'Key2'; Value = 'Value2' }) } + Mock -CommandName Set-Variable + $ENV:AZDODSC_CACHE_DIRECTORY = 'C:\Cache' + + } + + AfterAll { + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + } + + Context "Valid CacheType parameter" { + + It "Imports the cache object successfully" { + Import-CacheObject -CacheType 'Project' + + Assert-MockCalled -CommandName Test-Path -Exactly 1 + Assert-MockCalled -CommandName Import-Clixml -Exactly 1 + Assert-MockCalled -CommandName Set-Variable -Exactly 1 + } + + It "Sets the correct variable name and value" { + Import-CacheObject -CacheType 'Team' + + Assert-MockCalled -CommandName Set-Variable -Exactly 1 -ParameterFilter { + $Name -eq 'AzDoTeam' -and $Scope -eq 'Global' -and $Force -eq $true + } + } + } + + Context "Cache file not found" { + BeforeEach { + Mock -CommandName Test-Path -MockWith { return $false } + Mock -CommandName Write-Warning + } + + It "Writes a warning when cache file is not found" { + Import-CacheObject -CacheType 'Project' + + Assert-MockCalled -CommandName Write-Warning -Exactly 1 -ParameterFilter { + $Message -match 'Cache file not found' + } + } + } + + Context "Environment variable not set" { + BeforeEach { + $ENV:AZDODSC_CACHE_DIRECTORY = $null + } + + It "Throws an exception if the environment variable is not set" { + { + Import-CacheObject -CacheType 'Project' + } | Should -Throw + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.tests.ps1 new file mode 100644 index 000000000..8be3a79d9 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.tests.ps1 @@ -0,0 +1,101 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Initialize-CacheObject Tests" -Tags "Unit", "Cache" { + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Initialize-CacheObject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + # Mock the necessary Commands and Variables + Mock -CommandName Get-AzDoCacheObjects -MockWith { return @('LiveProject', 'LiveProjects', 'Project', 'Team', 'Group', 'SecurityDescriptor') } + Mock -CommandName Test-Path -MockWith { param($Path) return $false } + Mock -CommandName Import-CacheObject + Mock -CommandName Set-CacheObject + Mock -CommandName Remove-Item + Mock -CommandName New-Item + + $ENV:AZDODSC_CACHE_DIRECTORY = 'C:\Cache' + + } + + AfterAll { + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + } + + Context "Valid CacheType parameter" { + + It "Imports the cache object if cache file exists" { + + Mock Test-Path { $true } + + Initialize-CacheObject -CacheType 'Project' + + Assert-MockCalled -CommandName Import-CacheObject -Exactly 1 + Assert-MockCalled -CommandName Set-CacheObject -Exactly 0 + } + + It "Creates a new cache object if cache file does not exist" { + Mock Test-Path { $false } + + Initialize-CacheObject -CacheType 'Project' + + Assert-MockCalled -CommandName Import-CacheObject -Exactly 0 + Assert-MockCalled -CommandName Set-CacheObject -Exactly 1 + } + + It "Removes cache file if BypassFileCheck is not present and CacheType matches '^Live'" { + Mock Test-Path -MockWith { $true } -ParameterFilter { $ErrorAction -eq 'SilentlyContinue' } + Mock Test-Path -MockWith { $false } -ParameterFilter { $Path -eq 'C:\Cache\Cache\LiveProject.clixml' } + + Initialize-CacheObject -CacheType 'LiveProject' + + Assert-MockCalled -CommandName Remove-Item -Times 1 + Assert-MockCalled -CommandName Import-CacheObject -Exactly 0 + Assert-MockCalled -CommandName Set-CacheObject -Times 1 + } + } + + Context "Environment variable not set" { + BeforeEach { + $ENV:AZDODSC_CACHE_DIRECTORY = $null + } + + It "Throws an exception if the environment variable is not set" { + { + Initialize-CacheObject -CacheType 'Project' + } | Should -Throw "*The environment variable `'AZDODSC_CACHE_DIRECTORY`' is not set.*" + } + } + + Context "BypassFileCheck switch" { + + BeforeAll { + $ENV:AZDODSC_CACHE_DIRECTORY = 'C:\Cache' + } + + It "Does not remove cache file if BypassFileCheck is present" { + Mock Test-Path { $true } -ParameterFilter { $Path -ne $null } + + Initialize-CacheObject -CacheType 'LiveProjects' -BypassFileCheck + + Assert-MockCalled -CommandName Remove-Item -Exactly 0 + Assert-MockCalled -CommandName Import-CacheObject -Exactly 1 + Assert-MockCalled -CommandName Set-CacheObject -Exactly 0 + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.tests.ps1 new file mode 100644 index 000000000..fcedd7d7a --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.tests.ps1 @@ -0,0 +1,60 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Refresh-AzDoCache Tests" -Tags "Unit", "Cache" { + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Refresh-AzDoCache.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + # Mock the Get-Command cmdlet to return a list of commands matching the pattern + Mock -CommandName Get-Command -MockWith { + @( + [pscustomobject]@{ Name = 'AzDoAPI_CacheType1'; Source = 'AzureDevOpsDsc.Common' }, + [pscustomobject]@{ Name = 'AzDoAPI_CacheType2'; Source = 'AzureDevOpsDsc.Common' } + ) + } + + function AzDoAPI_CacheType1 { + param ($OrganizationName) + } + function AzDoAPI_CacheType2 { + param ($OrganizationName) + } + + # Mock the commands that would be invoked by Refresh-AzDoCache + Mock -CommandName AzDoAPI_CacheType1 + Mock -CommandName AzDoAPI_CacheType2 + Mock -CommandName Remove-Variable + + } + + Context "When OrganizationName is provided" { + It "Should call each caching command with the correct OrganizationName parameter" { + $orgName = 'MyOrg' + Refresh-AzDoCache -OrganizationName $orgName + + # Verify that Get-Command was called with the correct parameters + Assert-MockCalled -CommandName Get-Command + + # Verify that each caching command was called with the correct OrganizationName parameter + Assert-MockCalled -CommandName AzDoAPI_CacheType1 + Assert-MockCalled -CommandName AzDoAPI_CacheType2 + + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.tests.ps1 new file mode 100644 index 000000000..f50359c82 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.tests.ps1 @@ -0,0 +1,77 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Refresh-CacheIdentity' -Tags "Unit", "Cache" { + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Refresh-CacheIdentity.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Get-AzDoCacheObjects -MockWith { return @('TypeA', 'TypeB', 'TypeC') } + Mock -CommandName Get-DevOpsDescriptorIdentity -MockWith { + return [PSCustomObject]@{ + id = 'id123' + descriptor = 'descriptor123' + subjectDescriptor = 'subjectDescriptor123' + providerDisplayName = 'providerDisplayName123' + isActive = $true + isContainer = $false + } + } + Mock -CommandName Add-CacheItem -MockWith {} + Mock -CommandName Get-CacheObject -MockWith { return @() } + Mock -CommandName Set-CacheObject -MockWith {} + + $global:DSCAZDO_OrganizationName = 'TestOrg' + $key = 'testKey' + $cacheType = 'TypeA' + + } + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -ErrorAction SilentlyContinue + } + + BeforeEach { + $identity = [PSCustomObject]@{ descriptor = 'descriptor123' } + } + + It 'Adds ACLIdentity to Identity' { + + Refresh-CacheIdentity -Identity $identity -Key $key -CacheType $cacheType + + $identity.PSObject.Properties.Match('ACLIdentity').Count | Should -Be 1 + $identity.ACLIdentity.id | Should -Be 'id123' + } + + It 'Should not throw an error' { + $ErrorActionPreference = 'Stop' + { Refresh-CacheIdentity -Identity $identity -Key $key -CacheType $cacheType } | Should -Not -Throw + } + + It 'Should not throw an error when there are duplicate cache objects' { + $ErrorActionPreference = 'Stop' + { Refresh-CacheIdentity -Identity $identity -Key $key -CacheType $cacheType } | Should -Not -Throw + { Refresh-CacheIdentity -Identity $identity -Key $key -CacheType $cacheType } | Should -Not -Throw + } + + + It 'Calls Add-CacheItem with correct parameters' { + Refresh-CacheIdentity -Identity $identity -Key $key -CacheType $cacheType + + Assert-MockCalled -CommandName Add-CacheItem -Exactly 1 + } + + It 'Calls Set-CacheObject with current cache' { + Refresh-CacheIdentity -Identity $identity -Key $key -CacheType $cacheType + + Assert-MockCalled -CommandName Set-CacheObject -Exactly 1 -Scope It + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.tests.ps1 new file mode 100644 index 000000000..11ff3eff5 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.tests.ps1 @@ -0,0 +1,65 @@ +# Tests for Refresh-CacheObject function + +# Import the module containing the Refresh-CacheObject function if necessary +# Import-Module YourModuleName + +$currentFile = $MyInvocation.MyCommand.Path + + +Describe "Refresh-CacheObject" -tags Unit, Cache { + + AfterAll { + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + } + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-CacheItem.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + # Mock the Get-AzDoCacheObjects function to return a list of valid cache types + Mock -CommandName Get-AzDoCacheObjects -MockWith { return @('Type1', 'Type2', 'Type3') } + + # Mock the Remove-Variable cmdlet to prevent actual removal of variables + Mock -CommandName Remove-Variable + + # Mock the Import-CacheObject function to prevent actual import actions + Mock -CommandName Import-CacheObject + + } + + Context "When CacheType is valid" { + It "Should unload and reload the cache object of type 'Type1'" { + $cacheType = 'Type1' + Refresh-CacheObject -CacheType $cacheType + + # Verify that Remove-Variable was called with the correct parameters + Assert-MockCalled -CommandName Remove-Variable -Exactly 1 -ParameterFilter { $Name -eq "AzDoType1" } + + # Verify that Import-CacheObject was called with the correct parameter + Assert-MockCalled -CommandName Import-CacheObject -Exactly 1 -ParameterFilter { $CacheType -eq 'Type1' } + } + + It "Should unload and reload the cache object of type 'Type2'" { + $cacheType = 'Type2' + Refresh-CacheObject -CacheType $cacheType + + # Verify that Remove-Variable was called with the correct parameters + Assert-MockCalled -CommandName Remove-Variable -Exactly 1 -ParameterFilter { $Name -eq "AzDoType2" } + + # Verify that Import-CacheObject was called with the correct parameter + Assert-MockCalled -CommandName Import-CacheObject -Exactly 1 -ParameterFilter { $CacheType -eq 'Type2' } + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.tests.ps1 new file mode 100644 index 000000000..367fdd298 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.tests.ps1 @@ -0,0 +1,75 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Remove-CacheItem' -Tags "Unit", "Cache" { + + AfterAll { + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + } + + BeforeAll { + + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-CacheItem.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + Mock -CommandName Get-CacheObject -MockWith { + param ([string]$CacheType) + + $list = [System.Collections.Generic.List[CacheItem]]::New() + + switch ($CacheType) + { + "Project" { + $list.Add([CacheItem]::New("myKey", "someValue")) + } + "Group" { + $list.Add([CacheItem]::New("anotherKey", "anotherValue")) + } + default { + throw "Invalid CacheType" + } + } + + return $list + + } + + Mock -CommandName Set-Variable -MockWith {} + + } + + It 'Removes item from Project cache when key matches' { + $cache = Get-CacheObject -CacheType "Project" + Remove-CacheItem -Key "myKey" -Type "Project" + $global:AzDoProject | Should -BeNullOrEmpty + } + + It 'Removes item from Group cache when key matches' { + $cache = Get-CacheObject -CacheType "Group" + Remove-CacheItem -Key "anotherKey" -Type "Group" + $global:AzDoGroup | Should -BeNullOrEmpty + } + + It 'Handles non-matching key correctly' { + $cache = Get-CacheObject -CacheType "Group" + Remove-CacheItem -Key "nonMatchingKey" -Type "Group" + $global:AzDoGroup | Should -Be $null + } + + It 'Validates Type parameter against cache objects' { + Mock -CommandName Get-AzDoCacheObjects -MockWith { return @('Project', 'Group', 'Team', 'SecurityDescriptor') } + { Remove-CacheItem -Key "sampleKey" -Type "InvalidType" } | Should -Throw + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.tests.ps1 new file mode 100644 index 000000000..3a4212921 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.tests.ps1 @@ -0,0 +1,65 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Set-CacheObject' -Tags "Unit", "Cache" { + + BeforeAll { + + # Set the Project + $null = Set-Variable -Name "AzDoProject" -Value @() -Scope Global + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-CacheObject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + Mock -CommandName Get-AzDoCacheObjects -MockWith { return @('Project', 'Team', 'Group', 'SecurityDescriptor') } + Mock -CommandName Export-CacheObject -MockWith {} + + } + + AfterAll { + Remove-Variable -Name AzDoProject -ErrorAction SilentlyContinue + } + + Context 'When setting Project cache' { + + It 'should set the global variable AzDoProject' { + $content = @('Project1', 'Project2') + $global:AzDoProject = $null + + Set-CacheObject -CacheType 'Project' -Content $content -Depth 2 + + $global:AzDoProject | Should -Be $content + } + + It 'should call Export-CacheObject with correct parameters' { + $content = @('Project1', 'Project2') + + Set-CacheObject -CacheType 'Project' -Content $content -Depth 2 + + Assert-MockCalled Export-CacheObject -Exactly -Times 1 -ParameterFilter { + $CacheType -eq 'Project' -and + $Depth -eq 2 + } + } + + It 'should throw an error if CacheType is invalid' { + { Set-CacheObject -CacheType 'InvalidType' -Content @('data') } | Should -Throw + } + + It 'should throw an error if Export-CacheObject fails' { + Mock -CommandName Export-CacheObject -MockWith { throw "Export failed" } + Mock -CommandName Write-Error -Verifiable + + { Set-CacheObject -CacheType 'Project' -Content @('data') } | Should -Throw + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiHttpRequestHeader.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiHttpRequestHeader.Tests.ps1 deleted file mode 100644 index b03170072..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiHttpRequestHeader.Tests.ps1 +++ /dev/null @@ -1,87 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesValidPats = Get-TestCase -ScopeName 'Pat' -TestCaseName 'Valid' - $testCasesInvalidPats = Get-TestCase -ScopeName 'Pat' -TestCaseName 'Invalid' - - - Context 'When input parameters are valid' { - - It 'Should not throw - ""' -TestCases $testCasesValidPats { - param ([System.String]$Pat) - - { Get-AzDevOpsApiHttpRequestHeader -Pat $Pat } | Should -Not -Throw - } - - It 'Should output a "Hashtable" type - ""' -TestCases $testCasesValidPats { - param ([System.String]$Pat) - - $httpRequestHeader = Get-AzDevOpsApiHttpRequestHeader -Pat $Pat - - $httpRequestHeader.GetType() | Should -Be $(@{}.GetType()) - } - - It 'Should output a "Hashtable" type containing an "Authorization" key - ""' -TestCases $testCasesValidPats { - param ([System.String]$Pat) - - $httpRequestHeader = Get-AzDevOpsApiHttpRequestHeader -Pat $Pat - - $httpRequestHeader.ContainsKey('Authorization') | Should -BeTrue - } - - It 'Should output a "Hashtable" type containing an "Authorization" key that has a value beginning with "Basic " - ""' -TestCases $testCasesValidPats { - param ([System.String]$Pat) - - $httpRequestHeader = Get-AzDevOpsApiHttpRequestHeader -Pat $Pat - - $httpRequestHeader.Authorization | Should -BeLike "Basic *" - } - - It 'Should output a "Hashtable" type containing an "Authorization" key that has a value as expected - "" ' -TestCases $testCasesValidPats { - param ([System.String]$Pat) - - $authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$Pat")) - $httpRequestHeader = Get-AzDevOpsApiHttpRequestHeader -Pat $Pat - - $httpRequestHeader.Authorization | Should -BeExactly $authorization - } - - It 'Should output a "Hashtable" type that is successfully validated by "Test-AzDevOpsApiHttpRequestHeader" - ""' -TestCases $testCasesValidPats { - param ([System.String]$Pat) - - $httpRequestHeader = Get-AzDevOpsApiHttpRequestHeader -Pat $Pat - - Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $httpRequestHeader -IsValid | Should -BeTrue - } - - } - - - Context "When input parameters are invalid" { - - It 'Should throw - ""' -TestCases $testCasesInvalidPats { - param ([System.String]$Pat) - - { Get-AzDevOpsApiHttpRequestHeader -Pat $Pat } | Should -Throw - } - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResource.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResource.Tests.ps1 deleted file mode 100644 index 922fb886c..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResource.Tests.ps1 +++ /dev/null @@ -1,278 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - # Get default, parameter values - $defaultApiVersion = Get-AzDevOpsApiVersion -Default - - # Helper function for generating fake resource, JSON response - Function Get-MockResourceJson - { - return '{ - "count": 3, - "value": [ - { - "id": "8d4bff8d-6169-45cf-b085-fe12ad67e76b", - "name": "Test Resource 1", - "description": "Test Resource Description 1", - "url": "https://dev.azure.com/fabrikam/_apis/resources/8d4bff8d-6169-45cf-b085-fe12ad67e76b", - "state": "wellFormed" - }, - { - "id": "114bff8d-6169-45cf-b085-fe121267e7aa", - "name": "Test Resource 2", - "description": "Test Resource Description 2", - "url": "https://dev.azure.com/fabrikam/_apis/resources/114bff8d-6169-45cf-b085-fe121267e7aa", - "state": "wellFormed" - }, - { - "id": "a654b805-6be9-477b-a00c-bd76949192c3", - "name": "Test Resource 3", - "description": "Test Resource Description 3", - "url": "https://dev.azure.com/fabrikam/_apis/resources/a654b805-6be9-477b-a00c-bd76949192c3", - "state": "wellFormed" - } - ] - }' - } - $noOfMockResources = $((Get-MockResourceJson | ConvertFrom-Json).value).Count - $resourceIdThatExists = '8d4bff8d-6169-45cf-b085-fe12ad67e76b' # Same as 'Test Resource 1' in 'Get-MockResourceJson', JSON output - $resourceIdThatDoesNotExist = '7f5a49c8-9424-4ec5-b4b7-1dc76cd05149' - $resourceIdThatIsInvalid = Get-TestCaseValue -ScopeName 'ResourceId' -TestCaseName 'Invalid' - - # Mock functions called in function - Mock Invoke-AzDevOpsApiRestMethod { - - #$resourceIdThatExists = '8d4bff8d-6169-45cf-b085-fe12ad67e76b' - [PSObject]$resources = Get-MockResourceJson | ConvertFrom-Json - - if (![string]::IsNullOrWhiteSpace($ResourceId)) - { - [PSObject[]]$resources = $resources.value - [PSObject]$resources = $resources | - Where-Object { $_.id -eq $ResourceId} - } - - return $resources - } - # Mock Get-AzDevOpsApiResourceUri # Do not mock - # Mock Get-AzDevOpsApiHttpRequestHeader # Do not mock - - # Generate valid, test cases - $testCasesValidApiUris = Get-TestCase -ScopeName 'ApiUri' -TestCaseName 'Valid' - $testCasesValidPats = Get-TestCase -ScopeName 'Pat' -TestCaseName 'Valid' - $testCasesValidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Valid' - $testCasesValidApiUriPatResourceNames = Join-TestCaseArray -TestCaseArray @( - $testCasesValidApiUris, - $testCasesValidPats, - $testCasesValidResourceNames) -Expand - $testCasesValidApiUriPatResourceNames3 = $testCasesValidApiUriPatResourceNames | Select-Object -First 3 - - $validApiVersion = Get-TestCaseValue -ScopeName 'ApiVersion' -TestCaseName 'Valid' -First 1 - - # Generate invalid, test cases - $testCasesInvalidApiUris = Get-TestCase -ScopeName 'ApiUri' -TestCaseName 'Invalid' - $testCasesInvalidPats = Get-TestCase -ScopeName 'Pat' -TestCaseName 'Invalid' - $testCasesInvalidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Invalid' - $testCasesInvalidApiUriPatResourceNames = Join-TestCaseArray -TestCaseArray @( - $testCasesInvalidApiUris, - $testCasesInvalidPats, - $testCasesInvalidResourceNames) -Expand - $testCasesInvalidApiUriPatResourceNames3 = $testCasesInvalidApiUriPatResourceNames | Select-Object -First 3 - - $invalidApiVersion = Get-TestCaseValue -ScopeName 'ApiVersion' -TestCaseName 'Invalid' -First 1 - - - Context 'When input parameters are valid' { - - Context 'When called with mandatory, "ApiUri", "Pat" and "ResourceName" parameters' { - - It 'Should not throw - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName } | Should -Not -Throw - } - - It 'Should return a type of "System.Management.Automation.PsObject[]" - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - [System.Management.Automation.PsObject[]]$resources = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName - - $true | Should -Be $true # Note: Will always evaluate true (but strong-typing of $resources variable would fail this test anyway) - } - - It 'Should return all resources - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - $resources = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName - - $resources.Count | Should -Be $noOfMockResources - } - - It 'Should invoke "Get-AzDevOpsApiResourceUri" only once - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - Mock Get-AzDevOpsApiResourceUri { - return "http://someUri.api/" - } -Verifiable - - $resources = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName - - Assert-MockCalled 'Get-AzDevOpsApiResourceUri' -Times 1 -Exactly -Scope 'It' - } - - It 'Should invoke "Get-AzDevOpsApiHttpRequestHeader" only once - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - Mock Get-AzDevOpsApiHttpRequestHeader { - Get-TestCaseValue -ScopeName 'HttpRequestHeader' -TestCaseName 'Valid' -First 1 - } -Verifiable - - $resources = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName - - Assert-MockCalled 'Get-AzDevOpsApiHttpRequestHeader' -Times 1 -Exactly -Scope 'It' - } - - It 'Should invoke "Invoke-AzDevOpsApiRestMethod" only once - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - Mock Invoke-AzDevOpsApiRestMethod {} -Verifiable - - $resources = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName - - Assert-MockCalled 'Invoke-AzDevOpsApiRestMethod' -Times 1 -Exactly -Scope 'It' - } - - } - - - Context 'When called with mandatory, "ApiUri", "Pat", "ResourceName" and "ResourceId" parameters' { - - Context 'When the "ResourceId" parameter value is invalid' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $resourceIdThatIsInvalid } | Should -Throw - } - } - - Context 'When a resource with the "ResourceId" parameter value does exist' { - - It 'Should not throw - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $resourceIdThatExists } | Should -Not -Throw - } - - It 'Should return a type of "System.Management.Automation.PsObject[]" - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - [System.Management.Automation.PsObject[]]$resources = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $resourceIdThatExists - - $true | Should -Be $true # Note: Will always evaluate true (but strong-typing of $resources variable would fail this test anyway) - } - - It 'Should not return a $null - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - [System.Management.Automation.PsObject]$resource = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $resourceIdThatExists - - $resource | Should -Not -BeNullOrEmpty - } - - It 'Should return a resource with the correct "id"/"ResourceId" - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - [System.Management.Automation.PsObject]$resource = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $resourceIdThatExists - - $resource.id | Should -Be $resourceIdThatExists - } - } - - - Context 'When a resource with the "ResourceId" parameter value does not exist' { - - It 'Should not throw - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $resourceIdThatDoesNotExist } | Should -Not -Throw - } - - It 'Should return a type of "System.Management.Automation.PsObject[]" - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - [System.Management.Automation.PsObject[]]$resources = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $resourceIdThatDoesNotExist - - $true | Should -Be $true # Note: Will always evaluate true (but strong-typing of $resources variable would fail this test anyway) - } - - It 'Should return no resources - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - [System.Management.Automation.PsObject[]]$resources = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $resourceIdThatDoesNotExist - - $resources.Count | Should -Be 0 - } - - It 'Should return $null - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - $resource = Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $resourceIdThatDoesNotExist - - $resource | Should -Be $null - } - } - - - Context "When also called with valid 'ApiVersion' parameter value" { - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Get-AzDevOpsApiResource -ApiUri $ApiUri -ApiVersion $validApiVersion -Pat $Pat -ResourceName $ResourceName } | Should -Not -Throw - } - } - - Context "When also called with invalid 'ApiVersion' parameter value" { - - It "Should throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Get-AzDevOpsApiResource -ApiUri $ApiUri -ApiVersion $invalidApiVersion -Pat $Pat -ResourceName $ResourceName } | Should -Throw - } - } - } - } - - - Context 'When input parameters are invalid' { - - Context 'When called with mandatory, "ApiUri", "Pat" and "ResourceName" parameters' { - - It 'Should throw - "", "", ""' -TestCases $testCasesinvalidApiUriPatResourceNames { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Get-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName } | Should -Throw - } - } - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResourceName.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResourceName.Tests.ps1 deleted file mode 100644 index 68f829c67..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiResourceName.Tests.ps1 +++ /dev/null @@ -1,80 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - - Context 'When input parameters are valid' { - - It 'Should not throw' { - - { Get-AzDevOpsApiResourceName } | Should -Not -Throw - } - - It 'Should output a "System.String[]" type containing atleast 1 string value' { - - [System.String[]]$resourceNames = Get-AzDevOpsApiResourceName - - $resourceNames.Count | Should -BeGreaterThan 0 - } - - It 'Should output a "System.String[]" type containing no empty values' { - - [System.String[]]$resourceNames = Get-AzDevOpsApiResourceName - - [System.String]::Empty | Should -Not -BeIn $resourceNames - } - - It 'Should output a "System.String[]" type containing no $null values' { - - [System.String[]]$resourceNames = Get-AzDevOpsApiResourceName - - $null | Should -Not -BeIn $resourceNames - } - - It 'Should output a "System.String[]" type containing unique values' { - - [System.String[]]$resourceNames = Get-AzDevOpsApiResourceName - - $resourceNames.Count | Should -Be $($resourceNames | Select-Object -Unique).Count - } - - # Create test cases for each 'ResourceName' returned by 'Get-AzDevOpsApiResourceName' - [Hashtable[]]$testCasesResourceNames = Get-AzDevOpsApiResourceName | - ForEach-Object { - @{ - ResourceName = $_ - } - } - - It 'Should output values that are all validated by "Test-AzDevOpsApiResourceName" - ""' -TestCases $testCasesResourceNames { - param ([System.String]$ResourceName) - - Test-AzDevOpsApiResourceName -ResourceName $ResourceName -IsValid | Should -BeTrue - } - - } - - - Context "When input parameters are invalid" { - - # N/A - No parameters passed to function - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriAreaName.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriAreaName.Tests.ps1 deleted file mode 100644 index d075813bd..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriAreaName.Tests.ps1 +++ /dev/null @@ -1,127 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesValidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Valid' - $testCasesInvalidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Invalid' - - - Context 'When input parameters are valid' { - - - Context 'When called with no parameter values' { - - It 'Should not throw' { - - { Get-AzDevOpsApiUriAreaName } | Should -Not -Throw - } - - It 'Should output a "System.String[]" type containing more than 1 value' { - - [System.String[]]$uriAreaNames = Get-AzDevOpsApiUriAreaName - - $uriAreaNames.Count | Should -BeGreaterThan 1 - } - - It 'Should output a "System.String[]" type containing no empty values' { - - [System.String[]]$uriAreaNames = Get-AzDevOpsApiUriAreaName - - [System.String]::Empty | Should -Not -BeIn $uriAreaNames - } - - It 'Should output a "System.String[]" type containing no $null values' { - - [System.String[]]$uriAreaNames = Get-AzDevOpsApiUriAreaName - - $null | Should -Not -BeIn $uriAreaNames - } - - It 'Should output a "System.String[]" type containing unique values' { - - [System.String[]]$uriAreaNames = Get-AzDevOpsApiUriAreaName - - $uriAreaNames.Count | Should -Be $($uriAreaNames | Select-Object -Unique).Count - } - - # Create test cases for each 'UriResourceName' returned by 'Get-AzDevOpsApiUriAreaName' - #[Hashtable[]]$testCasesUriResourceNames = Get-AzDevOpsApiUriAreaName | - # ForEach-Object { - # @{ - # UriResourceName = $_ - # } - # } - - # TODO: Uncomment this test once 'Test-AzDevOpsApiUriAreaName' function available - #It 'Should output values that are all validated by "Test-AzDevOpsApiUriAreaName" - ""' -TestCases $testCasesUriResourceNames { - # param ([System.String]$UriResourceName) - # - # Test-AzDevOpsApiUriAreaName -UriResourceName $UriResourceName -IsValid | Should -BeTrue - #} - } - - - Context 'When called with a "ResourceName" parameter value' { - - It 'Should not throw - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - { Get-AzDevOpsApiUriAreaName -ResourceName $ResourceName } | Should -Not -Throw - } - - It 'Should output a "System.String[]" type containing exactly 1 value - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - [System.String[]]$uriAreaNames = Get-AzDevOpsApiUriAreaName -ResourceName $ResourceName - - $uriAreaNames.Count | Should -BeExactly 1 - } - - It 'Should output a "System.String" type that is not null or empty - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - [System.String]$uriResourceName = Get-AzDevOpsApiUriAreaName -ResourceName $ResourceName - - $uriResourceName | Should -Not -BeNullOrEmpty - } - - It 'Should output a "System.String" type that is lowercase - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - [System.String]$uriResourceName = Get-AzDevOpsApiUriAreaName -ResourceName $ResourceName - - $uriResourceName | Should -BeExactly $($uriResourceName.ToLower()) - } - } - } - - - Context "When input parameters are invalid" { - - Context 'When called with a "ResourceName" parameter value' { - - It 'Should throw - ""' -TestCases $testCasesInvalidResourceNames { - param ([System.String]$ResourceName) - - { Get-AzDevOpsApiUriAreaName -ResourceName $ResourceName } | Should -Throw - } - } - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriResourceName.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriResourceName.Tests.ps1 deleted file mode 100644 index 639639ea3..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiUriResourceName.Tests.ps1 +++ /dev/null @@ -1,127 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesValidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Valid' - $testCasesInvalidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Invalid' - - - Context 'When input parameters are valid' { - - - Context 'When called with no parameter values' { - - It 'Should not throw' { - - { Get-AzDevOpsApiUriResourceName } | Should -Not -Throw - } - - It 'Should output a "System.String[]" type containing more than 1 value' { - - [System.String[]]$uriResourceNames = Get-AzDevOpsApiUriResourceName - - $uriResourceNames.Count | Should -BeGreaterThan 1 - } - - It 'Should output a "System.String[]" type containing no empty values' { - - [System.String[]]$uriResourceNames = Get-AzDevOpsApiUriResourceName - - [System.String]::Empty | Should -Not -BeIn $uriResourceNames - } - - It 'Should output a "System.String[]" type containing no $null values' { - - [System.String[]]$uriResourceNames = Get-AzDevOpsApiUriResourceName - - $null | Should -Not -BeIn $uriResourceNames - } - - It 'Should output a "System.String[]" type containing unique values' { - - [System.String[]]$uriResourceNames = Get-AzDevOpsApiUriResourceName - - $uriResourceNames.Count | Should -Be $($uriResourceNames | Select-Object -Unique).Count - } - - # Create test cases for each 'UriResourceName' returned by 'Get-AzDevOpsApiUriResourceName' - #[Hashtable[]]$testCasesUriResourceNames = Get-AzDevOpsApiUriResourceName | - # ForEach-Object { - # @{ - # UriResourceName = $_ - # } - # } - - # TODO: Uncomment this test once 'Test-AzDevOpsApiUriResourceName' function available - #It 'Should output values that are all validated by "Test-AzDevOpsApiUriResourceName" - ""' -TestCases $testCasesUriResourceNames { - # param ([System.String]$UriResourceName) - # - # Test-AzDevOpsApiUriResourceName -UriResourceName $UriResourceName -IsValid | Should -BeTrue - #} - } - - - Context 'When called with a "ResourceName" parameter value' { - - It 'Should not throw - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - { Get-AzDevOpsApiUriResourceName -ResourceName $ResourceName } | Should -Not -Throw - } - - It 'Should output a "System.String[]" type containing exactly 1 value - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - [System.String[]]$uriResourceNames = Get-AzDevOpsApiUriResourceName -ResourceName $ResourceName - - $uriResourceNames.Count | Should -BeExactly 1 - } - - It 'Should output a "System.String" type that is not null or empty - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - [System.String]$uriResourceName = Get-AzDevOpsApiUriResourceName -ResourceName $ResourceName - - $uriResourceName | Should -Not -BeNullOrEmpty - } - - It 'Should output a "System.String" type that is lowercase - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - [System.String]$uriResourceName = Get-AzDevOpsApiUriResourceName -ResourceName $ResourceName - - $uriResourceName | Should -BeExactly $($uriResourceName.ToLower()) - } - } - } - - - Context "When input parameters are invalid" { - - Context 'When called with a "ResourceName" parameter value' { - - It 'Should throw - ""' -TestCases $testCasesInvalidResourceNames { - param ([System.String]$ResourceName) - - { Get-AzDevOpsApiUriResourceName -ResourceName $ResourceName } | Should -Throw - } - } - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.Tests.ps1 deleted file mode 100644 index 8e28c30b7..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiVersion.Tests.ps1 +++ /dev/null @@ -1,141 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesValidApiVersions = Get-TestCase -ScopeName 'ApiVersion' -TestCaseName 'Valid' - $testCasesInvalidApiVersions = Get-TestCase -ScopeName 'ApiVersion' -TestCaseName 'Invalid' - $supportedApiVersion = '6.0' - - - Context 'When input parameters are valid' { - - - Context 'When called with no parameter values' { - - It 'Should not throw' { - - { Get-AzDevOpsApiVersion } | Should -Not -Throw - } - - # Note: Only applicable if only 1 'ApiVersion' is supported - It "Should output a 'System.String' type containing an 'ApiVersion' of '$supportedApiVersion'" { - - [System.String]$apiVersion = Get-AzDevOpsApiVersion - - $apiVersion | Should -BeExactly $supportedApiVersion - } - - # Note: Only applicable if only 1 'ApiVersion' is supported - It 'Should output a "System.String[]" type containing no empty values' { - - [System.String[]]$apiVersions = Get-AzDevOpsApiVersion - - [System.String]::Empty | Should -Not -BeIn $apiVersions - } - - It 'Should output a "System.String[]" type containing no $null values' { - - [System.String[]]$apiVersions = Get-AzDevOpsApiVersion - - $null | Should -Not -BeIn $apiVersions - } - - It 'Should output a "System.String[]" type containing unique values' { - - [System.String[]]$apiVersions = Get-AzDevOpsApiVersion - - $apiVersions.Count | Should -Be $($apiVersions | Select-Object -Unique).Count - } - - #Create test cases for each 'ApiVersion' returned by 'Get-AzDevOpsApiVersion' - [Hashtable[]]$testCasesApiVersions = Get-AzDevOpsApiVersion | - ForEach-Object { - @{ - ApiVersion = $_ - } - } - - It 'Should output values that are all validated by "Test-AzDevOpsApiVersion" - ""' -TestCases $testCasesApiVersions { - param ([System.String]$ApiVersion) - - Test-AzDevOpsApiVersion -ApiVersion $ApiVersion -IsValid | Should -BeTrue - } - - It 'Should output values that are in the valid, "ApiVersion" test cases - ""' -TestCases $testCasesValidApiVersions { - param ([System.String]$ApiVersion) - - $ApiVersion | Should -BeIn $([System.String[]]$(Get-AzDevOpsApiVersion)) - } - - It 'Should not output values that are in the invalid, "ApiVersion" test cases - ""' -TestCases $testCasesInvalidApiVersions { - param ([System.String]$ApiVersion) - - $ApiVersion | Should -Not -BeIn $([System.String[]]$(Get-AzDevOpsApiVersion)) - } - } - - - Context 'When called with the "Default" switch parameter' { - - It 'Should not throw' { - - { Get-AzDevOpsApiVersion -Default } | Should -Not -Throw - } - - It 'Should output a "System.String[]" type containing exactly 1 value' { - - [System.String[]]$apiVersions = Get-AzDevOpsApiVersion -Default - - $apiVersions.Count | Should -BeExactly 1 - } - - It 'Should output a "System.String" type that is not null or empty' { - - [System.String]$uriResourceName = Get-AzDevOpsApiVersion -Default - - $uriResourceName | Should -Not -BeNullOrEmpty - } - - It "Should output a 'System.String' type containing an 'ApiVersion' of '$supportedApiVersion'" { - - [System.String]$apiVersion = Get-AzDevOpsApiVersion -Default - - $apiVersion | Should -BeExactly $supportedApiVersion - } - } - - - # Effectively identical to 'When called with no parameter values' context (with test cases above) - Context 'When called with a "Default" switch parameter value of $false' { - - It 'Should not throw' { - - { Get-AzDevOpsApiVersion -Default:$false } | Should -Not -Throw - } - } - } - - - Context "When input parameters are invalid" { - - # N/A - Only the 'Default' switch parameter on this function/commands - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitIntervalMs.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitIntervalMs.Tests.ps1 deleted file mode 100644 index f79fa9e34..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitIntervalMs.Tests.ps1 +++ /dev/null @@ -1,51 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - [Int32]$expectedWaitIntervalMs = 500 - - - Context 'When input parameters are valid' { - - - Context 'When called with no parameter values' { - - It 'Should not throw' { - - { Get-AzDevOpsApiWaitIntervalMs } | Should -Not -Throw - } - - It "Should output a 'Int32' type containing an 'WaitIntervalMs' of '$expectedWaitIntervalMs'" { - - [Int32]$waitIntervalMs = Get-AzDevOpsApiWaitIntervalMs - - $waitIntervalMs | Should -BeExactly $expectedWaitIntervalMs - } - } - - } - - - Context "When input parameters are invalid" { - - # N/A - No parameters on this function/command - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitTimeoutMs.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitTimeoutMs.Tests.ps1 deleted file mode 100644 index 6f92ad0ab..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Get-AzDevOpsApiWaitTimeoutMs.Tests.ps1 +++ /dev/null @@ -1,51 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - [Int32]$expectedWaitTimeoutMs = 300000 - - - Context 'When input parameters are valid' { - - - Context 'When called with no parameter values' { - - It 'Should not throw' { - - { Get-AzDevOpsApiWaitTimeoutMs } | Should -Not -Throw - } - - It "Should output a 'Int32' type containing an 'WaitTimeoutMs' of '$expectedWaitTimeoutMs'" { - - [Int32]$waitTimeoutMs = Get-AzDevOpsApiWaitTimeoutMs - - $waitTimeoutMs | Should -BeExactly $expectedWaitTimeoutMs - } - } - - } - - - Context "When input parameters are invalid" { - - # N/A - No parameters on this function/command - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.tests.ps1 new file mode 100644 index 000000000..10eb829a8 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.tests.ps1 @@ -0,0 +1,78 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "ConvertTo-ACEList" -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'ConvertTo-ACEList.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + # Mock external functions + Mock -CommandName Find-Identity -MockWith { + return @{ + Identity = "MockIdentity" + } + } + + Mock -CommandName ConvertTo-ACETokenList -MockWith { + return @("MockPermission") + } + } + + It "should return a singular item of ACEs when valid parameters are provided" { + $result = ConvertTo-ACEList -SecurityNamespace "Namespace" -Permissions @( + @{ Identity = "User1"; Permission = "Read" } + ) -OrganizationName "MyOrg" + + $result | Should -Not -BeNullOrEmpty + $result.Identity.Identity | Should -Be "MockIdentity" + $result.Permissions | Should -Contain "MockPermission" + } + + It "should return multiple items of ACEs when valid parameters are provided" { + $result = ConvertTo-ACEList -SecurityNamespace "Namespace" -Permissions @( + @{ Identity = "User1"; Permission = "Read" }, + @{ Identity = "User2"; Permission = "Write" } + ) -OrganizationName "MyOrg" + + $result | Should -Not -BeNullOrEmpty + $result | Should -HaveCount 2 + $result[0].Identity.Identity | Should -Be "MockIdentity" + $result[0].Permissions | Should -Contain "MockPermission" + $result[1].Identity.Identity | Should -Be "MockIdentity" + $result[1].Permissions | Should -Contain "MockPermission" + } + + It "should log a warning if the identity is not found" { + Mock -CommandName Find-Identity -MockWith { return $null } + Mock -CommandName Write-Warning -Verifiable + + { ConvertTo-ACEList -SecurityNamespace "Namespace" -Permissions @(@{ Identity = "User1"; Permission = "Read" }) -OrganizationName "MyOrg" } | Should -Not -Throw + Assert-VerifiableMock + + } + + It "should log a warning if permissions are not found" { + Mock -CommandName ConvertTo-ACETokenList -MockWith { return $null } + Mock -CommandName Write-Warning -Verifiable + + $result = ConvertTo-ACEList -SecurityNamespace "Namespace" -Permissions @(@{ Identity = "User1"; Permission = "Read" }) -OrganizationName "MyOrg" + $result | Should -BeNullOrEmpty + Assert-VerifiableMock + } + + It "should handle empty permissions array gracefully" { + $result = ConvertTo-ACEList -SecurityNamespace "Namespace" -Permissions @() -OrganizationName "MyOrg" + + $result | Should -BeNullOrEmpty + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.tests.ps1 new file mode 100644 index 000000000..aaf16cfb0 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.tests.ps1 @@ -0,0 +1,84 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "ConvertTo-ACETokenList Tests" -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'ConvertTo-ACETokenList.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + # Cache Item + . (Get-ClassFilePath '000.CacheItem') + + Mock -CommandName Get-AzDoCacheObjects -MockWith { + return @('SecurityNamespaces') + } + + } + + BeforeEach { + # Mock Get-CacheItem to return a mock SecurityDescriptor + Mock -CommandName Get-CacheItem -MockWith { + return @{ + actions = @( + @{ displayName = "Read"; name = "read" }, + @{ displayName = "Write"; name = "write" }, + @{ displayName = "Execute"; name = "execute" } + ) + } + } + } + + It "should return an empty list when SecurityDescriptor is not found" { + # Mock to return $null for not found SecurityDescriptor + Mock -CommandName Get-CacheItem -MockWith { return $null } + Mock -CommandName Write-Error -Verifiable + + $result = ConvertTo-ACETokenList -SecurityNamespace "TestNamespace" -ACEPermissions @( + @{ "Read" = "Allow"; "Write" = "Deny" } + ) + + $result | Should -BeNullOrEmpty + Assert-VerifiableMock + } + + It "should correctly process Allow and Deny permissions" { + $acePermissions = @( + @{ "Read" = "Allow"; "Write" = "Deny" }, + @{ "Execute" = "Allow"; "Read" = "Deny" } + ) + $result = ConvertTo-ACETokenList -SecurityNamespace "TestNamespace" -ACEPermissions $acePermissions + + $result | Should -HaveCount 2 + $result[0].DescriptorType | Should -Be "TestNamespace" + $result[0].Allow.displayName | Should -Contain "Read" + $result[0].Deny.displayName | Should -Contain "Write" + $result[1].Allow.displayName | Should -Contain "Execute" + $result[1].Deny.displayName | Should -Contain "Read" + } + + It "should filter out permissions not found in the SecurityDescriptor" { + $acePermissions = @( + @{ "UnknownPermission" = "Allow"; "Read" = "Deny"; "Write" = "Deny" } + ) + + $result = ConvertTo-ACETokenList -SecurityNamespace "TestNamespace" -ACEPermissions $acePermissions + + $result | Should -HaveCount 1 + $result.Allow | Should -BeNullOrEmpty + $result.Deny.displayName | Should -Contain "Read" + $result.Deny.displayName | Should -Contain "Write" + } + + +} + +# End of Pester tests for ConvertTo-ACETokenList diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.tests.ps1 new file mode 100644 index 000000000..ac1813905 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.tests.ps1 @@ -0,0 +1,57 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "ConvertTo-ACL" -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'ConvertTo-ACL.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Write-Warning + Mock -CommandName New-ACLToken -MockWith { return @{ Token = "mockToken" } } + Mock -CommandName ConvertTo-ACEList -MockWith { + return @( + @{ Identity = "User1"; Permissions = "Read" }, + @{ Identity = "User2"; Permissions = "Read, Write" } + ) + } + Mock -CommandName Group-ACEs -MockWith { param($ACEs) return $ACEs } + + $permissions = @( + @{ + Identity = 'User1' + Permissions = 'Read' + }, + @{ + Identity = 'User2' + Permissions = 'Read', 'Write' + } + ) + + } + + It "should return an ACL with correct properties" { + $result = ConvertTo-ACL -Permissions $permissions -SecurityNamespace 'Namespace1' -isInherited $true -OrganizationName 'Org1' -TokenName 'Token1' + + $result | Should -Not -BeNullOrEmpty + $result.token.Token | Should -Be "mockToken" + $result.aces | Should -HaveCount 2 + $result.inherited | Should -Be $true + } + + It "should return a warning if no ACEs are created" { + Mock -CommandName ConvertTo-ACEList -MockWith { return @() } + $result = ConvertTo-ACL -Permissions $permissions -SecurityNamespace 'Namespace1' -isInherited $true -OrganizationName 'Org1' -TokenName 'Token1' + $result.aces | Should -HaveCount 0 + } +} + +# End of Pester tests for ConvertTo-ACL diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.tests.ps1 new file mode 100644 index 000000000..bffc54d4b --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.tests.ps1 @@ -0,0 +1,130 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Test to ensure the function handles the provided parameters correctly and returns the expected hashtable +Describe "ConvertTo-ACLHashtable" -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'ConvertTo-ACLHashtable.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + # Mock functions to simplify testing + Mock -CommandName Write-Verbose + Mock -CommandName ConvertTo-FormattedToken -MockWith { param ($Token) return $Token } + Mock -CommandName Get-BitwiseOrResult -MockWith { + param ($integers) + return $integers + } + + # Define the test cases + $referenceACLs = @( + [PSCustomObject]@{ + token = "token2" + inherited = $true + aces = @( + [PSCustomObject]@{ + permissions = @{ + allow = @{ + bit = 1 + } + deny = @{ + bit = 0 + } + } + Identity = @{ + value = @{ + ACLIdentity = @{ + descriptor = "descriptor1" + } + } + } + } + ) + } + ) + + $descriptorACLList = @( + [PSCustomObject]@{ + token = "token2" + inheritPermissions = $false + acesDictionary = @{ + descriptor2 = @{ + allow = 0 + deny = 1 + } + + } + }, + [PSCustomObject]@{ + token = "token3" + inheritPermissions = $true + acesDictionary = @{ + descriptor3 = @{ + allow = 1 + deny = 0 + } + } + } + ) + + $descriptorMatchToken = "token2" + Mock -CommandName Write-Warning + + } + + It "Correctly converts and builds the ACL hashtable" { + $result = ConvertTo-ACLHashtable -ReferenceACLs $referenceACLs -DescriptorACLList $descriptorACLList -DescriptorMatchToken $descriptorMatchToken + + $result.Count | Should -Be 2 + $result.value[0].token | Should -Be 'token3' + $result.value[0].inheritPermissions | Should -Be $true + $result.value[0].acesDictionary.descriptor3 | Should -Not -BeNullOrEmpty + $result.value[0].acesDictionary.descriptor3.allow | Should -Be 1 + $result.value[0].acesDictionary.descriptor3.deny | Should -Be 0 + + $result.value[1].token | Should -Be 'token2' + $result.value[1].inheritPermissions | Should -Be $true + $result.value[1].acesDictionary.descriptor1 | Should -Not -BeNullOrEmpty + $result.value[1].acesDictionary.descriptor1.allow | Should -Be 1 + $result.value[1].acesDictionary.descriptor1.deny | Should -Be 0 + + } + + It "Correctly converts and builds the ACL hashtable when the descriptor match token is not found" { + + $descriptorACLList = @( + [PSCustomObject]@{ + token = "token3" + inheritPermissions = $true + acesDictionary = @{ + descriptor3 = @{ + allow = 1 + deny = 0 + } + } + } + ) + + $result = ConvertTo-ACLHashtable -ReferenceACLs $referenceACLs -DescriptorACLList $descriptorACLList -DescriptorMatchToken 'token4' + + $result.Count | Should -Be 2 + $result.value[0].token | Should -Be 'token3' + $result.value[0].inheritPermissions | Should -Be $true + $result.value[1].token | Should -Be 'token2' + $result.value[1].inheritPermissions | Should -Be $true + + } +} + +# End of Pester tests for ConvertTo-ACLHashtable + +# Be sure you've installed and imported the Pester module version 5 before running this test. +# You can do so by running `Install-Module -Name Pester -Force`, then `Import-Module Pester`. diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.tests.ps1 new file mode 100644 index 000000000..1963a6c8a --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.tests.ps1 @@ -0,0 +1,141 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# ConvertTo-FormattedACL.Tests.ps1 + +Describe "ConvertTo-FormattedACL" -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'ConvertTo-FormattedACL.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Find-Identity -MockWith { return @{Id = "TestIdentity"} } + Mock -CommandName Format-ACEs -MockWith { return @{ Permissions = "TestPermissions" } } + Mock -CommandName Parse-ACLToken -MockWith { return "FormattedToken" } + Mock -CommandName Write-Warning + + } + + Context "When ACL has token and ACE entries" { + + BeforeAll { + + $ACLList = @( + @{ + inheritPermissions = $true + token = "TestToken" + acesDictionary = [PSCustomObject]@{ + "TestACE" = @{ + descriptor = "TestDescriptor" + allow = 2 + deny = 0 + } + } + }, + @{ + inheritPermissions = $true + token = "TestToken" + acesDictionary = [PSCustomObject]@{ + "TestACE" = @{ + descriptor = "TestDescriptor2" + allow = 2 + deny = 0 + } + } + } + ) + + } + + It "Should format ACL Git Repositories properly" { + $result = $ACLList | ConvertTo-FormattedACL -SecurityNamespace "Git Repositories" -OrganizationName "TestOrg" + $result.count | Should -Be 2 + $result[0].token | Should -Be "FormattedToken" + $result[0].aces | Should -HaveCount 1 + $result[0].aces[0].Identity.Id | Should -Be "TestIdentity" + $result[0].aces[0].Permissions.Permissions | Should -Be "TestPermissions" + } + + It "Should format ACL Identity properly" { + $result = $ACLList | ConvertTo-FormattedACL -SecurityNamespace "Identity" -OrganizationName "TestOrg" + $result.count | Should -Be 2 + $result[0].token | Should -Be "FormattedToken" + $result[0].aces | Should -HaveCount 1 + $result[0].aces[0].Identity.Id | Should -Be "TestIdentity" + $result[0].aces[0].Permissions.Permissions | Should -Be "TestPermissions" + } + } + + Context "When ACL has no token" { + + BeforeAll { + + $ACLList = @( + @{ + inheritPermissions = $true + token = $null + acesDictionary = [PSCustomObject]@{ + "TestACE" = @{ + descriptor = "TestDescriptor" + allow = 2 + deny = 0 + } + } + }, + @{ + inheritPermissions = $true + token = "" + acesDictionary = [PSCustomObject]@{ + "TestACE" = @{ + descriptor = "TestDescriptor2" + allow = 2 + deny = 0 + } + } + } + ) + + + } + + It "Should skip ACL without token" { + $result = $ACLList | ConvertTo-FormattedACL -SecurityNamespace "Git Repositories" -OrganizationName "TestOrg" + $result | Should -BeNullOrEmpty + } + } + + Context "When ACL has empty ACE entries" { + + BeforeAll { + + $ACLList = @( + @{ + inheritPermissions = $true + token = $null + acesDictionary = [PSCustomObject]@{ + "TestACE" = @{ + descriptor = "TestDescriptor" + allow = 2 + deny = 0 + } + } + } + ) + + } + + It "Should skip ACL with empty ACE entries" { + $result = $ACLList | ConvertTo-FormattedACL -SecurityNamespace "Identity" -OrganizationName "TestOrg" + $result | Should -BeNullOrEmpty + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.tests.ps1 new file mode 100644 index 000000000..e3727e34a --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.tests.ps1 @@ -0,0 +1,82 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# ConvertTo-FormattedToken.Tests.ps1 + +Describe "ConvertTo-FormattedToken" -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'ConvertTo-FormattedToken.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + } + + It "should format GitOrganization token correctly" { + $token = @{ + type = 'GitOrganization' + } + + Mock -CommandName ConvertTo-FormattedToken -ParameterFilter { $Token.type -eq 'GitOrganization' } -MockWith { return 'repoV2' } + + $result = ConvertTo-FormattedToken -Token $token + + $result | Should -Be 'repoV2' + } + + It "should format GitProject token correctly" { + $token = @{ + type = 'GitProject' + projectId = 'myProject' + } + + Mock -CommandName ConvertTo-FormattedToken -ParameterFilter { $Token.type -eq 'GitProject' -and $Token.projectId -eq 'myProject' } -MockWith { return 'repoV2/myProject' } + + $result = ConvertTo-FormattedToken -Token $token + + $result | Should -Be 'repoV2/myProject' + } + + It "should format GitRepository token correctly" { + $token = @{ + type = 'GitRepository' + projectId = 'myProject' + RepoId = 'myRepo' + } + + Mock -CommandName ConvertTo-FormattedToken -ParameterFilter { $Token.type -eq 'GitRepository' -and $Token.projectId -eq 'myProject' -and $Token.RepoId -eq 'myRepo' } -MockWith { return 'repoV2/myProject/myRepo' } + + $result = ConvertTo-FormattedToken -Token $token + + $result | Should -Be 'repoV2/myProject/myRepo' + } + + It "should return an empty string for unrecognized token type" { + $token = @{ + type = 'UnknownType' + } + + Mock -CommandName ConvertTo-FormattedToken -ParameterFilter { $Token.type -eq 'UnknownType' } -MockWith { return '' } + + $result = ConvertTo-FormattedToken -Token $token + + $result | Should -Be '' + } + + It "should return an empty string for an empty token" { + $token = @{} + + Mock -CommandName ConvertTo-FormattedToken -ParameterFilter { $null -eq $Token.type } -MockWith { return '' } + + $result = ConvertTo-FormattedToken -Token $token + + $result | Should -Be '' + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.tests.ps1 new file mode 100644 index 000000000..9af20dcef --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.tests.ps1 @@ -0,0 +1,60 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Format-ACEs' -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Format-ACEs.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'SecurityNamespace' } -MockWith { + return @{ + Key = 'SecurityNamespace' + Type = 'SecurityNamespaces' + Actions = @( + [PSCustomObject]@{ bit = 1; Name = 'Read' }, + [PSCustomObject]@{ bit = 2; Name = 'Write' } + ) + } + } + } + + It 'Returns Allow actions from the specified security namespace' { + $result = Format-ACEs -Allow 1 -Deny 0 -SecurityNamespace "SecurityNamespace" + + $result.Allow.bit | Should -Be 1 + $result.Allow.Name | Should -Be 'Read' + $result.Deny | Should -BeNullOrEmpty + $result.DescriptorType | Should -Be "SecurityNamespace" + } + + It 'Returns Deny actions from the specified security namespace' { + $result = Format-ACEs -Allow 0 -Deny 2 -SecurityNamespace "SecurityNamespace" + + $result.Allow | Should -BeNullOrEmpty + $result.Deny.bit | Should -Be 2 + $result.Deny.Name | Should -Be 'Write' + $result.DescriptorType | Should -Be "SecurityNamespace" + } + + It 'Returns both Allow and Deny actions from the specified security namespace' { + $result = Format-ACEs -Allow 1 -Deny 2 -SecurityNamespace "SecurityNamespace" + + $result.Allow.bit | Should -Be 1 + $result.Allow.Name | Should -Be 'Read' + $result.Deny.bit | Should -Be 2 + $result.Deny.Name | Should -Be 'Write' + $result.DescriptorType | Should -Be "SecurityNamespace" + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.tests.ps1 new file mode 100644 index 000000000..d6d610652 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.tests.ps1 @@ -0,0 +1,60 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-BitwiseOrResult' -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-BitwiseOrResult.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + } + + It 'should return 0 for an empty array' { + $result = Get-BitwiseOrResult -integers @() + $result | Should -Be 0 + } + + It 'should return a value for an array with a single item' { + $result = Get-BitwiseOrResult -integers 5 + $result | Should -Be 5 + } + + It 'should return the same number for a single-item array' { + $result = Get-BitwiseOrResult -integers @(5) + $result | Should -Be 5 + } + + It 'should return correct bitwise OR result for an array of positive integers' { + $result = Get-BitwiseOrResult -integers @(1, 2, 4, 8) + $result | Should -Be 15 + } + + It 'should handle large numbers correctly' { + $result = Get-BitwiseOrResult -integers @(2147483647, 1) + $result | Should -Be 2147483647 + } + + It 'should return 0 for an array with all zeros' { + $result = Get-BitwiseOrResult -integers @(0, 0, 0) + $result | Should -Be 0 + } + + It 'should write error and return null for an invalid integer' { + $invalidInteger = "abc" + { Get-BitwiseOrResult -integers @($invalidInteger) } | Should -Throw + } + + It 'should return correct result for mixture of valid integers' { + $result = Get-BitwiseOrResult -integers @(-1, 1) + $result | Should -Be -1 + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.tests.ps1 new file mode 100644 index 000000000..5ee91cbb8 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.tests.ps1 @@ -0,0 +1,102 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Group-ACEs' -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Group-ACEs.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Group-ACEs -MockWith { + param ($ACEs) + if ($ACEs.Count -eq 0) { + return @() + } elseif ($ACEs.Count -eq 1) { + return $ACEs + } else { + $groupedACEs = @{} + foreach ($ace in $ACEs) { + $id = $ace.Identity.value.originId + if (-not $groupedACEs.ContainsKey($id)) { + $groupedACEs[$id] = $ace + } + } + return $groupedACEs.Values + } + } + + $ace1 = @{ + Identity = @{ + value = @{ + originId = "user1" + } + } + Permissions = @{ + Deny = 0, 1 + Allow = 2, 3 + DescriptorType = "SecurityNamespace" + } + } + + $ace2 = @{ + Identity = @{ + value = @{ + originId = "user1" + } + } + Permissions = @{ + Deny = 0, 1 + Allow = 2, 3 + DescriptorType = "SecurityNamespace" + } + } + + $ace3 = @{ + Identity = @{ + value = @{ + originId = "user2" + } + } + Permissions = @{ + Deny = 0, 1 + Allow = 2, 3 + DescriptorType = "SecurityNamespace" + } + } + + } + + It 'Returns empty list when ACEs are not provided' { + $result = Group-ACEs -ACEs @() + $result | Should -BeNullOrEmpty + } + + It 'Processes single identity ACE' { + $result = Group-ACEs -ACEs @($ace1) + $result.Identity.value.originId | Should -Be "user1" + $result.Permissions.Deny | Should -Be 0,1 + $result.Permissions.Allow | Should -Be 2,3 + } + + It 'Groups multiple identities correctly' { + $result = Group-ACEs -ACEs @($ace1, $ace2, $ace3) + $result.Count | Should -Be 2 + $grouped = $result | Where-Object { $_.Identity.value.originId -eq "user1" } + $grouped.Permissions.Deny | Should -Be 0,1 + $grouped.Permissions.Allow | Should -Be 2,3 + } + + It "Doesn't group single identity ACE" { + $result = Group-ACEs -ACEs @($ace1) + @($result).Count | Should -Be 1 + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.tests.ps1 new file mode 100644 index 000000000..aa0722d9f --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.tests.ps1 @@ -0,0 +1,76 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'New-ACLToken Function Tests' -Skip -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-ACLToken.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + # Load 001.LocalizedDataAzResourceTokenPatten + . (Get-ClassFilePath '001.LocalizedDataAzResourceTokenPatten') + + Mock -CommandName Get-CacheItem -MockWith { + return [PSCustomObject]@{id = "1234"} + } + Mock -CommandName Write-Warning + + } + + Context 'Git Repositories Namespace' { + + It 'Should return GitOrganization type for valid Git organization token' { + $result = New-ACLToken -SecurityNamespace 'Git Repositories' -TokenName 'OrgName' + $result.type | Should -Be 'GitOrganization' + } + + It 'Should return GitProject type for valid Git project token' { + $result = New-ACLToken -SecurityNamespace 'Git Repositories' -TokenName '[OrgName]/ProjectName' + $result.type | Should -Be 'GitProject' + $result.projectId | Should -Be '1234' + } + + It 'Should return GitRepository type for valid Git repository token' { + $result = New-ACLToken -SecurityNamespace 'Git Repositories' -TokenName '[OrgName]/ProjectName/RepoName' + $result.type | Should -Be 'GitRepository' + $result.projectId | Should -Be '1234' + $result.RepoId | Should -Be '1234' + } + + It 'Should return GitUnknown type for unknown Git token' { + $result = New-ACLToken -SecurityNamespace 'Git Repositories' -TokenName 'Unknown/Token' + $result.type | Should -Be 'GitUnknown' + } + } + + Context 'Identity Namespace' { + + It 'Should return GitGroupPermission type for valid identity group token' { + $result = New-ACLToken -SecurityNamespace 'Identity' -TokenName '[ProjectId]/[GroupId]' + $result.type | Should -Be 'GitGroupPermission' + $result.projectId | Should -Be 'ProjectId' + $result.groupId | Should -Be 'GroupId' + } + + It 'Should return GroupUnknown type for unknown identity token' { + $result = New-ACLToken -SecurityNamespace 'Identity' -TokenName 'Unknown/Token' + $result.type | Should -Be 'GroupUnknown' + } + } + + Context 'Unknown SecurityNamespace' { + + It 'Should return UnknownSecurityNamespace type for unrecognized security namespace' { + $result = New-ACLToken -SecurityNamespace 'Unknown' -TokenName 'Any/Token' + $result.type | Should -Be 'UnknownSecurityNamespace' + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.tests.ps1 new file mode 100644 index 000000000..c690a0d64 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.tests.ps1 @@ -0,0 +1,83 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Parse-ACLToken' -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Parse-ACLToken.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + + $script:LocalizedDataAzACLTokenPatten = @{ + OrganizationGit = '^org:(.+)$' + GitProject = '^project:(.+)$' + GitRepository = '^repo:(.+)$' + GitBranch = '^branch:(.+)$' + ResourcePermission = '^resource:(.+)$' + GroupPermission = '^group:(.+)$' + } + + # If there were any Mock commands needed, they should be added here using the complete syntax. + # Example: + # Mock -CommandName SomeCommand -MockWith { + # return "mocked result" + # } + + } + + It 'Should parse OrganizationGit token correctly' { + $token = "org:testOrg" + $SecurityNamespace = "Git Repositories" + $result = Parse-ACLToken -Token $token -SecurityNamespace $SecurityNamespace + + $result.type | Should -Be 'OrganizationGit' + $result._token | Should -Be $token + } + + It 'Should parse GitProject token correctly' { + $token = "project:testProject" + $SecurityNamespace = "Git Repositories" + $result = Parse-ACLToken -Token $token -SecurityNamespace $SecurityNamespace + + $result.type | Should -Be 'GitProject' + $result._token | Should -Be $token + } + + It 'Should throw for unrecognized Git Repositories token' { + $token = "unknown:test" + $SecurityNamespace = "Git Repositories" + { Parse-ACLToken -Token $token -SecurityNamespace $SecurityNamespace } | Should -Throw "Token '$token' is not recognized." + } + + It 'Should parse ResourcePermission token correctly' { + $token = "resource:testResource" + $SecurityNamespace = "Identity" + $result = Parse-ACLToken -Token $token -SecurityNamespace $SecurityNamespace + + $result.type | Should -Be 'ResourcePermission' + $result._token | Should -Be $token + } + + It 'Should parse GroupPermission token correctly' { + $token = "group:testGroup" + $SecurityNamespace = "Identity" + $result = Parse-ACLToken -Token $token -SecurityNamespace $SecurityNamespace + + $result.type | Should -Be 'GroupPermission' + $result._token | Should -Be $token + } + + It 'Should throw for unrecognized Identity token' { + $token = "unknown:test" + $SecurityNamespace = "Identity" + { Parse-ACLToken -Token $token -SecurityNamespace $SecurityNamespace } | Should -Throw "Token '$token' is not recognized." + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.tests.ps1 new file mode 100644 index 000000000..5e0d98632 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.tests.ps1 @@ -0,0 +1,57 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Resolve-ACLToken' -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Resolve-ACLToken.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + $referenceObject = [PSCustomObject]@{ token = [PSCustomObject]@{ _token = 'refToken' } } + $differenceObject = [PSCustomObject]@{ token = [PSCustomObject]@{ _token = 'diffToken' } } + + # If there were any Mock commands needed, they should be added here using the complete syntax. + # Example: + # Mock -CommandName Resolve-ACLToken -MockWith { + # param ($ReferenceObject, $DifferenceObject) + # if ($DifferenceObject -ne $null) { + # return $DifferenceObject.token._token + # } + # elseif ($ReferenceObject -ne $null) { + # return $ReferenceObject.token._token + # } + # else { + # return $null + # } + # } + } + + Context 'When DifferenceObject is not null' { + It 'should return the token from DifferenceObject' { + $result = Resolve-ACLToken -ReferenceObject $referenceObject -DifferenceObject $differenceObject + $result | Should -Be 'diffToken' + } + } + + Context 'When DifferenceObject is null' { + It 'should return the token from ReferenceObject' { + $result = Resolve-ACLToken -ReferenceObject $referenceObject -DifferenceObject $null + $result | Should -Be 'refToken' + } + } + + Context 'When both DifferenceObject and ReferenceObject are null' { + It 'should return $null' { + $result = Resolve-ACLToken -ReferenceObject $null -DifferenceObject $null + $result | Should -Be $null + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.tests.ps1 new file mode 100644 index 000000000..f77401efc --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.tests.ps1 @@ -0,0 +1,279 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Test-ACLListforChanges" -Tags "Unit", "ACL", "Helper" { + + BeforeAll { + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-ACLListforChanges.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the classes to test + . (Get-ClassFilePath 'Get-BitwiseOrResult.ps1') + + # Mock Data + $ReferenceACLsSample = @{ + aces = @( + @{ + Identity = @{ + value = @{ + originId = 1 + } + } + Permissions = @{ + Allow = @{ + Bit = 1 + } + Deny = @{ + Bit = 0 + } + } + } + ) + isInherited = $False + } + + $DifferenceACLsSample = @{ + aces = @( + @{ + Identity = @{ + value = @{ + originId = 1 + } + } + Permissions = @{ + Allow = @{ + Bit = 1 + } + Deny = @{ + Bit = 0 + } + } + } + ) + isInherited = $False + } + + $ModifiedDifferenceACLsSample = @{ + aces = @( + @{ + Identity = @{ + value = @{ + originId = 1 + } + } + Permissions = @{ + Allow = @{ + Bit = 2 + } + Deny = @{ + Bit = 0 + } + } + isInherited = $False + } + ) + } + + } + + It "Returns Unchanged when ACLs are identical" { + $result = Test-ACLListforChanges -ReferenceACLs $ReferenceACLsSample -DifferenceACLs $DifferenceACLsSample + $result.status | Should -Be "Unchanged" + } + + It "Returns Changed when ACLs are different" { + $result = Test-ACLListforChanges -ReferenceACLs $ReferenceACLsSample -DifferenceACLs $ModifiedDifferenceACLsSample + $result.status | Should -Be "Changed" + } + + It "Returns Missing when Reference ACL is null" { + $result = Test-ACLListforChanges -ReferenceACLs $null -DifferenceACLs $DifferenceACLsSample + $result.status | Should -Be "Missing" + } + + It "Returns NotFound when Difference ACL is null" { + $result = Test-ACLListforChanges -ReferenceACLs $ReferenceACLsSample -DifferenceACLs $null + $result.status | Should -Be "NotFound" + } + + It "Returns Changed when ACLs count is not equal" { + $DifferentCountACLs = @{ + aces = @( + @{ + Identity = @{ + value = @{ + originId = 1 + } + } + Permissions = @{ + Allow = @{ + Bit = 1 + } + Deny = @{ + Bit = 0 + } + } + isInherited = $False + }, + @{ + Identity = @{ + value = @{ + originId = 2 + } + } + Permissions = @{ + Allow = @{ + Bit = 1 + } + Deny = @{ + Bit = 0 + } + } + isInherited = $False + } + ) + } + $result = Test-ACLListforChanges -ReferenceACLs $ReferenceACLsSample -DifferenceACLs $DifferentCountACLs + $result.status | Should -Be "Changed" + } + + It "Returns Changed when inherited flag is not equal" { + $InheritedFlagACLs = @{ + aces = @( + @{ + Identity = @{ + value = @{ + originId = 1 + } + } + Permissions = @{ + Allow = @{ + Bit = 1 + } + Deny = @{ + Bit = 0 + } + } + } + ) + isInherited = $True + } + + $result = Test-ACLListforChanges -ReferenceACLs $ReferenceACLsSample -DifferenceACLs $InheritedFlagACLs + $result.status | Should -Be "Changed" + + } + + It "Returns Changed when ACE is not found in Difference ACL" { + $MissingACEACLs = @{ + aces = @( + @{ + Identity = @{ + value = @{ + originId = 2 + } + } + Permissions = @{ + Allow = @{ + Bit = 1 + } + Deny = @{ + Bit = 0 + } + } + } + ) + isInherited = $False + } + + $result = Test-ACLListforChanges -ReferenceACLs $ReferenceACLsSample -DifferenceACLs $MissingACEACLs + $result.status | Should -Be "Changed" + } + + It "Returns Changed when Allow ACEs are not equal" { + $DifferentAllowACLs = @{ + aces = @( + @{ + Identity = @{ + value = @{ + originId = 1 + } + } + Permissions = @{ + Allow = @{ + Bit = 2 + } + Deny = @{ + Bit = 0 + } + } + } + ) + isInherited = $False + } + + $result = Test-ACLListforChanges -ReferenceACLs $ReferenceACLsSample -DifferenceACLs $DifferentAllowACLs + $result.status | Should -Be "Changed" + } + + It "Returns Changed when Deny ACEs are not equal" { + $DifferentDenyACLs = @{ + aces = @( + @{ + Identity = @{ + value = @{ + originId = 1 + } + } + Permissions = @{ + Allow = @{ + Bit = 1 + } + Deny = @{ + Bit = 1 + } + } + } + ) + isInherited = $False + } + + $result = Test-ACLListforChanges -ReferenceACLs $ReferenceACLsSample -DifferenceACLs $DifferentDenyACLs + $result.status | Should -Be "Changed" + } + + It "Returns Changed when ACLs are different" { + $DifferentACLs = @{ + aces = @( + @{ + Identity = @{ + value = @{ + originId = 1 + } + } + Permissions = @{ + Allow = @{ + Bit = 2 + } + Deny = @{ + Bit = 1 + } + } + } + ) + isInherited = $False + } + + $result = Test-ACLListforChanges -ReferenceACLs $ReferenceACLsSample -DifferenceACLs $DifferentACLs + $result.status | Should -Be "Changed" + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.tests.ps1 new file mode 100644 index 000000000..58e649927 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.tests.ps1 @@ -0,0 +1,68 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Test-AzDevOpsApiHttpRequestHeader' { + + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-AzDevOpsApiHttpRequestHeader.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + } + + + It 'Returns $true when HttpRequestHeader contains Metadata' { + $header = @{ Metadata = 'someValue' } + $result = Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $header -IsValid + $result | Should -Be $true + } + + It 'Returns $true when HttpRequestHeader.Authorization is a valid Basic auth' { + $header = @{ Authorization = 'Basic: dXNlcm5hbWU6cGFzc3dvcmQ=' } + $result = Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $header -IsValid + $result | Should -Be $true + } + + It 'Returns $true when HttpRequestHeader.Authorization is a valid Bearer token' { + $header = @{ Authorization = 'Bearer: yourTokenHere' } + $result = Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $header -IsValid + $result | Should -Be $true + } + + It 'Returns $true when HttpRequestHeader.Authorization is a valid Bearer token with space and no colon' { + $header = @{ Authorization = 'Bearer yourTokenHere' } + $result = Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $header -IsValid + $result | Should -Be $true + } + + It 'Returns $false when HttpRequestHeader.Authorization is null' { + $header = @{ Authorization = $null } + $result = Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $header -IsValid + $result | Should -Be $false + } + + It 'Returns $false when HttpRequestHeader is null' { + $header = $null + $result = Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $header -IsValid + $result | Should -Be $false + } + + It 'Returns $false when HttpRequestHeader.Authorization is invalid' { + $header = @{ Authorization = 'InvalidAuthString' } + $result = Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $header -IsValid + $result | Should -Be $false + } + + It 'Throws exception when IsValid switch is missing' -skip { + $header = @{ Authorization = 'Bearer: yourTokenHere' } + { Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $header } | Should -Throw + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.tests.ps1 new file mode 100644 index 000000000..bc6c46218 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.tests.ps1 @@ -0,0 +1,36 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Test-AzDevOpsApiResourceId' { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-AzDevOpsApiResourceId.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + } + + It 'Returns $true for a valid ResourceId' { + $ValidResourceId = [guid]::NewGuid().ToString() + $result = Test-AzDevOpsApiResourceId -ResourceId $ValidResourceId -IsValid + $result | Should -Be $true + } + + It 'Returns $false for an invalid ResourceId' { + $InvalidResourceId = 'Invalid-GUID' + $result = Test-AzDevOpsApiResourceId -ResourceId $InvalidResourceId -IsValid + $result | Should -Be $false + } + + It 'Throws exception if IsValid switch is not provided' -skip { + $ValidResourceId = [guid]::NewGuid().ToString() + { Test-AzDevOpsApiResourceId -ResourceId $ValidResourceId } | Should -Throw + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.tests.ps1 new file mode 100644 index 000000000..ee2f9ff96 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.tests.ps1 @@ -0,0 +1,48 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Test-AzDevOpsApiTimeoutExceeded" { + + BeforeAll { + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Test-AzDevOpsApiTimeoutExceeded.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + } + + It "Should return true if duration exceeds TimeoutMs" { + $StartTime = [datetime]::Now + $EndTime = $StartTime.AddMilliseconds(1200) + $TimeoutMs = 1000 + $result = Test-AzDevOpsApiTimeoutExceeded -StartTime $StartTime -EndTime $EndTime -TimeoutMs $TimeoutMs + $result | Should -Be $true + } + + It "Should return false if duration does not exceed TimeoutMs" { + $StartTime = [datetime]::Now + $EndTime = $StartTime.AddMilliseconds(800) + $TimeoutMs = 1000 + $result = Test-AzDevOpsApiTimeoutExceeded -StartTime $StartTime -EndTime $EndTime -TimeoutMs $TimeoutMs + $result | Should -Be $false + } + + It "Should validate the TimeoutMs parameter against its range" { + $StartTime = [datetime]::Now + $EndTime = $StartTime.AddMilliseconds(3000) + $TimeoutMs = 200000 + {Test-AzDevOpsApiTimeoutExceeded -StartTime $StartTime -EndTime $EndTime -TimeoutMs $TimeoutMs} | Should -Not -Throw + } + + It "Should return false if duration equals TimeoutMs" { + $StartTime = [datetime]::Now + $EndTime = $StartTime.AddMilliseconds(1000) + $TimeoutMs = 1000 + $result = Test-AzDevOpsApiTimeoutExceeded -StartTime $StartTime -EndTime $EndTime -TimeoutMs $TimeoutMs + $result | Should -Be $false + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.tests.ps1 new file mode 100644 index 000000000..d364c6cb2 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.tests.ps1 @@ -0,0 +1,46 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Test-AzDevOpsApiUri' { + + BeforeAll { + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-AzDevOpsApiUri.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + } + + It 'Returns $true if ApiUri is null or empty' { + $result = Test-AzDevOpsApiUri -ApiUri '' -IsValid + $result | Should -Be $true + } + + It 'Returns $true for a valid HTTP URI when -IsValid is used' { + $result = Test-AzDevOpsApiUri -ApiUri 'http://example.com/_apis/' -IsValid + $result | Should -Be $true + } + + It 'Returns $true for a valid HTTPS URI when -IsValid is used' { + $result = Test-AzDevOpsApiUri -ApiUri 'https://example.com/_apis/' -IsValid + $result | Should -Be $true + } + + It 'Returns $false for an invalid HTTP URI when -IsValid is used' { + $result = Test-AzDevOpsApiUri -ApiUri 'http://example.com/invalid' -IsValid + $result | Should -Be $false + } + + It 'Returns $false for an invalid HTTPS URI when -IsValid is used' { + $result = Test-AzDevOpsApiUri -ApiUri 'https://example.com/invalid' -IsValid + $result | Should -Be $false + } + + It 'Throws an exception if -IsValid is not used' -skip { + { Test-AzDevOpsApiUri -ApiUri 'http://example.com/_apis/' } | Should -Throw + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.tests.ps1 new file mode 100644 index 000000000..4fe196070 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.tests.ps1 @@ -0,0 +1,28 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Test-AzDevOpsApiVersion' { + BeforeAll { + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-AzDevOpsApiVersion.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + } + + It 'Should return $true for supported API version' { + $result = Test-AzDevOpsApiVersion -ApiVersion '6.0' -IsValid + $result | Should -Be $true + } + + It 'Should return $false for unsupported API version' { + $result = Test-AzDevOpsApiVersion -ApiVersion '5.0' -IsValid + $result | Should -Be $false + } + +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.tests.ps1 new file mode 100644 index 000000000..8ea659353 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.tests.ps1 @@ -0,0 +1,82 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Wait-AzDevOpsApiResource' -skip { + + BeforeAll { + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Wait-AzDevOpsApiResource.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName 'Test-AzDevOpsApiUri' -MockWith { return $true } + Mock -CommandName 'Test-AzDevOpsApiVersion' -MockWith { return $true } + Mock -CommandName 'Test-AzDevOpsPat' -MockWith { return $true } + Mock -CommandName 'Test-AzDevOpsApiResourceName' -MockWith { return $true } + Mock -CommandName 'Test-AzDevOpsApiResourceId' -MockWith { return $true } + Mock -CommandName 'Get-AzDevOpsApiVersion' -MockWith { return '6.0' } + Mock -CommandName 'Get-AzDevOpsApiWaitIntervalMs' -MockWith { return 1000 } + Mock -CommandName 'Get-AzDevOpsApiWaitTimeoutMs' -MockWith { return 30000 } + Mock -CommandName 'Test-AzDevOpsApiResource' -MockWith { return $false } + Mock -CommandName 'Test-AzDevOpsApiTimeoutExceeded' -MockWith { return $false } + Mock -CommandName 'New-InvalidOperationException' -MockWith { Throw "Operation Timeout" } + + } + + Context 'When waiting for resource to be present' { + It 'Waits for the resource to be present' { + $script:localizedData = @{ + AzDevOpsApiResourceWaitTimeoutExceeded = 'Timeout exceeded waiting for {0} resource {1} with ID {2} after {3} milliseconds.' + } + $params = @{ + ApiUri = 'https://dev.azure.com/example/_apis/' + Pat = 'dummyPAT' + ResourceName = 'Project' + ResourceId = 'dummyResourceId' + IsPresent = $true + WaitIntervalMilliseconds = 1000 + WaitTimeoutMilliseconds = 5000 + } + + { Wait-AzDevOpsApiResource @params } | Should -Not -Throw + } + } + + Context 'When waiting for resource to be absent' { + It 'Waits for the resource to be absent' { + Mock -CommandName 'Test-AzDevOpsApiResource' -MockWith { return $true } + $script:localizedData = @{ + AzDevOpsApiResourceWaitTimeoutExceeded = 'Timeout exceeded waiting for {0} resource {1} with ID {2} after {3} milliseconds.' + } + $params = @{ + ApiUri = 'https://dev.azure.com/example/_apis/' + Pat = 'dummyPAT' + ResourceName = 'Project' + ResourceId = 'dummyResourceId' + IsAbsent = $true + WaitIntervalMilliseconds = 1000 + WaitTimeoutMilliseconds = 5000 + } + + { Wait-AzDevOpsApiResource @params } | Should -Not -Throw + } + } + + Context 'When both IsPresent and IsAbsent are missing' { + It 'Throws an error' { + $params = @{ + ApiUri = 'https://dev.azure.com/example/_apis/' + Pat = 'dummyPAT' + ResourceName = 'Project' + ResourceId = 'dummyResourceId' + } + + { Wait-AzDevOpsApiResource @params } | Should -Throw + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.tests.ps1 new file mode 100644 index 000000000..2416d53d7 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.tests.ps1 @@ -0,0 +1,54 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "ConvertTo-Base64String" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "ConvertTo-Base64String.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + } + + It "should convert a string to a Base64 string" { + $input = "Hello, World!" + $expected = "SGVsbG8sIFdvcmxkIQ==" + + $result = ConvertTo-Base64String -InputObject $input + + $result | Should -Be $expected + } + + It "should throw an error for null input" { + { ConvertTo-Base64String -InputObject $null } | Should -Throw + } + + It "should throw an error for empty input" { + { ConvertTo-Base64String -InputObject "" } | Should -Throw + } + + It "should handle special characters correctly" { + $input = "!@#$%^&*()_+|" + $expected = "IUAjJCVeJiooKV8rfA==" + + $result = ConvertTo-Base64String -InputObject $input + + $result | Should -Be $expected + } + + It "should handle non-ASCII characters correctly" { + $input = "こんにちは" + $expected = "44GT44KT44Gr44Gh44Gv" + + $result = ConvertTo-Base64String -InputObject $input + + $result | Should -Be $expected + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.tests.ps1 new file mode 100644 index 000000000..7f42f2b3d --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.tests.ps1 @@ -0,0 +1,185 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Find-AzDoIdentity" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Find-AzDoIdentity.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + . (Get-ClassFilePath '000.CacheItem') + + # Mocking Get-CacheItem to simulate cache retrieval + Mock -CommandName Get-CacheItem -MockWith { + param ($Key, $Type) + + switch ($Type) + { + 'LiveUsers' + { + if ($Key -eq 'user@domain.com') + { + return @{ + ACLIdentity = @{ + descriptor = "userDescriptor" + id = "userId" + } + originId = "userOriginId" + principalName = "userPrincipalName" + displayName = "User Display Name" + } + } + return $null + } + 'LiveGroups' + { + if ($Key -eq '[Project]\GroupName') + { + return @{ + ACLIdentity = @{ + descriptor = "groupDescriptor" + id = "groupId" + } + originId = "groupOriginId" + principalName = "groupPrincipalName" + displayName = "Group Display Name" + } + } + return $null + } + } + } + + Mock Write-Verbose + Mock Write-Warning + + $Global:AZDOLiveUsers = @( + @{ + key = "user1" + value = @{ + ACLIdentity = @{ + descriptor = "descriptor1" + id = "id1" + } + originId = "originId1" + principalName = "principalName1" + displayName = "User One" + } + }, + @{ + key = "user2" + value = @{ + ACLIdentity = @{ + descriptor = "descriptor2" + id = "id2" + } + originId = "originId2" + principalName = "principalName2" + displayName = "User Two" + } + } + ) + + $Global:AZDOLiveGroups = @( + @{ + key = "group1" + value = @{ + ACLIdentity = @{ + descriptor = "descriptor1" + id = "id1" + } + originId = "originId1" + principalName = "principalName1" + displayName = "Group One" + } + }, + @{ + key = "group2" + value = @{ + ACLIdentity = @{ + descriptor = "descriptor2" + id = "id2" + } + originId = "originId2" + principalName = "principalName2" + displayName = "Group Two" + } + } + ) + } + + It "Should find user by email address" { + $result = Find-AzDoIdentity -Identity 'user@domain.com' + $result.ACLIdentity.descriptor | Should -Be 'userDescriptor' + } + + It "Should find group by name with backslash" { + $result = Find-AzDoIdentity -Identity 'Project\GroupName' + $result.ACLIdentity.descriptor | Should -Be 'groupDescriptor' + } + + It "Should find group by name with forward slash" { + $result = Find-AzDoIdentity -Identity 'Project/GroupName' + $result.ACLIdentity.descriptor | Should -Be 'groupDescriptor' + } + + It "Should find user by display name" { + $result = Find-AzDoIdentity -Identity 'User One' + $result.displayName | Should -Be 'User One' + } + + It "Should find group by display name" { + $result = Find-AzDoIdentity -Identity 'Group One' + $result.displayName | Should -Be 'Group One' + } + + It "Should handle multiple users with the same display name" { + Mock -CommandName 'Where-Object' -MockWith { + param ($condition) + return @($Global:AZDOLiveUsers[0], $Global:AZDOLiveUsers[1]) + } + $result = Find-AzDoIdentity -Identity 'User One' + $result | Should -BeNullOrEmpty + } + + It "Should handle multiple groups with the same display name" { + Mock -CommandName 'Where-Object' -MockWith { + param ($condition) + return @($Global:AZDOLiveGroups[0], $Global:AZDOLiveGroups[1]) + } + + $result = Find-AzDoIdentity -Identity 'Group One' + $result | Should -BeNullOrEmpty + } + + It "Should handle both user and group with the same display name" { + Mock -CommandName 'Where-Object' -MockWith { + param ($condition) + if ($condition -like "*User One*") { + return $Global:AZDOLiveUsers[0] + } elseif ($condition -like "*Group One*") { + return $Global:AZDOLiveGroups[0] + } + } + + $result = Find-AzDoIdentity -Identity 'User One' + $result | Should -BeNullOrEmpty + } + + It "Should return null if no identity found" { + $result = Find-AzDoIdentity -Identity 'NonExistent' + $result | Should -BeNullOrEmpty + } + + It "Should throw identity is null" { + { Find-AzDoIdentity -Identity $null } | Should -Throw + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 new file mode 100644 index 000000000..54777eca1 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 @@ -0,0 +1,118 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Unit Tests for Find-Identity function +Describe 'Find-Identity Function Tests' { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Find-Identity.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + # Mock Get-CacheObject to return test data + Mock -CommandName Get-CacheObject -MockWith { + param ( + [string]$CacheType + ) + + switch ($CacheType) + { + 'LiveGroups' { + return @{ value = [PSCustomObject]@{ ACLIdentity = [PSCustomObject]@{ descriptor = 'groupDescriptor'; id = 'groupId'; originId = 'groupOrigin'; principalName = 'groupPrincipal'; displayName = 'groupDisplay' } } } + } + 'LiveUsers' { + return @{ value = [PSCustomObject]@{ ACLIdentity = [PSCustomObject]@{ descriptor = 'userDescriptor'; id = 'userId'; originId = 'userOrigin'; principalName = 'userPrincipal'; displayName = 'userDisplay' } } } + } + 'LiveServicePrinciples' { + return @{ value = [PSCustomObject]@{ ACLIdentity = [PSCustomObject]@{ descriptor = 'spDescriptor'; id = 'spId'; originId = 'spOrigin'; principalName = 'spPrincipal'; displayName = 'spDisplay' } } } + } + } + } + + # Mock Get-DevOpsDescriptorIdentity to return test identity + Mock -CommandName Get-DevOpsDescriptorIdentity -MockWith { + param ( + [string]$OrganizationName, + [string]$Descriptor + ) + + return [PSCustomObject]@{ ACLIdentity = [PSCustomObject]@{ descriptor = 'apiDescriptor'; id = 'apiId'; originId = 'apiOrigin'; principalName = 'apiPrincipal'; displayName = 'apiDisplay' } } + } + + Mock Write-Verbose + Mock Write-Warning + + } + + Context "when searching the existing cache" { + + It 'Should return group identity for valid group descriptor' { + $result = Find-Identity -Name 'groupDescriptor' -OrganizationName 'TestOrg' -SearchType 'descriptor' + + $result.value.ACLIdentity.descriptor | Should -Be 'groupDescriptor' + } + + It 'Should return user identity for valid user descriptor' { + $result = Find-Identity -Name 'userDescriptor' -OrganizationName 'TestOrg' -SearchType 'descriptor' + + $result.value.ACLIdentity.descriptor | Should -Be 'userDescriptor' + } + + It 'Should return null for multiple identities with the same name' { + Mock -CommandName Get-CacheObject -MockWith { + @{ + value = [PSCustomObject]@{ ACLIdentity = [PSCustomObject]@{ descriptor = 'duplicateDescriptor'; id = 'duplicateId' } } + }, @{ + value = [PSCustomObject]@{ ACLIdentity = [PSCustomObject]@{ descriptor = 'duplicateDescriptor'; id = 'duplicateId' } } + } + } + + $result = Find-Identity -Name 'duplicateDescriptor' -OrganizationName 'TestOrg' -SearchType 'descriptor' + + $result | Should -BeNullOrEmpty + } + + } + + Context "when searching the API" { + + It 'Should return null for non-existent descriptor' { + + Mock -CommandName Get-DevOpsDescriptorIdentity -MockWith { + return $null + } + + $result = Find-Identity -Name 'nonExistentDescriptor' -OrganizationName 'TestOrg' -SearchType 'descriptor' + $result | Should -BeNullOrEmpty + } + + It 'Should attempt to search the cache again using the ID this time' { + Mock Write-Warning -Verifiable + Mock Write-Verbose -Verifiable + Mock -CommandName Get-DevOpsDescriptorIdentity -MockWith { + return @{ + id = 'groupId' + descriptor = 'mockDescriptor' + } + } + + $result = Find-Identity -Name 'unknownName' -OrganizationName 'TestOrg' -SearchType 'descriptor' + + Assert-MockCalled Get-DevOpsDescriptorIdentity + $result.value.ACLIdentity.descriptor | Should -Be 'groupDescriptor' + Assert-VerifiableMock + } + + } + + + + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 new file mode 100644 index 000000000..7ab3a9d9c --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 @@ -0,0 +1,63 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Format-AzDoGroup' { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Format-AzDoGroup.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + } + + Context 'Formatting UPN' { + It 'Should format correctly with valid inputs' { + Mock -CommandName Format-AzDoGroup -MockWith { + param ( + [string]$Prefix, + [string]$GroupName + ) + + return "[{0}]\{1}" -f $Prefix.Trim('[]'), $GroupName + } + + $result = Format-AzDoGroup -Prefix "Contoso" -GroupName "Developers" + $result | Should -Be "[Contoso]\Developers" + } + + It 'Should remove starting/ending square brackets from Prefix' { + Mock -CommandName Format-AzDoGroup -MockWith { + param ( + [string]$Prefix, + [string]$GroupName + ) + + return "[{0}]\{1}" -f $Prefix.Trim('[]'), $GroupName + } + + $result = Format-AzDoGroup -Prefix "[Contoso]" -GroupName "Developers" + $result | Should -Be "[Contoso]\Developers" + } + + It 'Should remove starting/ending square brackets from Prefix' { + Mock -CommandName Format-AzDoGroup -MockWith { + param ( + [string]$Prefix, + [string]$GroupName + ) + + return "[{0}]\{1}" -f $Prefix.Trim('[]'), $GroupName + } + + $result = Format-AzDoGroup -Prefix "[Contoso]" -GroupName "Developers" + $result | Should -Be "[Contoso]\Developers" + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.tests.ps1 new file mode 100644 index 000000000..760ecda39 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.tests.ps1 @@ -0,0 +1,59 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Format-AzDoProjectName' { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Format-AzDoProjectName.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Write-Verbose + + } + + Context 'When GroupName is already formatted' { + It 'Returns the same GroupName' { + $result = Format-AzDoProjectName -GroupName '[ProjectName]\GroupName' -OrganizationName 'OrgName' + $result | Should -Be '[ProjectName]\GroupName' + } + } + + Context 'When GroupName needs formatting' { + It 'Formats correctly with given organization name' { + $result = Format-AzDoProjectName -GroupName 'ProjectName/GroupName' -OrganizationName 'OrgName' + $result | Should -Be '[ProjectName]\GroupName' + } + + It 'Throws if the given format has insufficient parts' { + { Format-AzDoProjectName -GroupName 'GroupName' -OrganizationName 'OrgName' } | Should -Throw + } + + It 'Replaces %ORG% with given organization name' { + $result = Format-AzDoProjectName -GroupName '%ORG%\GroupName' -OrganizationName 'OrgName' + $result | Should -Be '[OrgName]\GroupName' + } + + It 'Replaces %TFS% with TEAM FOUNDATION' { + $result = Format-AzDoProjectName -GroupName '%TFS%\GroupName' -OrganizationName 'OrgName' + $result | Should -Be '[TEAM FOUNDATION]\GroupName' + } + + It 'Throws if group part is empty' { + { Format-AzDoProjectName -GroupName 'ProjectName\' -OrganizationName 'OrgName' } | Should -Throw + } + + It 'Trims leading and trailing spaces' { + $result = Format-AzDoProjectName -GroupName ' ProjectName / GroupName ' -OrganizationName 'OrgName' + $result | Should -Be '[ProjectName]\GroupName' + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.tests.ps1 new file mode 100644 index 000000000..6825763ee --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.tests.ps1 @@ -0,0 +1,36 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Format-DescriptorType' { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Format-DescriptorType.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + } + + + It 'returns "Git Repositories" for DescriptorType "GitRepositories"' { + $result = Format-DescriptorType -DescriptorType 'GitRepositories' + $result | Should -Be 'Git Repositories' + } + + It 'returns the same value for DescriptorType "APIServices"' { + $result = Format-DescriptorType -DescriptorType 'APIServices' + $result | Should -Be 'APIServices' + } + + It 'returns the same value for DescriptorType "Webhooks"' { + $result = Format-DescriptorType -DescriptorType 'Webhooks' + $result | Should -Be 'Webhooks' + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiHttpRequestHeader.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiHttpRequestHeader.tests.ps1 new file mode 100644 index 000000000..a52587068 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiHttpRequestHeader.tests.ps1 @@ -0,0 +1,52 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-AzDevOpsApiHttpRequestHeader' { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Get-AzDevOpsApiHttpRequestHeader.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + Mock -CommandName Test-AzDevOpsPat -MockWith { + param ( + [string]$Pat + ) + return $true + } + + } + + Context 'when called with valid PAT' { + It 'should return a hashtable with Authorization header' { + $Pat = 'ValidPAT' + $ExpectedHeader = @{ + Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$Pat")) + } + + $Result = Get-AzDevOpsApiHttpRequestHeader -Pat $Pat + + $Result | Should -BeOfType 'Hashtable' + $Result['Authorization'] | Should -BeExactly $ExpectedHeader['Authorization'] + } + } + + Context 'when called with invalid PAT' { + It 'should throw a validation exception' { + + Mock -CommandName Test-AzDevOpsPat -MockWith { + return $false + } + + { Get-AzDevOpsApiHttpRequestHeader -Pat 'InvalidPAT' } | Should -Throw + + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceName.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceName.tests.ps1 new file mode 100644 index 000000000..968195920 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceName.tests.ps1 @@ -0,0 +1,24 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-AzDevOpsApiResourceName' { + + BeforeAll { + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Get-AzDevOpsApiResourceName.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + } + + It 'Should return expected resource names' { + $expected = @('Operation', 'Project') + $result = Get-AzDevOpsApiResourceName + $result | Should -Be $expected + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceUri.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceUri.tests.ps1 new file mode 100644 index 000000000..413660662 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceUri.tests.ps1 @@ -0,0 +1,46 @@ +$currentFile = $MyInvocation.MyCommand.Path +# Not used +Describe 'Get-AzDevOpsApiResourceUri' -skip { + Mock Get-AzDevOpsApiVersion { return '6.0' } + Mock Test-AzDevOpsApiUri { return $true } + Mock Test-AzDevOpsApiVersion { return $true } + Mock Test-AzDevOpsApiResourceName { return $true } + Mock Test-AzDevOpsApiResourceId { return $true } + Mock Get-AzDevOpsApiUriAreaName { param($ResourceName) return 'defaultarea' } + Mock Get-AzDevOpsApiUriResourceName { param($ResourceName) return $ResourceName } + + Context 'When called with mandatory parameters only' { + It 'Should return a valid URI with default API version' { + $result = Get-AzDevOpsApiResourceUri -ApiUri 'https://dev.azure.com/someOrg/_apis/' -ResourceName 'Project' + $expectedUri = 'https://dev.azure.com/someOrg/_apis/defaultarea/Project/?&api-version=6.0&includeCapabilities=true' + $result | Should -Be $expectedUri + } + } + + Context 'When called with ResourceId' { + It 'Should include the ResourceId in the returned URI' { + $result = Get-AzDevOpsApiResourceUri -ApiUri 'https://dev.azure.com/someOrg/_apis/' -ResourceName 'Project' -ResourceId '12345' + $expectedUri = 'https://dev.azure.com/someOrg/_apis/defaultarea/Project/12345/?&api-version=6.0&includeCapabilities=true' + $result | Should -Be $expectedUri + } + } + + Context 'When called with custom ApiVersion' { + It 'Should include the custom ApiVersion in the returned URI' { + $result = Get-AzDevOpsApiResourceUri -ApiUri 'https://dev.azure.com/someOrg/_apis/' -ResourceName 'Project' -ApiVersion '5.0' + $expectedUri = 'https://dev.azure.com/someOrg/_apis/defaultarea/Project/?&api-version=5.0&includeCapabilities=true' + $result | Should -Be $expectedUri + } + } + + Context 'When ResourceName is in core area' { + Mock Get-AzDevOpsApiUriAreaName { param($ResourceName) return 'core' } + + It 'Should not append area name to URI' { + $result = Get-AzDevOpsApiResourceUri -ApiUri 'https://dev.azure.com/someOrg/_apis/' -ResourceName 'Project' + $expectedUri = 'https://dev.azure.com/someOrg/_apis/Project/?&api-version=6.0&includeCapabilities=true' + $result | Should -Be $expectedUri + } + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriAreaName.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriAreaName.tests.ps1 new file mode 100644 index 000000000..6fca0f7e4 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriAreaName.tests.ps1 @@ -0,0 +1,46 @@ +$currentFile = $MyInvocation.MyCommand.Path +# Not used +Describe 'Get-AzDevOpsApiUriAreaName' -skip { + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Get-AzDevOpsApiUriAreaName.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + } + + Context 'When ResourceName is provided and valid' { + It 'Should return corresponding URI-specific area name' { + $result = Get-AzDevOpsApiUriAreaName -ResourceName 'Project' + $result | Should -Be 'core' + } + + It 'Should return another URI-specific area name' { + $result = Get-AzDevOpsApiUriAreaName -ResourceName 'Profile' + $result | Should -Be 'profile' + } + } + + Context 'When no ResourceName is provided' { + It 'Should return all unique URI-specific area names' { + $result = Get-AzDevOpsApiUriAreaName + $result | Should -Contain 'core' + $result | Should -Contain 'profile' + $result.Count | Should -Be 2 + } + } + + Context 'When invalid ResourceName is provided' { + It 'Should throw validation exception' { + { Get-AzDevOpsApiUriAreaName -ResourceName 'Invalid' } | Should -Throw + } + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriResourceName.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriResourceName.tests.ps1 new file mode 100644 index 000000000..50f13b795 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriResourceName.tests.ps1 @@ -0,0 +1,45 @@ +$currentFile = $MyInvocation.MyCommand.Path +# Ignore the file. It is not used. +Describe 'Get-AzDevOpsApiUriResourceName Tests' -skip { + + BeforeEach { + function Test-AzDevOpsApiResourceName { + param ($ResourceName, $IsValid) + return $true + } + } + + Context 'When ResourceName is provided' { + It 'Should return correct URI-specific resource name for "Project"' { + $result = Get-AzDevOpsApiUriResourceName -ResourceName 'Project' + $result | Should -Be 'projects' + } + + It 'Should return correct URI-specific resource name for "Operation"' { + $result = Get-AzDevOpsApiUriResourceName -ResourceName 'Operation' + $result | Should -Be 'operations' + } + } + + Context 'When ResourceName is not provided' { + It 'Should return all URI-specific resource names' { + $result = Get-AzDevOpsApiUriResourceName + $expectedResult = @('operations', 'projects') + $result | Should -Be $expectedResult + } + } + + Context 'When ResourceName is invalid' { + BeforeEach { + function Test-AzDevOpsApiResourceName { + param ($ResourceName, $IsValid) + return $false + } + } + + It 'Should not validate and throw an error' { + { Get-AzDevOpsApiUriResourceName -ResourceName 'InvalidResource' } | Should -Throw + } + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.tests.ps1 new file mode 100644 index 000000000..1ca0622cd --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.tests.ps1 @@ -0,0 +1,31 @@ +$currentFile = $MyInvocation.MyCommand.Path +Describe 'Get-AzDevOpsApiVersion Tests' { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Get-AzDevOpsApiVersion.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + } + + It 'Should return all supported API versions when no parameters are specified' { + $result = Get-AzDevOpsApiVersion + $result.count | Should -BeGreaterThan 1 + } + + It 'Should return default API version when -Default is specified' { + $expected = '7.0-preview.1' + $result = Get-AzDevOpsApiVersion -Default + $result | Should -Be $expected + } + +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitIntervalMs.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitIntervalMs.tests.ps1 new file mode 100644 index 000000000..6dcd8cde2 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitIntervalMs.tests.ps1 @@ -0,0 +1,28 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Get-AzDevOpsApiWaitIntervalMs" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Get-AzDevOpsApiWaitIntervalMs.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + } + + It "Should return an integer" { + $result = Get-AzDevOpsApiWaitIntervalMs + $result | Should -BeOfType 'System.Int32' + } + + It "Should return 500" { + $result = Get-AzDevOpsApiWaitIntervalMs + $result | Should -Be 500 + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitTimeoutMs.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitTimeoutMs.tests.ps1 new file mode 100644 index 000000000..6b90d0338 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitTimeoutMs.tests.ps1 @@ -0,0 +1,23 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Get-AzDevOpsApiWaitTimeoutMs" { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Get-AzDevOpsApiWaitTimeoutMs.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + } + + It "Should return 300000 milliseconds" { + $result = Get-AzDevOpsApiWaitTimeoutMs + $result | Should -Be 300000 + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.tests.ps1 new file mode 100644 index 000000000..3f84ff4c5 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.tests.ps1 @@ -0,0 +1,43 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-AzDoCacheObjects' { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Get-AzDoCacheObjects.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + } + + It 'Returns an array with 13 elements' { + $result = Get-AzDoCacheObjects + $result.Length | Should -Be 13 + } + + It 'Contains expected elements' { + $expectedElements = @( + 'Project', + 'Team', + 'Group', + 'SecurityDescriptor', + 'LiveGroups', + 'LiveProjects', + 'LiveUsers', + 'LiveGroupMembers', + 'LiveRepositories', + 'LiveServicePrinciples', + 'LiveACLList', + 'LiveProcesses', + 'SecurityNamespaces' + ) + $result = Get-AzDoCacheObjects + $result | Should -Be $expectedElements + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.tests.ps1 new file mode 100644 index 000000000..e6ea0bc84 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.tests.ps1 @@ -0,0 +1,179 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Invoke-AzDevOpsApiRestMethod' { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "Invoke-AzDevOpsApiRestMethod.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + # Get 007.APIRateLimit.ps1 + . (Get-ClassFilePath '007.APIRateLimit') + + $defaultParameters = @{ + ApiUri = 'https://dev.azure.com/someOrganizationName/_apis/' + HttpMethod = 'Get' + HttpHeaders = @{} + RetryAttempts = 1 + RetryIntervalMs = 250 + } + + Mock -CommandName Test-AzDevOpsApiHttpRequestHeader -MockWith { return $true } + Mock -CommandName Get-AzDevOpsApiVersion -MockWith { return '6.0-preview.1' } + Mock -CommandName Add-AuthenticationHTTPHeader -MockWith { return $null } + + # Define a custom exception class + class CustomException : System.Exception { + + [System.Net.WebExceptionStatus]$Status + [HashTable]$Response + + CustomException([string]$message, [System.Net.WebExceptionStatus]$status, + [HashTable]$httpWebResponse, [System.Net.HttpStatusCode]$statusCode) : base($message) { + $this.Status = $status + $this.Response = @{ + StatusCode = $statusCode + Headers = $httpWebResponse + } + } + } + + } + + Context 'Basic functionality' { + + BeforeAll { + Mock -CommandName Invoke-RestMethod -MockWith { + param ( + [string]$Uri, + [string]$Method, + [hashtable]$Headers + ) + # Default mock behavior can be defined here if needed. + } + } + + It 'should call Invoke-RestMethod with correct parameters' { + Invoke-AzDevOpsApiRestMethod @defaultParameters + Assert-MockCalled -CommandName Invoke-RestMethod -Exactly -Times 1 + } + + It 'should return results from Invoke-RestMethod' { + Mock -CommandName Invoke-RestMethod -MockWith { return @{ success = $true } } + $result = Invoke-AzDevOpsApiRestMethod @defaultParameters + $result | Should -BeOfType [System.Collections.Hashtable] + $result.success | Should -Be $true + } + } + + Context 'Retry mechanism' { + It 'should retry if Invoke-RestMethod throws' { + Mock -CommandName Start-Sleep + Mock -CommandName Invoke-RestMethod -MockWith { throw "Error" } + $parameters = $defaultParameters.Clone() + $parameters.RetryAttempts = 2 + + { Invoke-AzDevOpsApiRestMethod @parameters } | Should -Throw + Assert-MockCalled -CommandName Invoke-RestMethod -Exactly -Times 3 + } + + It 'should wait between retries' { + Mock -CommandName Start-Sleep -Verifiable + Mock -CommandName Invoke-RestMethod -MockWith { throw "Error" } + $parameters = $defaultParameters.Clone() + $parameters.RetryAttempts = 2 + + { Invoke-AzDevOpsApiRestMethod @parameters } | Should -Throw + Assert-MockCalled -CommandName Start-Sleep -Exactly -Times 3 + } + } + + Context 'Continuation token handling' { + + AfterAll { + Remove-Variable -Name ResponseHeaders -Scope Global -ErrorAction SilentlyContinue + } + + It 'should handle continuation tokens and loop until no token is found' { + + # First call + Mock -CommandName Invoke-RestMethod -ParameterFilter { $Uri -notlike '*continuationToken*' } -MockWith { + Set-Variable responseHeaders -Value @{ 'x-ms-continuationtoken' = 'token' } -Scope Global + return @{ success = $true } + } -Verifiable + + # Second call + Mock -CommandName Invoke-RestMethod -ParameterFilter { $Uri -like '*continuationToken*' } -MockWith { + Remove-Variable -Name ResponseHeaders -Scope Global -ErrorAction SilentlyContinue + return @{ success = $true } + } -Verifiable + + $parameters = $defaultParameters.Clone() + $result = Invoke-AzDevOpsApiRestMethod @parameters + + Assert-MockCalled -CommandName Invoke-RestMethod -Times 2 + $result | Should -BeOfType [System.Collections.Hashtable] + $result.Count | Should -Be 2 + } + } + + Context 'HTTP 429 Handling' { + + AfterAll { + Remove-Variable -Name TooManyRequestsFlag -Scope Global -ErrorAction SilentlyContinue + Remove-Variable -Name DSCAZDO_APIRateLimit -Scope Global -ErrorAction SilentlyContinue + } + + It 'should handle HTTP 429 and retry with appropriate delay' { + + Mock -CommandName Write-Verbose + Mock -CommandName Write-Warning + + Mock -CommandName Invoke-RestMethod -MockWith { + Set-Variable TooManyRequestsFlag -Value $true -Scope Global + + Throw [CustomException]::New( + "Too Many Requests", + [System.Net.WebExceptionStatus]::ProtocolError, + @{ "Retry-After" = 1 }, + [System.Net.HttpStatusCode]::TooManyRequests + ) + + } -ParameterFilter { + $null -eq $Global:TooManyRequestsFlag + } + + Mock -CommandName Invoke-RestMethod -MockWith { + Remove-Variable -Name TooManyRequestsFlag -Scope Global + return @{ success = $true } + } -ParameterFilter { + $Global:TooManyRequestsFlag -eq $true + } + + Mock -CommandName Start-Sleep -Verifiable + + $parameters = $defaultParameters.Clone() + $parameters.RetryAttempts = 2 + + $result = Invoke-AzDevOpsApiRestMethod @parameters + + $result | Should -BeOfType [System.Collections.Hashtable] + Assert-MockCalled -CommandName Write-Verbose -ParameterFilter { + $Message -like '*Too Many Requests*' + } + Assert-MockCalled -CommandName Write-Verbose -ParameterFilter { + $Message -like '*seconds before retrying*' + } + Assert-MockCalled -CommandName Start-Sleep -Times 1 + + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.tests.ps1 new file mode 100644 index 000000000..ecf4bb866 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.tests.ps1 @@ -0,0 +1,55 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Unit Tests for New-AzDevOpsACLToken function +Describe 'New-AzDevOpsACLToken' -Skip { + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath "New-AzDevOpsACLToken.tests.ps1" + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + ForEach ($file in $files) { + . $file.FullName + } + + } + + Context 'When TeamId is provided' { + It 'Should create a team-level access token' { + $OrganizationName = "Contoso" + $ProjectId = "MyProject" + $TeamId = "MyTeam" + $expectedToken = "vstfs:///Classification/TeamProject/$ProjectId/$TeamId" + + $result = New-AzDevOpsACLToken -OrganizationName $OrganizationName -ProjectId $ProjectId -TeamId $TeamId + + $result | Should -Be $expectedToken + } + } + + Context 'When TeamId is not provided' { + It 'Should create a project-level access token' { + $OrganizationName = "Contoso" + $ProjectId = "MyProject" + $expectedToken = "vstfs:///Classification/TeamProject/$ProjectId" + + $result = New-AzDevOpsACLToken -OrganizationName $OrganizationName -ProjectId $ProjectId + + $result | Should -Be $expectedToken + } + } + + Context 'When required parameters are missing' { + It 'Should throw an error if OrganizationName is missing' { + { New-AzDevOpsACLToken -ProjectId "MyProject" } | Should -Throw + } + + It 'Should throw an error if ProjectId is missing' { + { New-AzDevOpsACLToken -OrganizationName "Contoso" } | Should -Throw + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.tests.ps1 new file mode 100644 index 000000000..b10158b9c --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.tests.ps1 @@ -0,0 +1,53 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'New-InvalidOperationException' -Skip { + + BeforeAll { + + Mock -CommandName New-InvalidOperationException -MockWith { + param ( + [string]$Message, + [switch]$Throw + ) + + if (-not $Message) { + throw [System.Management.Automation.ParameterBindingValidationException]::new("Message parameter cannot be null or empty") + } + + $errorRecord = [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($Message), + "InvalidOperation", + [System.Management.Automation.ErrorCategory]::ConnectionError, + $null + ) + + if ($Throw) { + throw $errorRecord + } + + return $errorRecord + } + + } + + It 'Should return an ErrorRecord when given a valid message' { + $message = 'An error occurred' + $result = New-InvalidOperationException -Message $message + $result | Should -BeOfType [System.Management.Automation.ErrorRecord] + $result.Exception.Message | Should -BeExactly $message + $result.CategoryInfo.Category | Should -Be [System.Management.Automation.ErrorCategory]::ConnectionError + } + + It 'Should throw an ErrorRecord when -Throw is specified' { + $message = 'An error occurred' + { New-InvalidOperationException -Message $message -Throw } | Should -Throw -ExceptionType [System.Management.Automation.ErrorRecord] + } + + It 'Should fail if Message parameter is null' { + { New-InvalidOperationException -Message $null } | Should -Throw -ExceptionType [System.Management.Automation.ParameterBindingValidationException] + } + + It 'Should fail if Message parameter is empty' { + { New-InvalidOperationException -Message '' } | Should -Throw -ExceptionType [System.Management.Automation.ParameterBindingValidationException] + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.tests.ps1 new file mode 100644 index 000000000..2b535f50f --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.tests.ps1 @@ -0,0 +1,56 @@ +# Not implemented +Describe "PreCommandLookupAction" -skip { + BeforeAll { + $global:ExecutionContext = [pscustomobject]@{ + InvokeCommand = [pscustomobject]@{ + PreCommandLookupAction = $null + } + } + } + + It "Should throw if Add-AuthenticationHTTPHeader is called outside of Invoke-AzDevOpsApiRestMethod" { + $customCommand = [pscustomobject]@{ Name = "SomeOtherFunction" } + $global:MyInvocation = [pscustomobject]@{ MyCommand = $customCommand } + + { + & $global:ExecutionContext.InvokeCommand.PreCommandLookupAction.Invoke("Add-AuthenticationHTTPHeader", $null) + } | Should -Throw "The function 'Add-AuthenticationHTTPHeader' can only be called inside of 'Invoke-AzDevOpsApiRestMethod' function." + } + + It "Should not throw if Add-AuthenticationHTTPHeader is called inside of Invoke-AzDevOpsApiRestMethod" { + $customCommand = [pscustomobject]@{ Name = "Invoke-AzDevOpsApiRestMethod" } + $global:MyInvocation = [pscustomobject]@{ MyCommand = $customCommand } + + { + & $global:ExecutionContext.InvokeCommand.PreCommandLookupAction.Invoke("Add-AuthenticationHTTPHeader", $null) + } | Should -Not -Throw + } + + It "Should throw if Export- command is used within Invoke-AzDevOpsApiRestMethod function" { + $customCommand = [pscustomobject]@{ Name = "Invoke-AzDevOpsApiRestMethod" } + $global:MyInvocation = [pscustomobject]@{ MyCommand = $customCommand } + + { + & $global:ExecutionContext.InvokeCommand.PreCommandLookupAction.Invoke("Export-SomeData", $null) + } | Should -Throw "The command 'Export-SomeData' is not allowed to be used within 'Invoke-AzDevOpsApiRestMethod' function." + } + + It "Should throw if System.Runtime.InteropServices.Marshal is used outside of AuthenticationToken class" { + $customCommand = [pscustomobject]@{ Name = "SomeOtherClass" } + $global:MyInvocation = [pscustomobject]@{ MyCommand = $customCommand } + + { + & $global:ExecutionContext.InvokeCommand.PreCommandLookupAction.Invoke("System.Runtime.InteropServices.Marshal", $null) + } | Should -Throw "The command 'System.Runtime.InteropServices.Marshal' is not allowed to be used outside of 'AuthenticationToken' class." + } + + It "Should not throw if System.Runtime.InteropServices.Marshal is used inside AuthenticationToken class" { + $customCommand = [pscustomobject]@{ Name = "AuthenticationToken" } + $global:MyInvocation = [pscustomobject]@{ MyCommand = $customCommand } + + { + & $global:ExecutionContext.InvokeCommand.PreCommandLookupAction.Invoke("System.Runtime.InteropServices.Marshal", $null) + } | Should -Not -Throw + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.tests.ps1 new file mode 100644 index 000000000..421108c02 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.tests.ps1 @@ -0,0 +1,46 @@ +Describe 'New-Thread' -skip { + BeforeAll { + function New-Thread { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ScriptBlock]$ScriptBlock + ) + + Write-Verbose "[New-Thread] Started." + $thread = [System.Threading.Thread]::new($ScriptBlock) + $thread.Start() + Write-Verbose "[New-Thread] Completed." + return $thread + } + } + + It 'Throws an error when ScriptBlock is not provided' { + { New-Thread } | Should -Throw + } + + It 'Creates and starts a new thread' { + $scriptBlock = { Start-Sleep -Seconds 3 } + $thread = New-Thread -ScriptBlock $scriptBlock + $thread | Should -BeOfType 'System.Threading.Thread' + $thread.IsAlive | Should -Be $true + } + + It 'Thread runs the provided ScriptBlock' { + $hasRun = $false + $scriptBlock = { $script:hasRun = $true } + $thread = New-Thread -ScriptBlock $scriptBlock + $thread.Join() + $hasRun | Should -Be $true + } + + It 'Verbose output is produced' { + $verboseOutput = { + $scriptBlock = { Start-Sleep -Seconds 1 } + New-Thread -ScriptBlock $scriptBlock -Verbose + } | Out-String + $verboseOutput | Should -Contain '[New-Thread] Started.' + $verboseOutput | Should -Contain '[New-Thread] Completed.' + } +} + diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.Tests.ps1 deleted file mode 100644 index 6d9a19585..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Invoke-AzDevOpsApiRestMethod.Tests.ps1 +++ /dev/null @@ -1,351 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - # Get default, parameter values - $defaultHttpContentType = 'application/json' - $defaultHttpBody = '' - $defaultRetryAttempts = 5 - $defaultRetryIntervalMs = 250 - - # Mock functions called in function - Mock Invoke-RestMethod {} - # Mock New-InvalidOperationException {} # Do not mock - Mock Start-Sleep {} - - # Generate valid, test cases - $testCasesValidApiUris = Get-TestCase -ScopeName 'ApiUri' -TestCaseName 'Valid' - $testCasesValidHttpMethods = Get-TestCase -ScopeName 'HttpMethod' -TestCaseName 'Valid' - $testCasesValidHttpRequestHeaders = Get-TestCase -ScopeName 'HttpRequestHeader' -TestCaseName 'Valid' - $testCasesValidApiUriHttpMethodHttpRequestHeaders = Join-TestCaseArray -TestCaseArray @( - $testCasesValidApiUris, - $testCasesValidHttpMethods, - $testCasesValidHttpRequestHeaders) -Expand - $testCasesValidApiUriHttpMethodHttpRequestHeaders3 = $testCasesValidApiUriHttpMethodHttpRequestHeaders | Select-Object -First 3 - - # Generate invalid, test cases - $testCasesInvalidApiUris = Get-TestCase -ScopeName 'ApiUri' -TestCaseName 'Invalid' - $testCasesInvalidHttpMethods = Get-TestCase -ScopeName 'HttpMethod' -TestCaseName 'Invalid' - $testCasesInvalidHttpRequestHeaders = Get-TestCase -ScopeName 'HttpRequestHeader' -TestCaseName 'Invalid' - $testCasesInvalidApiUriHttpMethodHttpRequestHeaders = Join-TestCaseArray -TestCaseArray @( - $testCasesInvalidApiUris, - $testCasesInvalidHttpMethods, - $testCasesInvalidHttpRequestHeaders) -Expand - $testCasesInvalidApiUriHttpMethodHttpRequestHeaders3 = $testCasesInvalidApiUriHttpMethodHttpRequestHeaders | Select-Object -First 3 - - - Context 'When input parameters are valid' { - - Context 'When called just with mandatory, "ApiUri", "HttpMethod" and "HttpRequestHeader" parameters' { - - It 'Should not throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader } | Should -Not -Throw - } - - It 'Should output nothing/null - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - $output = Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader - - $output | Should -BeNullOrEmpty - } - - It 'Should invoke "Invoke-RestMethod" exactly once - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - Mock Invoke-RestMethod {} -Verifiable - - Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader - - Assert-MockCalled Invoke-RestMethod -Times 1 -Exactly -Scope 'It' - } - - Context 'When "Invoke-RestMethod" throws an exception on every retry' { - Mock Invoke-RestMethod { throw "Some exception" } - - It 'Should invoke "Invoke-RestMethod" number of times equal to "RetryAttempts" parameter value + 1 - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - Mock Invoke-RestMethod { throw "Some exception" } -Verifiable - Mock New-InvalidOperationException {} - - Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader - - Assert-MockCalled Invoke-RestMethod -Times $($defaultRetryAttempts+1) -Exactly -Scope 'It' - } - - - It 'Should invoke "Start-Sleep" number of times equal to "RetryAttempts" parameter value + 1 - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - Mock Start-Sleep { } -Verifiable - - Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader - - Assert-MockCalled Start-Sleep -Times $($defaultRetryAttempts+1) -Exactly -Scope 'It' - } - - - It 'Should invoke "New-InvalidOperationException" exactly once - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - Mock New-InvalidOperationException {} -Verifiable - - Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader - - Assert-MockCalled New-InvalidOperationException -Times 1 -Exactly -Scope 'It' - } - - } - - } - } - - - Context 'When input parameters are invalid' { - - Context 'When called without mandatory, "ApiUri" parameter' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader } | Should -Throw - } - - } - - Context 'When called without mandatory, "HttpMethod" parameter' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $null -HttpRequestHeader $HttpRequestHeader } | Should -Throw - } - - } - - Context 'When called without mandatory, "ApiUri" and "HttpMethod" parameters' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $null -HttpRequestHeader $HttpRequestHeader } | Should -Throw - } - - } - - Context 'When called without mandatory, "HttpRequestHeader" parameter' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $null } | Should -Throw - } - - } - - Context 'When called without mandatory, "ApiUri" and "HttpRequestHeader" parameters' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $HttpMethod -HttpRequestHeader $null } | Should -Throw - } - - } - - Context 'When called without mandatory, "HttpMethod" and "HttpRequestHeader" parameters' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $null -HttpRequestHeader $null } | Should -Throw - } - - } - - Context 'When called without mandatory, "ApiUri", "HttpMethod" and "HttpRequestHeader" parameters' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $null -HttpRequestHeader $null } | Should -Throw - } - - } - } - } -} -# Existing code... - -Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - # Existing code... - - Context 'When input parameters are valid' { - - # Existing code... - - Context 'When called just with mandatory, "ApiUri", "HttpMethod" and "HttpRequestHeader" parameters' { - - # Existing code... - - It 'Should not throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader } | Should -Not -Throw - } - - It 'Should output nothing/null - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - $output = Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader - - $output | Should -BeNullOrEmpty - } - - It 'Should invoke "Invoke-RestMethod" exactly once - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - Mock Invoke-RestMethod {} -Verifiable - - Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader - - Assert-MockCalled Invoke-RestMethod -Times 1 -Exactly -Scope 'It' - } - - Context 'When "Invoke-RestMethod" throws an exception on every retry' { - Mock Invoke-RestMethod { throw "Some exception" } - - It 'Should invoke "Invoke-RestMethod" number of times equal to "RetryAttempts" parameter value + 1 - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - Mock Invoke-RestMethod { throw "Some exception" } -Verifiable - Mock New-InvalidOperationException {} - - Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader - - Assert-MockCalled Invoke-RestMethod -Times $($defaultRetryAttempts+1) -Exactly -Scope 'It' - } - - - It 'Should invoke "Start-Sleep" number of times equal to "RetryAttempts" parameter value + 1 - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - Mock Start-Sleep { } -Verifiable - - Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader - - Assert-MockCalled Start-Sleep -Times $($defaultRetryAttempts+1) -Exactly -Scope 'It' - } - - - It 'Should invoke "New-InvalidOperationException" exactly once - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - Mock New-InvalidOperationException {} -Verifiable - - Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader - - Assert-MockCalled New-InvalidOperationException -Times 1 -Exactly -Scope 'It' - } - - } - - } - } - - - Context 'When input parameters are invalid' { - - # Existing code... - - Context 'When called without mandatory, "ApiUri" parameter' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $HttpMethod -HttpRequestHeader $HttpRequestHeader } | Should -Throw - } - - } - - Context 'When called without mandatory, "HttpMethod" parameter' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $null -HttpRequestHeader $HttpRequestHeader } | Should -Throw - } - - } - - Context 'When called without mandatory, "ApiUri" and "HttpMethod" parameters' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $null -HttpRequestHeader $HttpRequestHeader } | Should -Throw - } - - } - - Context 'When called without mandatory, "HttpRequestHeader" parameter' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $HttpMethod -HttpRequestHeader $null } | Should -Throw - } - - } - - Context 'When called without mandatory, "ApiUri" and "HttpRequestHeader" parameters' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $HttpMethod -HttpRequestHeader $null } | Should -Throw - } - - } - - Context 'When called without mandatory, "HttpMethod" and "HttpRequestHeader" parameters' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $ApiUri -HttpMethod $null -HttpRequestHeader $null } | Should -Throw - } - - } - - Context 'When called without mandatory, "ApiUri", "HttpMethod" and "HttpRequestHeader" parameters' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriHttpMethodHttpRequestHeaders3 { - param ([System.String]$ApiUri, [System.String]$HttpMethod, [Hashtable]$HttpRequestHeader) - - { Invoke-AzDevOpsApiRestMethod -ApiUri $null -HttpMethod $null -HttpRequestHeader $null } | Should -Throw - } - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.Tests.ps1 deleted file mode 100644 index deb54d585..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiHttpRequestHeader.Tests.ps1 +++ /dev/null @@ -1,107 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesValidHttpRequestHeaders = Get-TestCase -ScopeName 'HttpRequestHeader' -TestCaseName 'Valid' - $testCasesInvalidHttpRequestHeaders = Get-TestCase -ScopeName 'HttpRequestHeader' -TestCaseName 'Invalid' - - - Context 'When input parameters are valid' { - - - Context 'When called with "HttpRequestHeader" parameter value and the "IsValid" switch' { - - - Context 'When "HttpRequestHeader" parameter value is a valid "HttpRequestHeader"' { - - It 'Should not throw - ""' -TestCases $testCasesValidHttpRequestHeaders { - param ([Hashtable]$HttpRequestHeader) - - { Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $HttpRequestHeader -IsValid } | Should -Not -Throw - } - - It 'Should return $true - ""' -TestCases $testCasesValidHttpRequestHeaders { - param ([Hashtable]$HttpRequestHeader) - - Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $HttpRequestHeader -IsValid | Should -BeTrue - } - } - - - Context 'When "HttpRequestHeader" parameter value is an invalid "HttpRequestHeader"' { - - It 'Should not throw - ""' -TestCases $testCasesInvalidHttpRequestHeaders { - param ([Hashtable]$HttpRequestHeader) - - { Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $HttpRequestHeader -IsValid } | Should -Not -Throw - } - - It 'Should return $false - ""' -TestCases $testCasesInvalidHttpRequestHeaders { - param ([Hashtable]$HttpRequestHeader) - - Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $HttpRequestHeader -IsValid | Should -BeFalse - } - } - } - } - - - Context "When input parameters are invalid" { - - - Context 'When called with no/null parameter values/switches' { - - It 'Should throw' { - - { Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader:$null -IsValid:$false } | Should -Throw - } - } - - - Context 'When "HttpRequestHeader" parameter value is a valid "HttpRequestHeader"' { - - - Context 'When called with "HttpRequestHeader" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesValidHttpRequestHeaders { - param ([Hashtable]$HttpRequestHeader) - - { Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $HttpRequestHeader -IsValid:$false } | Should -Throw - } - } - } - - - Context 'When "HttpRequestHeader" parameter value is an invalid "HttpRequestHeader"' { - - - Context 'When called with "HttpRequestHeader" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesInvalidHttpRequestHeaders { - param ([Hashtable]$HttpRequestHeader) - - { Test-AzDevOpsApiHttpRequestHeader -HttpRequestHeader $HttpRequestHeader -IsValid:$false } | Should -Throw - } - } - } - - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResource.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResource.Tests.ps1 deleted file mode 100644 index 161d2add0..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResource.Tests.ps1 +++ /dev/null @@ -1,165 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - - # Mock functions called in function - Mock Get-AzDevOpsApiResource {} - - # Generate valid, test cases - $testCasesValidApiUris = Get-TestCase -ScopeName 'ApiUri' -TestCaseName 'Valid' - $testCasesValidPats = Get-TestCase -ScopeName 'Pat' -TestCaseName 'Valid' - $testCasesValidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Valid' - $testCasesValidApiUriPatResourceNames = Join-TestCaseArray -TestCaseArray @( - $testCasesValidApiUris, - $testCasesValidPats, - $testCasesValidResourceNames) -Expand - $testCasesValidApiUriPatResourceNames3 = $testCasesValidApiUriPatResourceNames | Select-Object -First 3 - - $validApiVersion = Get-TestCaseValue -ScopeName 'ApiVersion' -TestCaseName 'Valid' -First 1 - $validResourceId = Get-TestCaseValue -ScopeName 'ResourceId' -TestCaseName 'Valid' -First 1 - - # Generate invalid, test cases - $testCasesInvalidApiUris = Get-TestCase -ScopeName 'ApiUri' -TestCaseName 'Invalid' - $testCasesInvalidPats = Get-TestCase -ScopeName 'Pat' -TestCaseName 'Invalid' - $testCasesInvalidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Invalid' - $testCasesInvalidApiUriPatResourceNames = Join-TestCaseArray -TestCaseArray @( - $testCasesInvalidApiUris, - $testCasesInvalidPats, - $testCasesInvalidResourceNames) -Expand - $testCasesInvalidApiUriPatResourceNames3 = $testCasesInvalidApiUriPatResourceNames | Select-Object -First 3 - - $invalidApiVersion = Get-TestCaseValue -ScopeName 'ApiVersion' -TestCaseName 'Invalid' -First 1 - $invalidResourceId = Get-TestCaseValue -ScopeName 'ResourceId' -TestCaseName 'Invalid' -First 1 - - - Context 'When input parameters are valid' { - - - Context 'When called with mandatory, "ApiUri", "Pat", "ResourceName" and "ResourceId" parameters' { - - Context 'When the "ResourceId" parameter value is invalid' { - - It 'Should throw - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Test-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $invalidResourceId } | Should -Throw - } - } - - Context 'When the "ResourceId" parameter value is valid' { - - It 'Should not throw - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Test-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $validResourceId } | Should -Not -Throw - } - - It 'Should return a type of "boolean" - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - $output = Test-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $validResourceId - - $output | Should -BeOfType [boolean] - } - - It 'Should invoke "Get-AzDevOpsApiResource" only once - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - Mock Get-AzDevOpsApiResource {} -Verifiable - - Test-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $validResourceId | Out-Null - - Assert-MockCalled 'Get-AzDevOpsApiResource' -Times 1 -Exactly -Scope 'It' - } - - - Context 'When the resource exists' { - - It 'Should return $true - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - Mock Get-AzDevOpsApiResource { - return [System.Management.Automation.PSObject[]]@( - [System.Management.Automation.PSObject]@{ - id = '9a7ee4cf-7fa7-40e1-a3c0-1d0aacdaad92' - }, - [System.Management.Automation.PSObject]@{ - id = 'db79312c-8231-48b7-9967-db1bad53c881' - } - ) - } - - Test-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $validResourceId | Should -BeTrue - } - - } - - - Context 'When the resource does not exist' { - - It 'Should return $false - "", "", ""' -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - Mock Get-AzDevOpsApiResource { - return [System.Management.Automation.PSObject[]]@() - } - - Test-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName -ResourceId $validResourceId | Should -BeFalse - } - - } - - } - - Context "When also called with valid 'ApiVersion' parameter value" { - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Test-AzDevOpsApiResource -ApiUri $ApiUri -ApiVersion $validApiVersion -Pat $Pat -ResourceName $ResourceName -ResourceId $validResourceId } | Should -Not -Throw - } - } - - Context "When also called with invalid 'ApiVersion' parameter value" { - - It "Should throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceNames3 { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Test-AzDevOpsApiResource -ApiUri $ApiUri -ApiVersion $invalidApiVersion -Pat $Pat -ResourceName $ResourceName -ResourceId $validResourceId } | Should -Throw - } - } - } - } - - - Context 'When input parameters are invalid' { - - Context 'When called with mandatory, "ApiUri", "Pat" and "ResourceName" parameters' { - - It 'Should throw - "", "", ""' -TestCases $testCasesinvalidApiUriPatResourceNames { - param ([System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceName) - - { Test-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName $ResourceName } | Should -Throw - } - } - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceId.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceId.Tests.ps1 deleted file mode 100644 index 0951e05da..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceId.Tests.ps1 +++ /dev/null @@ -1,107 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesValidResourceIds = Get-TestCase -ScopeName 'ResourceId' -TestCaseName 'Valid' - $testCasesInvalidResourceIds = Get-TestCase -ScopeName 'ResourceId' -TestCaseName 'Invalid' - - - Context 'When input parameters are valid' { - - - Context 'When called with "ResourceId" parameter value and the "IsValid" switch' { - - - Context 'When "ResourceId" parameter value is a valid "ResourceId"' { - - It 'Should not throw - ""' -TestCases $testCasesValidResourceIds { - param ([System.String]$ResourceId) - - { Test-AzDevOpsApiResourceId -ResourceId $ResourceId -IsValid } | Should -Not -Throw - } - - It 'Should return $true - ""' -TestCases $testCasesValidResourceIds { - param ([System.String]$ResourceId) - - Test-AzDevOpsApiResourceId -ResourceId $ResourceId -IsValid | Should -BeTrue - } - } - - - Context 'When "ResourceId" parameter value is an invalid "ResourceId"' { - - It 'Should not throw - ""' -TestCases $testCasesInvalidResourceIds { - param ([System.String]$ResourceId) - - { Test-AzDevOpsApiResourceId -ResourceId $ResourceId -IsValid } | Should -Not -Throw - } - - It 'Should return $false - ""' -TestCases $testCasesInvalidResourceIds { - param ([System.String]$ResourceId) - - Test-AzDevOpsApiResourceId -ResourceId $ResourceId -IsValid | Should -BeFalse - } - } - } - } - - - Context "When input parameters are invalid" { - - - Context 'When called with no/null parameter values/switches' { - - It 'Should throw' { - - { Test-AzDevOpsApiResourceId -ResourceId:$null -IsValid:$false } | Should -Throw - } - } - - - Context 'When "ResourceId" parameter value is a valid "ResourceId"' { - - - Context 'When called with "ResourceId" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesValidResourceIds { - param ([System.String]$ResourceId) - - { Test-AzDevOpsApiResourceId -ResourceId $ResourceId -IsValid:$false } | Should -Throw - } - } - } - - - Context 'When "ResourceId" parameter value is an invalid "ResourceId"' { - - - Context 'When called with "ResourceId" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesInvalidResourceIds { - param ([System.String]$ResourceId) - - { Test-AzDevOpsApiResourceId -ResourceId $ResourceId -IsValid:$false } | Should -Throw - } - } - } - - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceName.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceName.Tests.ps1 deleted file mode 100644 index 873bb161c..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiResourceName.Tests.ps1 +++ /dev/null @@ -1,107 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesValidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Valid' - $testCasesInvalidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Invalid' - - - Context 'When input parameters are valid' { - - - Context 'When called with "ResourceName" parameter value and the "IsValid" switch' { - - - Context 'When "ResourceName" parameter value is a valid "ResourceName"' { - - It 'Should not throw - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - { Test-AzDevOpsApiResourceName -ResourceName $ResourceName -IsValid } | Should -Not -Throw - } - - It 'Should return $true - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - Test-AzDevOpsApiResourceName -ResourceName $ResourceName -IsValid | Should -BeTrue - } - } - - - Context 'When "ResourceName" parameter value is an invalid "ResourceName"' { - - It 'Should not throw - ""' -TestCases $testCasesInvalidResourceNames { - param ([System.String]$ResourceName) - - { Test-AzDevOpsApiResourceName -ResourceName $ResourceName -IsValid } | Should -Not -Throw - } - - It 'Should return $false - ""' -TestCases $testCasesInvalidResourceNames { - param ([System.String]$ResourceName) - - Test-AzDevOpsApiResourceName -ResourceName $ResourceName -IsValid | Should -BeFalse - } - } - } - } - - - Context "When input parameters are invalid" { - - - Context 'When called with no/null parameter values/switches' { - - It 'Should throw' { - - { Test-AzDevOpsApiResourceName -ResourceName:$null -IsValid:$false } | Should -Throw - } - } - - - Context 'When "ResourceName" parameter value is a valid "ResourceName"' { - - - Context 'When called with "ResourceName" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesValidResourceNames { - param ([System.String]$ResourceName) - - { Test-AzDevOpsApiResourceName -ResourceName $ResourceName -IsValid:$false } | Should -Throw - } - } - } - - - Context 'When "ResourceName" parameter value is an invalid "ResourceName"' { - - - Context 'When called with "ResourceName" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesInvalidResourceNames { - param ([System.String]$ResourceName) - - { Test-AzDevOpsApiResourceName -ResourceName $ResourceName -IsValid:$false } | Should -Throw - } - } - } - - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiTimeoutExceeded.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiTimeoutExceeded.Tests.ps1 deleted file mode 100644 index c23ef1c10..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiTimeoutExceeded.Tests.ps1 +++ /dev/null @@ -1,202 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesTimeoutExceeded = @( - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 1) - EndTime = [DateTime]::new(2020,11,12, 09,35,00, 252) # 1ms longer than timeout - TimeoutMs = 250 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 0) - EndTime = [DateTime]::new(2020,11,12, 09,35,01, 0) # 1s longer than timeout - TimeoutMs = 250 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 0) - EndTime = [DateTime]::new(2020,11,12, 09,35,01, 0) # 1s longer than timeout - TimeoutMs = 999 # Almost 1 second - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 1) - EndTime = [DateTime]::new(2020,11,12, 09,36,00, 1) # 1m longer than timeout - TimeoutMs = 250 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 999) - EndTime = [DateTime]::new(2020,11,12, 10,35,00, 999) # 1h longer than timeout - TimeoutMs = 250 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 0) - EndTime = [DateTime]::new(2020,11,13, 09,35,00, 0) # 1 day longer than timeout - TimeoutMs = 250 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 0) - EndTime = [DateTime]::new(2020,12,12, 09,35,00, 0) # 1 month longer than timeout - TimeoutMs = 500 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 0) - EndTime = [DateTime]::new(2021,11,12, 09,35,00, 0) # 1 year longer than timeout - TimeoutMs = 300000 - } - ) - $testCasesTimeoutNotExceeded = @( - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 0) - EndTime = [DateTime]::new(2020,11,12, 09,35,00, 0) # Identical to StartTime - TimeoutMs = 250 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 1) - EndTime = [DateTime]::new(2020,11,12, 09,35,00, 1) # Identical to StartTime - TimeoutMs = 500 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 1) - EndTime = [DateTime]::new(2020,11,12, 09,35,00, 250) # 1ms shorter than timeout (compared to StartTime) - TimeoutMs = 250 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 0) - EndTime = [DateTime]::new(2020,11,12, 09,35,00, 249) # 1ms shorter than timeout (compared to StartTime) - TimeoutMs = 250 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 0) - EndTime = [DateTime]::new(2020,11,12, 09,35,01, 0) # 1s longer than timeout - TimeoutMs = 1000 # 1 second - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 502) - EndTime = [DateTime]::new(2020,11,12, 09,35,00, 1) # EndTime 501ms before StartTime (negative timespan) - TimeoutMs = 550 - }, - @{ - StartTime = [DateTime]::new(2020,11,12, 09,35,00, 501) - EndTime = [DateTime]::new(2020,11,12, 09,35,00, 0) # EndTime 501ms before StartTime (negative timespan) - TimeoutMs = 500 - } - ) - - - Context 'When input parameters are valid' { - - - Context 'When called with mandatory "StartTime", "EndTime" and "TimeoutMs" parameter values' { - - Context 'When called with values that should generate an exceeded timeout' { - - It 'Should not throw - "","",""' -TestCases $testCasesTimeoutExceeded { - param ([Datetime]$StartTime, [Datetime]$EndTime, [Int32]$TimeoutMs) - - { Test-AzDevOpsApiTimeoutExceeded -StartTime $StartTime -EndTime $EndTime -TimeoutMs $TimeoutMs } | Should -Not -Throw - } - - It 'Should return $true - "","",""' -TestCases $testCasesTimeoutExceeded { - param ([Datetime]$StartTime, [Datetime]$EndTime, [Int32]$TimeoutMs) - - Test-AzDevOpsApiTimeoutExceeded -StartTime $StartTime -EndTime $EndTime -TimeoutMs $TimeoutMs | Should -BeTrue - } - } - - Context 'When called with values that should not generate an exceeded timeout' { - - It 'Should not throw - "","",""' -TestCases $testCasesTimeoutNotExceeded { - param ([Datetime]$StartTime, [Datetime]$EndTime, [Int32]$TimeoutMs) - - { Test-AzDevOpsApiTimeoutExceeded -StartTime $StartTime -EndTime $EndTime -TimeoutMs $TimeoutMs } | Should -Not -Throw - } - - It 'Should return $false - "","",""' -TestCases $testCasesTimeoutNotExceeded { - param ([Datetime]$StartTime, [Datetime]$EndTime, [Int32]$TimeoutMs) - - Test-AzDevOpsApiTimeoutExceeded -StartTime $StartTime -EndTime $EndTime -TimeoutMs $TimeoutMs | Should -BeFalse - } - } - } - } - - - Context "When input parameters are invalid" { - - [DateTime]$testTime = [DateTime]::new(2020,11,12, 09,35,00, 0) - [Int32]$testTimeoutMs = 250 - - Context 'When called with no/null parameter values' { - - It 'Should throw' { - - { Test-AzDevOpsApiTimeoutExceeded -StartTime $null -EndTime $null -TimeoutMs $null } | Should -Throw - } - } - - Context 'When called with no/null "StartTime" parameter value' { - - It 'Should throw' { - - { Test-AzDevOpsApiTimeoutExceeded -StartTime $null -EndTime $testTime -TimeoutMs $testTimeoutMs } | Should -Throw - } - } - - Context 'When called with no/null "EndTime" parameter value' { - - It 'Should throw' { - - { Test-AzDevOpsApiTimeoutExceeded -StartTime $testTime -EndTime $null -TimeoutMs $testTimeoutMs } | Should -Throw - } - } - - Context 'When called with no/null "StartTime" and "EndTime" parameter values' { - - It 'Should throw' { - - { Test-AzDevOpsApiTimeoutExceeded -StartTime $null -EndTime $null -TimeoutMs $testTimeoutMs } | Should -Throw - } - } - - Context 'When called with no/null "TimeoutMs" parameter value' { - - It 'Should throw' { - - { Test-AzDevOpsApiTimeoutExceeded -StartTime $testTime -EndTime $testTime -TimeoutMs $null } | Should -Throw - } - } - - Context 'When called with no/null "StartTime" and "TimeoutMs" parameter values' { - - It 'Should throw' { - - { Test-AzDevOpsApiTimeoutExceeded -StartTime $null -EndTime $testTime -TimeoutMs $null } | Should -Throw - } - } - - Context 'When called with no/null "EndTime" and "TimeoutMs" parameter values' { - - It 'Should throw' { - - { Test-AzDevOpsApiTimeoutExceeded -StartTime $testTime -EndTime $null -TimeoutMs $null } | Should -Throw - } - } - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiUri.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiUri.Tests.ps1 deleted file mode 100644 index 92cc4d3d8..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiUri.Tests.ps1 +++ /dev/null @@ -1,107 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesValidApiUris = Get-TestCase -ScopeName 'ApiUri' -TestCaseName 'Valid' - $testCasesInvalidApiUris = Get-TestCase -ScopeName 'ApiUri' -TestCaseName 'Invalid' - - - Context 'When input parameters are valid' { - - - Context 'When called with "ApiUri" parameter value and the "IsValid" switch' { - - - Context 'When "ApiUri" parameter value is a valid "ApiUri"' { - - It 'Should not throw - ""' -TestCases $testCasesValidApiUris { - param ([System.String]$ApiUri) - - { Test-AzDevOpsApiUri -ApiUri $ApiUri -IsValid } | Should -Not -Throw - } - - It 'Should return $true - ""' -TestCases $testCasesValidApiUris { - param ([System.String]$ApiUri) - - Test-AzDevOpsApiUri -ApiUri $ApiUri -IsValid | Should -BeTrue - } - } - - - Context 'When "ApiUri" parameter value is an invalid "ApiUri"' { - - It 'Should not throw - ""' -TestCases $testCasesInvalidApiUris { - param ([System.String]$ApiUri) - - { Test-AzDevOpsApiUri -ApiUri $ApiUri -IsValid } | Should -Not -Throw - } - - It 'Should return $false - ""' -TestCases $testCasesInvalidApiUris { - param ([System.String]$ApiUri) - - Test-AzDevOpsApiUri -ApiUri $ApiUri -IsValid | Should -BeFalse - } - } - } - } - - - Context "When input parameters are invalid" { - - - Context 'When called with no/null parameter values/switches' { - - It 'Should throw' { - - { Test-AzDevOpsApiUri -ApiUri:$null -IsValid:$false } | Should -Throw - } - } - - - Context 'When "ApiUri" parameter value is a valid "ApiUri"' { - - - Context 'When called with "ApiUri" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesValidApiUris { - param ([System.String]$ApiUri) - - { Test-AzDevOpsApiUri -ApiUri $ApiUri -IsValid:$false } | Should -Throw - } - } - } - - - Context 'When "ApiUri" parameter value is an invalid "ApiUri"' { - - - Context 'When called with "ApiUri" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesInvalidApiUris { - param ([System.String]$ApiUri) - - { Test-AzDevOpsApiUri -ApiUri $ApiUri -IsValid:$false } | Should -Throw - } - } - } - - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiVersion.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiVersion.Tests.ps1 deleted file mode 100644 index 9e81ef7ab..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Test-AzDevOpsApiVersion.Tests.ps1 +++ /dev/null @@ -1,107 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesValidApiVersions = Get-TestCase -ScopeName 'ApiVersion' -TestCaseName 'Valid' - $testCasesInvalidApiVersions = Get-TestCase -ScopeName 'ApiVersion' -TestCaseName 'Invalid' - - - Context 'When input parameters are valid' { - - - Context 'When called with "ApiVersion" parameter value and the "IsValid" switch' { - - - Context 'When "ApiVersion" parameter value is a valid "ApiVersion"' { - - It 'Should not throw - ""' -TestCases $testCasesValidApiVersions { - param ([System.String]$ApiVersion) - - { Test-AzDevOpsApiVersion -ApiVersion $ApiVersion -IsValid } | Should -Not -Throw - } - - It 'Should return $true - ""' -TestCases $testCasesValidApiVersions { - param ([System.String]$ApiVersion) - - Test-AzDevOpsApiVersion -ApiVersion $ApiVersion -IsValid | Should -BeTrue - } - } - - - Context 'When "ApiVersion" parameter value is an invalid "ApiVersion"' { - - It 'Should not throw - ""' -TestCases $testCasesInvalidApiVersions { - param ([System.String]$ApiVersion) - - { Test-AzDevOpsApiVersion -ApiVersion $ApiVersion -IsValid } | Should -Not -Throw - } - - It 'Should return $false - ""' -TestCases $testCasesInvalidApiVersions { - param ([System.String]$ApiVersion) - - Test-AzDevOpsApiVersion -ApiVersion $ApiVersion -IsValid | Should -BeFalse - } - } - } - } - - - Context "When input parameters are invalid" { - - - Context 'When called with no/null parameter values/switches' { - - It 'Should throw' { - - { Test-AzDevOpsApiVersion -ApiVersion:$null -IsValid:$false } | Should -Throw - } - } - - - Context 'When "ApiVersion" parameter value is a valid "ApiVersion"' { - - - Context 'When called with "ApiVersion" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesValidApiVersions { - param ([System.String]$ApiVersion) - - { Test-AzDevOpsApiVersion -ApiVersion $ApiVersion -IsValid:$false } | Should -Throw - } - } - } - - - Context 'When "ApiVersion" parameter value is an invalid "ApiVersion"' { - - - Context 'When called with "ApiVersion" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesInvalidApiVersions { - param ([System.String]$ApiVersion) - - { Test-AzDevOpsApiVersion -ApiVersion $ApiVersion -IsValid:$false } | Should -Throw - } - } - } - - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Wait-AzDevOpsApiResource.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Wait-AzDevOpsApiResource.Tests.ps1 deleted file mode 100644 index c5be79a5f..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Wait-AzDevOpsApiResource.Tests.ps1 +++ /dev/null @@ -1,615 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Api\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - # Get default, parameter values - $defaultWaitIntervalMilliseconds = Get-AzDevOpsApiWaitIntervalMs - $defaultWaitTimeoutMilliseconds = Get-AzDevOpsApiWaitTimeoutMs - - # Mock functions called in function - Mock Get-AzDevOpsApiWaitIntervalMs {} - Mock Get-AzDevOpsApiWaitTimeoutMs {} - # Mock Get-Date {} # Do not mock - # Mock New-InvalidOperationException {} # Do not mock - Mock Start-Sleep {} - Mock Test-AzDevOpsApiResource {} - Mock Test-AzDevOpsApiTimeoutExceeded {} - - # Generate valid, test cases - $testCasesValidApiUris = Get-TestCase -ScopeName 'ApiUri' -TestCaseName 'Valid' - $testCasesValidPats = Get-TestCase -ScopeName 'Pat' -TestCaseName 'Valid' - $testCasesValidResourceIds = Get-TestCase -ScopeName 'ResourceId' -TestCaseName 'Valid' - $testCasesValidApiUriPatResourceIds = Join-TestCaseArray -TestCaseArray @( - $testCasesValidApiUris, - $testCasesValidPats, - $testCasesValidResourceIds) -Expand - $testCasesValidApiUriPatResourceIds3 = $testCasesValidApiUriPatResourceIds | Select-Object -First 3 - - $validApiVersion = Get-TestCaseValue -ScopeName 'ApiVersion' -TestCaseName 'Valid' -First 1 - - # Generate invalid, test cases - $testCasesInvalidApiUris = Get-TestCase -ScopeName 'ApiUri' -TestCaseName 'Invalid' - $testCasesInvalidPats = Get-TestCase -ScopeName 'Pat' -TestCaseName 'Invalid' - $testCasesInvalidResourceIds = Get-TestCase -ScopeName 'ResourceId' -TestCaseName 'Invalid' - $testCasesInvalidApiUriPatResourceIds = Join-TestCaseArray -TestCaseArray @( - $testCasesInvalidApiUris, - $testCasesInvalidPats, - $testCasesInvalidResourceIds) -Expand - $testCasesInvalidApiUriPatResourceIds3 = $testCasesInvalidApiUriPatResourceIds | Select-Object -First 3 - - $invalidApiVersion = Get-TestCaseValue -ScopeName 'ApiVersion' -TestCaseName 'Invalid' -First 1 - - - Context 'When input parameters are valid' { - - - Context "When called with all, mandatory parameters ('ApiUri', 'Pat' and 'ResourceId')" { - - It "Should throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceId $ResourceId } | Should -Throw - } - - - Context "When also called with mandatory, 'IsPresent', switch parameter" { - Mock Get-AzDevOpsApiWaitIntervalMs { return 250 } - Mock Get-AzDevOpsApiWaitTimeoutMs { return 250 } - Mock Test-AzDevOpsApiResource { return $true } - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent } | Should -Not -Throw - } - - It "Should output null/nothing - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - $output = Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - $output | Should -BeNullOrEmpty - } - - It "Should invoke 'Get-AzDevOpsApiWaitIntervalMs' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitIntervalMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - Assert-MockCalled 'Get-AzDevOpsApiWaitIntervalMs' -Times 1 -Exactly -Scope It - } - - It "Should invoke 'Get-AzDevOpsApiWaitTimeoutMs' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitTimeoutMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - Assert-MockCalled 'Get-AzDevOpsApiWaitTimeoutMs' -Times 1 -Exactly -Scope It - } - - Context "When also called with valid 'ApiVersion' parameter value" { - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -ApiVersion $validApiVersion -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent } | Should -Not -Throw - } - } - - Context "When also called with invalid 'ApiVersion' parameter value" { - - It "Should throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -ApiVersion $invalidApiVersion -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent } | Should -Throw - } - } - - Context "When 'Test-AzDevOpsApiResource' returns true" { - - It "Should invoke 'Test-AzDevOpsApiResource' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Test-AzDevOpsApiResource { return $true } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - Assert-MockCalled 'Test-AzDevOpsApiResource' -Times 1 -Exactly -Scope It - } - - It "Should invoke 'Get-Date' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-Date { return [DateTime]::get_UtcNow() } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - Assert-MockCalled 'Get-Date' -Times 1 -Exactly -Scope It - } - - It "Should not invoke 'Start-Sleep' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Start-Sleep {} -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - Assert-MockCalled 'Start-Sleep' -Times 0 -Exactly -Scope It - } - - It "Should not invoke 'Test-AzDevOpsApiTimeoutExceeded' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Test-AzDevOpsApiTimeoutExceeded {} -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - Assert-MockCalled 'Test-AzDevOpsApiTimeoutExceeded' -Times 0 -Exactly -Scope It - } - } - - - Context "When 'Test-AzDevOpsApiResource' returns false, then true" { - - - Context "When 'WaitTimeoutMilliseconds' has not been exceeded" { - Mock Get-AzDevOpsApiWaitTimeoutMs {250} # 250ms - Mock Test-AzDevOpsApiResource { - $script:mockTestAzDevOpsApiResourceInvoked = !($script:mockTestAzDevOpsApiResourceInvoked) - return !($script:mockTestAzDevOpsApiResourceInvoked) - } - Mock Test-AzDevOpsApiTimeoutExceeded { return $false } - - It "Should invoke 'Test-AzDevOpsApiResource' exactly twice - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - $script:mockTestAzDevOpsApiResourceInvoked = $false - Mock Test-AzDevOpsApiResource { - $script:mockTestAzDevOpsApiResourceInvoked = !($script:mockTestAzDevOpsApiResourceInvoked) - return !($script:mockTestAzDevOpsApiResourceInvoked) - } - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - Assert-MockCalled 'Test-AzDevOpsApiResource' -Times 2 -Exactly -Scope It - } - - It "Should invoke 'Get-Date' exactly twice - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-Date { return [DateTime]::get_UtcNow() } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - Assert-MockCalled 'Get-Date' -Times 2 -Exactly -Scope It - } - - It "Should invoke 'Start-Sleep' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - $script:mockTestAzDevOpsApiResourceInvoked = $false # for 'Test-AzDevOpsApiResource' mock - Mock Start-Sleep {} -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - Assert-MockCalled 'Start-Sleep' -Times 1 -Exactly -Scope It - } - - It "Should invoke 'Test-AzDevOpsApiTimeoutExceeded' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - $script:mockTestAzDevOpsApiResourceInvoked = $false # for 'Test-AzDevOpsApiResource' mock - Mock Test-AzDevOpsApiTimeoutExceeded { return $false } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent - - Assert-MockCalled 'Test-AzDevOpsApiTimeoutExceeded' -Times 1 -Exactly -Scope It - } - - } - } - - - Context "When 'Test-AzDevOpsApiResource' returns false, and exceeds timeout (i.e. 'Test-AzDevOpsApiTimeoutExceeded' returns true)" { - Mock Get-AzDevOpsApiWaitTimeoutMs {250} # 250ms - Mock Test-AzDevOpsApiTimeoutExceeded { return $true } # i.e. Timeout exceeded - Mock Test-AzDevOpsApiResource { return $false } # i.e. ApiResource has not completed - - It "Should throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent } | Should -Throw - } - } - - - Context "When also called with optional, 'WaitIntervalMilliseconds' parameter" { - - $exampleWaitIntervalMilliseconds = $defaultWaitIntervalMilliseconds - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent -WaitIntervalMilliseconds $exampleWaitIntervalMilliseconds } | Should -Not -Throw - } - - It "Should not invoke 'Get-AzDevOpsApiWaitIntervalMs' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitIntervalMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent -WaitIntervalMilliseconds $exampleWaitIntervalMilliseconds - - Assert-MockCalled 'Get-AzDevOpsApiWaitIntervalMs' -Times 0 -Exactly -Scope It - } - - - } - - - Context "When also called with optional, 'WaitTimeoutMilliseconds' parameter" { - - $exampleWaitTimeoutMilliseconds = $defaultWaitTimeoutMilliseconds - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent -WaitTimeoutMilliseconds $exampleWaitTimeoutMilliseconds } | Should -Not -Throw - } - - It "Should not invoke 'Get-AzDevOpsApiWaitTimeoutMs' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitTimeoutMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent -WaitTimeoutMilliseconds $exampleWaitTimeoutMilliseconds - - Assert-MockCalled 'Get-AzDevOpsApiWaitTimeoutMs' -Times 0 -Exactly -Scope It - } - - } - - - Context "When also called with both optional, 'WaitIntervalMilliseconds' and 'WaitTimeoutMilliseconds' parameters" { - - $exampleWaitIntervalMilliseconds = $defaultWaitIntervalMilliseconds - $exampleWaitTimeoutMilliseconds = $defaultWaitTimeoutMilliseconds - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent ` - -WaitTimeoutMilliseconds $exampleWaitTimeoutMilliseconds -WaitIntervalMilliseconds $exampleWaitIntervalMilliseconds } | Should -Not -Throw - } - - It "Should not invoke 'Get-AzDevOpsApiWaitIntervalMs' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitIntervalMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent ` - -WaitTimeoutMilliseconds $exampleWaitTimeoutMilliseconds -WaitIntervalMilliseconds $exampleWaitIntervalMilliseconds - - Assert-MockCalled 'Get-AzDevOpsApiWaitIntervalMs' -Times 0 -Exactly -Scope It - } - - It "Should not invoke 'Get-AzDevOpsApiWaitTimeoutMs' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitTimeoutMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent ` - -WaitTimeoutMilliseconds $exampleWaitTimeoutMilliseconds -WaitIntervalMilliseconds $exampleWaitIntervalMilliseconds - - Assert-MockCalled 'Get-AzDevOpsApiWaitTimeoutMs' -Times 0 -Exactly -Scope It - } - - } - - - } - - - Context "When also called with mandatory, 'IsAbsent', switch parameter" { - Mock Get-AzDevOpsApiWaitIntervalMs { return 250 } - Mock Get-AzDevOpsApiWaitTimeoutMs { return 250 } - Mock Test-AzDevOpsApiResource { return $false } - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent } | Should -Not -Throw - } - - It "Should output null/nothing - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - $output = Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - $output | Should -BeNullOrEmpty - } - - It "Should invoke 'Get-AzDevOpsApiWaitIntervalMs' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitIntervalMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - Assert-MockCalled 'Get-AzDevOpsApiWaitIntervalMs' -Times 1 -Exactly -Scope It - } - - It "Should invoke 'Get-AzDevOpsApiWaitTimeoutMs' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitTimeoutMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - Assert-MockCalled 'Get-AzDevOpsApiWaitTimeoutMs' -Times 1 -Exactly -Scope It - } - - - Context "When also called with valid 'ApiVersion' parameter value" { - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -ApiVersion $validApiVersion -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent } | Should -Not -Throw - } - } - - - Context "When also called with invalid 'ApiVersion' parameter value" { - - It "Should throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -ApiVersion $invalidApiVersion -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent } | Should -Throw - } - } - - - Context "When 'Test-AzDevOpsApiResource' returns false" { - - It "Should invoke 'Test-AzDevOpsApiResource' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Test-AzDevOpsApiResource { return $false } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - Assert-MockCalled 'Test-AzDevOpsApiResource' -Times 1 -Exactly -Scope It - } - - It "Should invoke 'Get-Date' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-Date { return [DateTime]::get_UtcNow() } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - Assert-MockCalled 'Get-Date' -Times 1 -Exactly -Scope It - } - - It "Should not invoke 'Start-Sleep' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Start-Sleep {} -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - Assert-MockCalled 'Start-Sleep' -Times 0 -Exactly -Scope It - } - - It "Should not invoke 'Test-AzDevOpsApiTimeoutExceeded' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Test-AzDevOpsApiTimeoutExceeded {} -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - Assert-MockCalled 'Test-AzDevOpsApiTimeoutExceeded' -Times 0 -Exactly -Scope It - } - } - - - Context "When 'Test-AzDevOpsApiResource' returns true, then false" { - - - Context "When 'WaitTimeoutMilliseconds' has not been exceeded" { - Mock Get-AzDevOpsApiWaitTimeoutMs {250} # 250ms - Mock Test-AzDevOpsApiResource { - $script:mockTestAzDevOpsApiResourceInvoked = !($script:mockTestAzDevOpsApiResourceInvoked) - return $script:mockTestAzDevOpsApiResourceInvoked - } - Mock Test-AzDevOpsApiTimeoutExceeded { return $false } - - It "Should invoke 'Test-AzDevOpsApiResource' exactly twice - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - $script:mockTestAzDevOpsApiResourceInvoked = $false - Mock Test-AzDevOpsApiResource { - $script:mockTestAzDevOpsApiResourceInvoked = !($script:mockTestAzDevOpsApiResourceInvoked) - return $script:mockTestAzDevOpsApiResourceInvoked - } - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - Assert-MockCalled 'Test-AzDevOpsApiResource' -Times 2 -Exactly -Scope It - } - - It "Should invoke 'Get-Date' exactly twice - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-Date { return [DateTime]::get_UtcNow() } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - Assert-MockCalled 'Get-Date' -Times 2 -Exactly -Scope It - } - - It "Should invoke 'Start-Sleep' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - $script:mockTestAzDevOpsApiResourceInvoked = $false # for 'Test-AzDevOpsApiResource' mock - Mock Start-Sleep {} -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - Assert-MockCalled 'Start-Sleep' -Times 1 -Exactly -Scope It - } - - It "Should invoke 'Test-AzDevOpsApiTimeoutExceeded' exactly once - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - $script:mockTestAzDevOpsApiResourceInvoked = $false # for 'Test-AzDevOpsApiResource' mock - Mock Test-AzDevOpsApiTimeoutExceeded { return $false } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent - - Assert-MockCalled 'Test-AzDevOpsApiTimeoutExceeded' -Times 1 -Exactly -Scope It - } - - } - } - - - Context "When 'Test-AzDevOpsApiResource' returns false, and exceeds timeout (i.e. 'Test-AzDevOpsApiTimeoutExceeded' returns true)" { - Mock Get-AzDevOpsApiWaitTimeoutMs {250} # 250ms - Mock Test-AzDevOpsApiTimeoutExceeded { return $true } # i.e. Timeout exceeded - Mock Test-AzDevOpsApiResource { return $true } # i.e. ApiResource has not completed - - It "Should throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent } | Should -Throw - } - } - - - Context "When also called with optional, 'WaitIntervalMilliseconds' parameter" { - - $exampleWaitIntervalMilliseconds = $defaultWaitIntervalMilliseconds - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent -WaitIntervalMilliseconds $exampleWaitIntervalMilliseconds } | Should -Not -Throw - } - - It "Should not invoke 'Get-AzDevOpsApiWaitIntervalMs' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitIntervalMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent -WaitIntervalMilliseconds $exampleWaitIntervalMilliseconds - - Assert-MockCalled 'Get-AzDevOpsApiWaitIntervalMs' -Times 0 -Exactly -Scope It - } - - - } - - - Context "When also called with optional, 'WaitTimeoutMilliseconds' parameter" { - - $exampleWaitTimeoutMilliseconds = $defaultWaitTimeoutMilliseconds - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent -WaitTimeoutMilliseconds $exampleWaitTimeoutMilliseconds } | Should -Not -Throw - } - - It "Should not invoke 'Get-AzDevOpsApiWaitTimeoutMs' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitTimeoutMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent -WaitTimeoutMilliseconds $exampleWaitTimeoutMilliseconds - - Assert-MockCalled 'Get-AzDevOpsApiWaitTimeoutMs' -Times 0 -Exactly -Scope It - } - - } - - - Context "When also called with both optional, 'WaitIntervalMilliseconds' and 'WaitTimeoutMilliseconds' parameters" { - - $exampleWaitIntervalMilliseconds = $defaultWaitIntervalMilliseconds - $exampleWaitTimeoutMilliseconds = $defaultWaitTimeoutMilliseconds - - It "Should not throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent ` - -WaitTimeoutMilliseconds $exampleWaitTimeoutMilliseconds -WaitIntervalMilliseconds $exampleWaitIntervalMilliseconds } | Should -Not -Throw - } - - It "Should not invoke 'Get-AzDevOpsApiWaitIntervalMs' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitIntervalMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent ` - -WaitTimeoutMilliseconds $exampleWaitTimeoutMilliseconds -WaitIntervalMilliseconds $exampleWaitIntervalMilliseconds - - Assert-MockCalled 'Get-AzDevOpsApiWaitIntervalMs' -Times 0 -Exactly -Scope It - } - - It "Should not invoke 'Get-AzDevOpsApiWaitTimeoutMs' - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - Mock Get-AzDevOpsApiWaitTimeoutMs { return 250 } -Verifiable - - Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsAbsent ` - -WaitTimeoutMilliseconds $exampleWaitTimeoutMilliseconds -WaitIntervalMilliseconds $exampleWaitIntervalMilliseconds - - Assert-MockCalled 'Get-AzDevOpsApiWaitTimeoutMs' -Times 0 -Exactly -Scope It - } - - } - - } - - - Context "When also called with both mandatory, 'IsPresent' and 'IsAbsent', switch parameters" { - - It "Should throw - '', '', ''" -TestCases $testCasesValidApiUriPatResourceIds3 { - param ( [System.String]$ApiUri, [System.String]$Pat, [System.String]$ResourceId ) - - { Wait-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat -ResourceName 'Project' -ResourceId $ResourceId -IsPresent -IsAbsent } | Should -Throw - } - } - } - } - - - Context "When input parameters are invalid" { - - # TODO - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.InvokeTests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.InvokeTests.ps1 new file mode 100644 index 000000000..5347ffe02 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.InvokeTests.ps1 @@ -0,0 +1,37 @@ +<# + .SYNOPSIS + Automated unit test for classes in AzureDevOpsDsc. +#> + + +Function Split-RecurivePath { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$Path, + [Parameter(Mandatory = $false)] + [int]$Times = 1 + ) + + 1 .. $Times | ForEach-Object { + $Path = Split-Path -Path $Path -Parent + } + + $Path +} + +$Global:RepositoryRoot = Split-RecurivePath $PSScriptRoot -Times 4 +$script:CurrentFolder = Split-RecurivePath $PSScriptRoot -Times 1 + +Import-Module -Name (Join-Path -Path $script:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestCases.psm1') +Import-Module -Name (Join-Path -Path $script:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1') +Import-Module -Name (Join-Path -Path $script:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1') + +# +# Recurse through the folders and invoke the tests. + +$script:TestFolders = Get-ChildItem -Path (Join-Path -Path $script:CurrentFolder -ChildPath '\AzureDevOpsDsc.Common') -Directory + +ForEach ($TestFolder in $script:TestFolders) { + Invoke-Pester -Path $TestFolder.FullName -Output Detailed -PassThru +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.TestInitialization.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.TestInitialization.ps1 deleted file mode 100644 index 8fda79eaa..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.TestInitialization.ps1 +++ /dev/null @@ -1,45 +0,0 @@ -<# - .SYNOPSIS - Automated unit test for classes in AzureDevOpsDsc. -#> - - -Function Split-RecurivePath { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - [string]$Path, - [Parameter(Mandatory = $false)] - [int]$Times = 1 - ) - - 1 .. $Times | ForEach-Object { - $Path = Split-Path -Path $Path -Parent - } - - $Path -} - - -$script:RepositoryRoot = Split-RecurivePath $PSScriptRoot -Times 4 - -Import-Module -Name (Join-Path -Path $script:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestCases.psm1') -Import-Module -Name (Join-Path -Path $script:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1') - -Set-OutputDirAsModulePath -RepositoryRoot $script:RepositoryRoot - -$script:dscModuleName = 'AzureDevOpsDsc' -$script:dscModule = Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1 -$script:dscModuleFile = $($script:dscModule.ModuleBase +'\'+ $script:dscModuleName + ".psd1") - -Get-Module -Name $script:dscModuleName -All | - Remove-Module $script:dscModuleName -Force -ErrorAction SilentlyContinue - -$script:subModuleName = 'AzureDevOpsDsc.Common' -Import-Module -Name $script:dscModuleFile #-Force - -Get-Module -Name $script:subModuleName -All | - Remove-Module -Force -ErrorAction SilentlyContinue -$script:subModulesFolder = Join-Path -Path $script:dscModule.ModuleBase -ChildPath 'Modules' -$script:subModuleFile = Join-Path $script:subModulesFolder "$($script:subModuleName)/$($script:subModuleName).psd1" -Import-Module -Name $script:subModuleFile #-Force #-Verbose diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzDevOpsCache.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzDevOpsCache.ps1.disabled new file mode 100644 index 000000000..5a42d6490 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzDevOpsCache.ps1.disabled @@ -0,0 +1,87 @@ +<# +.SYNOPSIS + Retrieves the Azure DevOps API cache. + +.DESCRIPTION + The Get-AzDevOpsApiCache function is used to retrieve the cached data from the Azure DevOps API. It checks for the presence of the cache files and verifies the parameters before returning the cached data. + +.PARAMETER ApiEndpoint + Specifies the API endpoint to retrieve the cache for. + +.PARAMETER Parameters + Specifies the parameters used for the API endpoint. + +.EXAMPLE + Get-AzDevOpsApiCache -ApiEndpoint 'projects/list' -Parameters @{ organization = 'myOrg' } + + This example retrieves the cached data for the 'projects/list' API endpoint with the specified parameters. + +#> + +. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 + +InModuleScope 'AzureDevOpsDsc.Common' { + Describe 'Get-AzDevOpsApiCache Tests' { + BeforeAll { + # Mock the environment variable for cache path + $env:AZDODSCCachePath = "TestCachePath" + + # Define test data + $testApiEndpoint = 'projects/list' + $testParameters = @{ organization = 'myOrg' } + $normalizedApiEndpoint = $testApiEndpoint -replace '[\/:\*\?"<>|]', '_' + $metadataFilePath = Join-Path -Path $env:AZDODSCCachePath -ChildPath "${normalizedApiEndpoint}_test.metadata.json" + $cacheFilePath = Join-Path -Path $env:AZDODSCCachePath -ChildPath "${normalizedApiEndpoint}_test.cache.json" + + # Create mock metadata and cache files + $metadataObject = @{ + Parameters = @{ organization = 'myOrg' } + CacheFile = "${normalizedApiEndpoint}_test.cache.json" + } | ConvertTo-Json + Set-Content -Path $metadataFilePath -Value $metadataObject + + $cacheObject = @{ + Data = "Cached API response" + } | ConvertTo-Json + Set-Content -Path $cacheFilePath -Value $cacheObject + } + + It 'Throws an exception if AZDODSCCachePath environment variable is not set' { + # Temporarily remove the environment variable + Remove-Item Env:\AZDODSCCachePath + + { Get-AzDevOpsApiCache -ApiEndpoint $testApiEndpoint -Parameters $testParameters } | Should -Throw -ExpectedMessage 'AZDODSCCachePath environment variable is not set.' + + # Restore the environment variable + $env:AZDODSCCachePath = "TestCachePath" + } + + It 'Returns $null if no metadata files are found' { + Mock Get-ChildItem { return @() } + + $result = Get-AzDevOpsApiCache -ApiEndpoint $testApiEndpoint -Parameters $testParameters + $result | Should -Be $null + } + + It 'Returns $null if parameters do not match' { + $wrongParameters = @{ organization = 'anotherOrg' } + + $result = Get-AzDevOpsApiCache -ApiEndpoint $testApiEndpoint -Parameters $wrongParameters + $result | Should -Be $null + } + + It 'Returns cache content if parameters match and cache file exists' { + $result = Get-AzDevOpsApiCache -ApiEndpoint $testApiEndpoint -Parameters $testParameters + $result.Data | Should -Be 'Cached API response' + } + + AfterAll { + # Clean up test files + Remove-Item $metadataFilePath + Remove-Item $cacheFilePath + + # Clean up environment variable + Remove-Item Env:\AZDODSCCachePath + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Get-AzManagedIdentityToken.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzManagedIdentityToken.Tests.ps1.disabled similarity index 92% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Get-AzManagedIdentityToken.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzManagedIdentityToken.Tests.ps1.disabled index 9f2fe9b00..9dba21da8 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Get-AzManagedIdentityToken.Tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzManagedIdentityToken.Tests.ps1.disabled @@ -26,7 +26,7 @@ InModuleScope 'AzureDevOpsDsc.Common' { } } - Mock Test-AzManagedIdentityToken { + Mock Test-Token { return $true } @@ -58,9 +58,9 @@ InModuleScope 'AzureDevOpsDsc.Common' { { Get-AzManagedIdentityToken -OrganizationName $organizationName } | Should -Throw } - It "Throws an exception if the Test-AzManagedIdentityToken returns false" { + It "Throws an exception if the Test-Token returns false" { # Arrange - Mock Test-AzManagedIdentityToken { + Mock Test-Token { return $false } $organizationName = "Contoso" diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsACLToken.Tests.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsACLToken.Tests.ps1.disabled new file mode 100644 index 000000000..17ddbca92 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsACLToken.Tests.ps1.disabled @@ -0,0 +1,68 @@ +<# +.SYNOPSIS + This script contains unit tests for the New-AzDevOpsACLToken function. + +.DESCRIPTION + The New-AzDevOpsACLToken function is used to generate access tokens for Azure DevOps. + It can generate project-level access tokens or team-level access tokens. + + This script contains unit tests to verify the behavior of the New-AzDevOpsACLToken function. + +.NOTES + Author: Your Name + Date: Current Date + +.LINK + https://link-to-documentation + +.EXAMPLE + Describe "New-AzDevOpsACLToken Tests" { + Context "Project-level access token" { + It "Returns the correct token without TeamId" { + $OrganizationName = "MyOrg" + $ProjectId = "1234" + $result = New-AzDevOpsACLToken -OrganizationName $OrganizationName -ProjectId $ProjectId + $expectedToken = "vstfs:///Classification/TeamProject/$ProjectId" + $result | Should -BeExactly $expectedToken + } + } + + Context "Team-level access token" { + It "Returns the correct token with TeamId" { + $OrganizationName = "MyOrg" + $ProjectId = "1234" + $TeamId = "abcd" + $result = New-AzDevOpsACLToken -OrganizationName $OrganizationName -ProjectId $ProjectId -TeamId $TeamId + $expectedToken = "vstfs:///Classification/TeamProject/$ProjectId/$TeamId" + $result | Should -BeExactly $expectedToken + } + } + } +#> + +. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 + +InModuleScope 'AzureDevOpsDsc.Common' { + Describe "New-AzDevOpsACLToken Tests" { + Context "Project-level access token" { + It "Returns the correct token without TeamId" { + $OrganizationName = "MyOrg" + $ProjectId = "1234" + $result = New-AzDevOpsACLToken -OrganizationName $OrganizationName -ProjectId $ProjectId + $expectedToken = "vstfs:///Classification/TeamProject/$ProjectId" + $result | Should -BeExactly $expectedToken + } + } + + Context "Team-level access token" { + It "Returns the correct token with TeamId" { + $OrganizationName = "MyOrg" + $ProjectId = "1234" + $TeamId = "abcd" + $result = New-AzDevOpsACLToken -OrganizationName $OrganizationName -ProjectId $ProjectId -TeamId $TeamId + $expectedToken = "vstfs:///Classification/TeamProject/$ProjectId/$TeamId" + $result | Should -BeExactly $expectedToken + } + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiCache.Tests.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiCache.Tests.ps1.disabled new file mode 100644 index 000000000..915806721 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiCache.Tests.ps1.disabled @@ -0,0 +1,68 @@ +<# +.SYNOPSIS +Unit tests for the New-AzDevOpsApiCache function. + +.DESCRIPTION +This script contains unit tests for the New-AzDevOpsApiCache function in the AzureDevOpsDsc.Common module. The tests cover various scenarios such as error handling, cache directory creation, file generation, and parameter handling. + +.PARAMETER ApiEndpoint +The API endpoint to be cached. + +.PARAMETER Parameters +The parameters to be passed to the API endpoint. + +.PARAMETER Content +The content to be cached. + +.PARAMETER Depth +The depth of the JSON conversion. + +.EXAMPLE +#> + +. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 + +Describe 'New-AzDevOpsApiCache Tests' { + BeforeAll { + # Mock the Get-Date cmdlet to return a predictable date-time + Mock Get-Date { return [DateTime]::ParseExact('2023-01-01T00:00:00.0000000Z', 'o', $null) } + } + + It 'Throws an error when AZDODSCCachePath is not set' { + # Temporarily clear the AZDODSCCachePath environment variable for this test + $originalAzDodscCachePath = $ENV:AZDODSCCachePath + $ENV:AZDODSCCachePath = $null + + { New-AzDevOpsApiCache -ApiEndpoint 'projects/list' -Parameters @{ organization = 'myOrg' } -Content @{ key1 = 'value1' } } | Should -Throw 'AZDODSCCachePath environment variable is not set.' + + # Restore the original AZDODSCCachePath value + $ENV:AZDODSCCachePath = $originalAzDodscCachePath + } + + It 'Creates the cache directory if it does not exist' { + Mock Test-Path { return $false } + Mock New-Item {} + + New-AzDevOpsApiCache -ApiEndpoint 'projects/list' -Parameters @{ organization = 'myOrg' } -Content @{ key1 = 'value1' } + + Assert-MockCalled New-Item -Times 1 -Exactly + } + + It 'Generates cache and metadata files with correct content' { + Mock Out-File {} + + $content = @{ key1 = 'value1'; key2 = 'value2' } + New-AzDevOpsApiCache -ApiEndpoint 'projects/list' -Parameters @{ organization = 'myOrg' } -Content $content + + Assert-MockCalled Out-File -Times 2 -Exactly + } + + It 'Handles the Depth parameter correctly' { + Mock ConvertTo-Json { return '{}' } + + $content = @{ key1 = @{ subKey = 'subValue' } } + New-AzDevOpsApiCache -ApiEndpoint 'projects/list' -Parameters @{ organization = 'myOrg' } -Content $content -Depth 5 + + Assert-MockCalled ConvertTo-Json -ParameterFilter { $Depth -eq 5 } -Times 1 -Exactly + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzDevOpsApiResource.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiResource.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzDevOpsApiResource.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiResource.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzManagedIdentity.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzManagedIdentity.Tests.ps1.disabled similarity index 87% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzManagedIdentity.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzManagedIdentity.Tests.ps1.disabled index bba7c3d70..997a27937 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/New-AzManagedIdentity.Tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzManagedIdentity.Tests.ps1.disabled @@ -35,8 +35,8 @@ InModuleScope 'AzureDevOpsDsc.Common' { # Assert $Global:DSCAZDO_OrganizationName | Should -Be $orgName - $Global:DSCAZDO_ManagedIdentityToken | Should -Not -Be $null - $Global:DSCAZDO_ManagedIdentityToken.access_token | Should -Be "mocked_access_token" + $Global:DSCAZDO_AuthenticationToken | Should -Not -Be $null + $Global:DSCAZDO_AuthenticationToken.access_token | Should -Be "mocked_access_token" } It "Sets the global managed identity token to null if Get-AzManagedIdentityToken fails" { @@ -46,7 +46,7 @@ InModuleScope 'AzureDevOpsDsc.Common' { # Act / Assert { New-AzManagedIdentity -OrganizationName $orgName } | Should -Throw "Failed to get token." - $Global:DSCAZDO_ManagedIdentityToken | Should -Be $null + $Global:DSCAZDO_AuthenticationToken | Should -Be $null } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzDevOpsPatCredential.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzDevOpsPatCredential.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzDevOpsPatCredential.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzDevOpsPatCredential.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzManagedIdentityToken.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzManagedIdentityToken.Tests.ps1.disabled similarity index 77% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzManagedIdentityToken.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzManagedIdentityToken.Tests.ps1.disabled index c38435312..358b2af44 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzManagedIdentityToken.Tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzManagedIdentityToken.Tests.ps1.disabled @@ -4,14 +4,14 @@ <# .SYNOPSIS - Test suite for the Test-AzManagedIdentityToken function. + Test suite for the Test-Token function. .DESCRIPTION - This test suite validates the functionality of the Test-AzManagedIdentityToken function, ensuring it properly tests the managed identity token. + This test suite validates the functionality of the Test-Token function, ensuring it properly tests the managed identity token. #> InModuleScope 'AzureDevOpsDsc.Common' { - Describe "Test-AzManagedIdentityToken Function Tests" { + Describe "Test-Token Function Tests" { Mock Invoke-AzDevOpsApiRestMethod { return @{ @@ -35,7 +35,7 @@ InModuleScope 'AzureDevOpsDsc.Common' { } # Act - $result = Test-AzManagedIdentityToken -ManagedIdentity $mockManagedIdentity + $result = Test-Token -ManagedIdentity $mockManagedIdentity # Assert $result | Should -Be $true @@ -49,7 +49,7 @@ InModuleScope 'AzureDevOpsDsc.Common' { } # Act - $result = Test-AzManagedIdentityToken -ManagedIdentity $mockManagedIdentity + $result = Test-Token -ManagedIdentity $mockManagedIdentity # Assert $result | Should -Be $false diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Update-AzManagedIdentity.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Update-AzManagedIdentity.Tests.ps1.disabled similarity index 90% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Update-AzManagedIdentity.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Update-AzManagedIdentity.Tests.ps1.disabled index 6f4faecdf..f0ce773d2 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Update-AzManagedIdentity.Tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Update-AzManagedIdentity.Tests.ps1.disabled @@ -13,7 +13,7 @@ InModuleScope 'AzureDevOpsDsc.Common' { Update-AzManagedIdentity - $global:DSCAZDO_ManagedIdentityToken | Should -Not -Be $null + $Global:DSCAZDO_AuthenticationToken | Should -Not -Be $null } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOperationId.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOperationId.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOperationId.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOperationId.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOrganizationName.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOrganizationName.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOrganizationName.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOrganizationName.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectDescription.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectDescription.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectDescription.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectDescription.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectId.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectId.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectId.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectId.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectName.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectName.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectName.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectName.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Wait-AzDevOpsOperation.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Wait-AzDevOpsOperation.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Wait-AzDevOpsOperation.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Wait-AzDevOpsOperation.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsOperation.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Archive/Get-AzDevOpsOperation.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsOperation.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Archive/Get-AzDevOpsOperation.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsProject.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Archive/Get-AzDevOpsProject.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsProject.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Archive/Get-AzDevOpsProject.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDevOpsProject.Tests.Old.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Archive/New-AzDevOpsProject.Tests.Old.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDevOpsProject.Tests.Old.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Archive/New-AzDevOpsProject.Tests.Old.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Test-AzDevOpsOperation.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Archive/Test-AzDevOpsOperation.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Test-AzDevOpsOperation.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Archive/Test-AzDevOpsOperation.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Test-AzDevOpsProject.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Archive/Test-AzDevOpsProject.Tests.ps1.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Test-AzDevOpsProject.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Archive/Test-AzDevOpsProject.Tests.ps1.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 new file mode 100644 index 000000000..afafca243 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 @@ -0,0 +1,196 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-xAzDoGitPermission Tests' { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoGitPermission.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + Function Mock-Get-CacheItem { + param ( + [string]$Key, + [string]$Type + ) + switch ($Type) { + 'LiveRepositories' { return @{ id = 123; Name = "TestRepository" } } + 'SecurityNamespaces' { return @{ namespaceId = "TestNamespaceId" } } + default { return $null } + } + } + + Function Mock-Get-DevOpsACL { + param ( + [Parameter(Mandatory)] + [string]$OrganizationName, + [Parameter(Mandatory)] + [string]$SecurityDescriptorId + ) + return @( @{ Token = @{ Type = 'GitRepository'; RepoId = 123 }; Permission = 'Allow' } ) + } + + Function Mock-ConvertTo-FormattedACL { + param ( + [Parameter(Mandatory)] + $SecurityNamespace, + [Parameter(Mandatory)] + $OrganizationName + ) + return @( @{ Token = @{ Type = 'GitRepository'; RepoId = 123 }; Permission = 'Allow' } ) + } + + Function Mock-ConvertTo-ACL { + param ( + [Parameter(Mandatory)] + $Permissions, + [Parameter(Mandatory)] + $SecurityNamespace, + [Parameter(Mandatory)] + $isInherited, + [Parameter(Mandatory)] + $OrganizationName, + [Parameter(Mandatory)] + $TokenName + ) + return @( @{ Token = @{ Type = 'GitRepository'; RepoId = 123 }; Permission = 'Deny' } ) + } + + Function Mock-Test-ACLListforChanges { + param ( + [Parameter(Mandatory)] + $ReferenceACLs, + [Parameter(Mandatory)] + $DifferenceACLs + ) + return @{ + propertiesChanged = @('Permission'); + status = 'Changed'; + reason = 'Permission mismatch' + } + } + + Mock -CommandName Write-Verbose + Mock -CommandName Write-Warning + Mock -CommandName Get-CacheItem -MockWith { Mock-Get-CacheItem -Key $Key -Type $Type } + Mock -CommandName Get-DevOpsACL -MockWith { Mock-Get-DevOpsACL -OrganizationName $OrganizationName -SecurityDescriptorId $SecurityDescriptorId } + Mock -CommandName ConvertTo-FormattedACL -MockWith { Mock-ConvertTo-FormattedACL -SecurityNamespace $SecurityNamespace -OrganizationName $OrganizationName } + Mock -CommandName ConvertTo-ACL -MockWith { Mock-ConvertTo-ACL -Permissions $Permissions -SecurityNamespace $SecurityNamespace -isInherited $isInherited -OrganizationName $OrganizationName -TokenName $TokenName } + Mock -CommandName Test-ACLListforChanges -MockWith { Mock-Test-ACLListforChanges -ReferenceACLs $ReferenceACLs -DifferenceACLs $DifferenceACLs } + } + + It 'Should retrieve repository and namespace, and compare ACLs correctly' { + $ProjectName = 'TestProject' + $RepositoryName = 'TestRepository' + $isInherited = $true + $Permissions = @(@{ 'Permission' = 'Deny' }) + + $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + + $result | Should -Not -BeNullOrEmpty + $result.status | Should -Be 'Changed' + $result.propertiesChanged | Should -Contain 'Permission' + } + + It "Should return 'Unchanged' if the permissions are the same" { + $ProjectName = 'TestProject' + $RepositoryName = 'TestRepository' + $isInherited = $true + $Permissions = @(@{ 'Permission' = 'Allow' }) + + Mock -CommandName Test-ACLListforChanges -MockWith { + return @{ + propertiesChanged = @() + status = 'Unchanged' + reason = 'No change' + } + } + + $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + + $result | Should -Not -BeNullOrEmpty + $result.status | Should -Be 'Unchanged' + $result.propertiesChanged | Should -BeNullOrEmpty + } + + It "Should returned 'Changed' if one of the permissions is null" { + $ProjectName = 'TestProject' + $RepositoryName = 'TestRepository' + $isInherited = $true + $Permissions = @(@{ 'Permission' = $null }) + + $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + + $result | Should -Not -BeNullOrEmpty + $result.status | Should -Be 'Changed' + $result.propertiesChanged | Should -Contain 'Permission' + } + + It "Should return 'NotFound' if the repository is not found" { + + Mock -CommandName Get-CacheItem -MockWith { return $null } -ParameterFilter { $Type -eq 'LiveRepositories' } + + $ProjectName = 'TestProject' + $RepositoryName = 'NotFoundRepository' + $isInherited = $true + $Permissions = @(@{ 'Permission' = 'Allow' }) + + $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + + $result | Should -Not -BeNullOrEmpty + $result.status | Should -Be 'NotFound' + $result.propertiesChanged | Should -BeNullOrEmpty + } + + It "Should return 'NotFound' if Get-DevOpsACL is null" { + Mock -CommandName Get-DevOpsACL -MockWith { return $null } + Mock -CommandName Write-Warning -Verifiable + + $ProjectName = 'TestProject' + $RepositoryName = 'TestRepository' + $isInherited = $true + $Permissions = @(@{ 'Permission' = 'Allow' }) + + $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + + $result | Should -Not -BeNullOrEmpty + $result.status | Should -Be 'NotFound' + Assert-VerifiableMock + } + + It "Should return 'NotFound' if ConvertTo-FormattedACL is null" { + Mock -CommandName ConvertTo-FormattedACL -MockWith { return $null } + Mock -CommandName Write-Warning -Verifiable + + $ProjectName = 'TestProject' + $RepositoryName = 'TestRepository' + $isInherited = $true + $Permissions = @(@{ 'Permission' = 'Allow' }) + + $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + + $result | Should -Not -BeNullOrEmpty + $result.status | Should -Be 'NotFound' + Assert-VerifiableMock + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 new file mode 100644 index 000000000..bf37d325d --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 @@ -0,0 +1,136 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'New-xAzDoGitPermission' { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoGitPermission.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + . (Get-ClassFilePath '002.LocalizedDataAzSerializationPatten') + + Mock -CommandName Get-CacheItem -MockWith { return @{ namespaceId = '12345'; id = '67890' } } + Mock -CommandName ConvertTo-ACLHashtable -MockWith { return @{} } + Mock -CommandName Set-xAzDoPermission -MockWith { } + } + + Context 'With mandatory parameters provided' { + It 'should call Get-CacheItem for SecurityNamespace and Project' { + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + isInherited = $true + } + New-xAzDoGitPermission @params + + Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly 1 + } + + It 'should call ConvertTo-ACLHashtable and Set-xAzDoPermission' { + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + isInherited = $true + LookupResult = @{ propertiesChanged = @{} } + } + New-xAzDoGitPermission @params + + Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Exactly 1 + Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly 1 + } + } + + Context 'With all parameters provided' { + It 'should set permissions correctly' { + $permissions = @(@{ Permission = 'Read'; Access = 'Allow' }) + + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + isInherited = $true + Permissions = $permissions + LookupResult = @{ propertiesChanged = @{} } + Ensure = 'Present' + Force = $true + } + New-xAzDoGitPermission @params + + Assert-MockCalled -CommandName Get-CacheItem -Times 2 + Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Exactly 1 + Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly 1 + } + } + + Context 'When Get-CacheItem returns nothing' { + It 'should not call ConvertTo-ACLHashtable or Set-xAzDoPermission' { + + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'SecurityNamespaces' } -MockWith { return $null } + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'LiveProjects' } -MockWith { return $null } + Mock -CommandName Write-Warning -Verifiable + + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + isInherited = $true + } + New-xAzDoGitPermission @params + + Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Exactly 0 + Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly 0 + Assert-VerifiableMock + } + } + + # Not in use + Context 'When Force switch is provided' -skip { + It 'should handle the Force switch correctly' { + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + isInherited = $true + Force = $true + } + New-xAzDoGitPermission @params + + # Verify if any additional logic related to -Force was executed + # This is a placeholder as the current implementation does not use -Force + } + } + + Context 'Verbose output' { + It 'should write verbose output' { + + Mock -CommandName Write-Verbose -Verifiable + + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + isInherited = $true + } + + New-xAzDoGitPermission @params + + Assert-VerifiableMock + + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 new file mode 100644 index 000000000..07a31994a --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 @@ -0,0 +1,158 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Remove-xAzDoGitPermission" { + + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoGitPermission.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + . (Get-ClassFilePath '002.LocalizedDataAzSerializationPatten') + + Mock -CommandName Remove-xAzDoPermission + Mock -CommandName Get-CacheItem -MockWith { + switch ($Type) + { + 'SecurityNamespaces' { @{ namespaceId = 'namespaceIdValue' } } + 'LiveProjects' { @{ id = 'projectIdValue' } } + 'LiveRepositories' { @{ id = 'repositoryIdValue' } } + 'LiveACLList' { @(@{ token = 'repoV2/projectIdValue/repositoryIdValue' }) } + default { $null } + } + } + + $Global:DSCAZDO_OrganizationName = 'TestOrg' + + } + + BeforeEach { + + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + isInherited = $false + Permissions = @() + LookupResult = @{} + Ensure = 'Present' + Force = $false + } + + } + + It "Removes ACLs if Filtered is not null" { + + Mock -CommandName 'Write-Verbose' -Verifiable + + Remove-xAzDoGitPermission @params + + Assert-MockCalled -CommandName Remove-xAzDoPermission -Times 1 + Assert-VerifiableMock + + } + + It "Does not call Remove-GitRepositoryPermission if Filtered is null" { + + Mock -CommandName Write-Error -Verifiable + Mock -CommandName Get-CacheItem -MockWith { + switch ($Type) { + 'LiveACLList' { @(@{ token = 'repoV2/notMatchingValue' }) } + default { $null } + } + } + + Remove-xAzDoGitPermission @params + + Assert-MockCalled -CommandName Remove-xAzDoPermission -Exactly 0 + Assert-VerifiableMock + + } + + It "Does not call Remove-GitRepositoryPermission if ACLs are null" { + + Mock -CommandName Write-Error -Verifiable + Mock -CommandName Get-CacheItem -MockWith { + switch ($Type) { + 'LiveACLList' { $null } + default { $null } + } + } + + Remove-xAzDoGitPermission @params + + Assert-MockCalled -CommandName Remove-xAzDoPermission -Exactly 0 + Assert-VerifiableMock + + } + + It "Does not call Remove-GitRepositoryPermission if Repository is null" { + + Mock -CommandName Write-Error -Verifiable + Mock -CommandName Get-CacheItem -MockWith { + switch ($Type) { + 'LiveRepositories' { $null } + default { $null } + } + } + + Remove-xAzDoGitPermission @params + + Assert-MockCalled -CommandName Remove-xAzDoPermission -Exactly 0 + Assert-VerifiableMock + + } + + It "Does not call Remove-GitRepositoryPermission if Project is null" { + + Mock -CommandName Write-Error -Verifiable + Mock -CommandName Get-CacheItem -MockWith { + switch ($Type) { + 'LiveProjects' { $null } + default { $null } + } + } + + Remove-xAzDoGitPermission @params + + Assert-MockCalled -CommandName Remove-xAzDoPermission -Exactly 0 + Assert-VerifiableMock + + } + + It "Does not call Remove-GitRepositoryPermission if SecurityNamespace is null" { + + Mock -CommandName Write-Error -Verifiable + Mock -CommandName Get-CacheItem -MockWith { + switch ($Type) { + 'SecurityNamespaces' { $null } + default { $null } + } + } + + Remove-xAzDoGitPermission @params + + Assert-MockCalled -CommandName Remove-xAzDoPermission -Exactly 0 + Assert-VerifiableMock + + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 new file mode 100644 index 000000000..78d50316a --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 @@ -0,0 +1,107 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Set-xAzDoGitPermission' { + + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoGitPermission.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + . (Get-ClassFilePath '002.LocalizedDataAzSerializationPatten') + + Mock -CommandName Get-CacheItem -MockWith { return @{ namespaceId = 'SampleNamespaceId' } } + Mock -CommandName ConvertTo-ACLHashtable -MockWith { return 'SerializedACLs' } + Mock -CommandName Set-xAzDoPermission + + $ProjectName = 'TestProject' + $RepositoryName = 'TestRepo' + $isInherited = $true + $Permissions = @(@{ User = 'TestUser'; Permission = 'Allow' }) + $LookupResult = @{ propertiesChanged = 'someValue' } + $Ensure = [Ensure]::Present + + $params = @{ + ProjectName = $ProjectName + RepositoryName = $RepositoryName + isInherited = $isInherited + Permissions = $Permissions + LookupResult = $LookupResult + Ensure = $Ensure + } + + $Global:DSCAZDO_OrganizationName = 'TestOrg' + + } + + BeforeEach { + + Mock Get-CacheItem -MockWith { + return @{ + namespaceId = 'SampleNamespaceId' + } + } + Mock ConvertTo-ACLHashtable -MockWith { + return 'SerializedACLs' + } + Mock Set-xAzDoPermission -MockWith { + return $null + } + + } + + It 'Calls Get-CacheItem with the correct parameters for security namespace' { + Set-xAzDoGitPermission @params + Assert-MockCalled Get-CacheItem -Exactly 1 -ParameterFilter { ($Key -eq 'Git Repositories') -and ($Type -eq 'SecurityNamespaces') } + } + + It 'Calls Get-CacheItem with the correct parameters for the project' { + Set-xAzDoGitPermission @params + Assert-MockCalled Get-CacheItem -Exactly 1 -ParameterFilter { ($Key -eq $ProjectName) -and ($Type -eq 'LiveProjects') } + } + + It 'Calls Set-xAzDoPermission with the correct parameters' { + Set-xAzDoGitPermission @params + Assert-MockCalled Set-xAzDoPermission -Exactly 1 -ParameterFilter { + ($OrganizationName -eq 'TestOrg') -and + ($SecurityNamespaceID -eq 'SampleNamespaceId') -and + ($SerializedACLs -eq 'SerializedACLs') + } + } + + It 'Serializes ACLs using ConvertTo-ACLHashtable with correct parameters' { + Set-xAzDoGitPermission @params + Assert-MockCalled ConvertTo-ACLHashtable -Exactly 1 -ParameterFilter { + $ReferenceACLs -eq 'someValue' + } + } + + It 'writes an error if Get-CacheItem is null' { + + Mock Get-CacheItem -MockWith { return $null } + Mock Write-Error -Verifiable + + Set-xAzDoGitPermission @params + Assert-VerifiableMock + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 new file mode 100644 index 000000000..8cd3d8829 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 @@ -0,0 +1,90 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Get-xAzDoGitRepository Tests" { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoGitRepository.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + + Mock -CommandName Get-CacheItem -MockWith { + return @{ RepositoryName = $repositoryName } + } + + } + + Context "When repository exists in the live cache" { + + It "should return repository from the live cache with Unchanged status" { + $projectName = "TestProject" + $repositoryName = "TestRepository" + $projectGroupKey = "$projectName\" + + Mock -CommandName Get-CacheItem -MockWith { + return @{ RepositoryName = $repositoryName } + } -ParameterFilter { + $Type -eq 'LiveRepositories' + } + + $result = Get-xAzDoGitRepository -ProjectName $projectName -RepositoryName $repositoryName + + $result.status | Should -Be "Unchanged" + $result.Ensure | Should -Be "Absent" + } + } + + Context "When repository does not exist in the live cache" { + + It "should perform a lookup within the local cache" -skip { + $projectName = "TestProject" + $repositoryName = "TestRepository" + $projectGroupKey = "$projectName\" + + Mock -CommandName Get-CacheItem -MockWith { + return $null + } -ParameterFilter { + ($Key -eq $projectGroupKey) -and ($Type -eq 'Repositories') + } + + $result = Get-xAzDoGitRepository -ProjectName $projectName -RepositoryName $repositoryName + + Assert-MockCalled -CommandName Get-CacheItem -Times 2 -Exactly + } + + It "should return NotFound status" { + $projectName = "TestProject" + $repositoryName = "TestRepository" + $projectGroupKey = "$projectName\" + + Mock -CommandName Get-CacheItem -ParameterFilter { + $Type -eq 'LiveRepositories' + } + + $result = Get-xAzDoGitRepository -ProjectName $projectName -RepositoryName $repositoryName + + $result.status | Should -Be "NotFound" + $result.Ensure | Should -Be "Absent" + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 new file mode 100644 index 000000000..8278af524 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 @@ -0,0 +1,137 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Pester tests for New-xAzDoGitRepository function +Describe "New-xAzDoGitRepository Tests" { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoGitRepository.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + # Mock external cmdlets/functions + Mock -CommandName Get-CacheItem -MockWith { return @{ Name = "TestProject" } } + Mock -CommandName New-GitRepository -MockWith { return @{ Name = $RepositoryName } } + Mock -CommandName Add-CacheItem + Mock -CommandName Export-CacheObject + Mock -CommandName Refresh-CacheObject + + } + + Context "When mandatory parameters are provided" { + + BeforeEach { + $Global:DSCAZDO_OrganizationName = "TestOrg" + } + + It "should call New-GitRepository" { + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + } + New-xAzDoGitRepository @params + + Assert-MockCalled -CommandName New-GitRepository -Exactly -Times 1 + } + + It "should call Add-CacheItem" { + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + } + New-xAzDoGitRepository @params + + Assert-MockCalled -CommandName Add-CacheItem -Exactly -Times 1 + } + + It "should call Export-CacheObject" { + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + } + New-xAzDoGitRepository @params + + Assert-MockCalled -CommandName Export-CacheObject -Exactly -Times 1 + } + + It "should call Refresh-CacheObject" { + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + } + New-xAzDoGitRepository @params + + Assert-MockCalled -CommandName Refresh-CacheObject -Exactly -Times 1 + } + + } + + Context "When optional parameters are provided" { + + It "should pass SourceRepository to New-GitRepository" { + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + SourceRepository= 'SourceRepo' + } + New-xAzDoGitRepository @params + + Assert-MockCalled -CommandName New-GitRepository -ParameterFilter { $RepositoryName -eq 'TestRepo' -and $SourceRepository -eq 'SourceRepo' } + } + + It "should handle Force switch parameter" -skip { + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + Force = $true + } + New-xAzDoGitRepository @params + + # Since Force is not used in function logic directly, verifying other aspects + Assert-MockCalled -CommandName New-GitRepository -Exactly -Times 1 + } + } + + Context 'When the cache returns $null' { + + BeforeEach { + Mock -CommandName Get-CacheItem -MockWith { return $null } + } + + It "should process the repository creation" { + + Mock -CommandName Write-Error -Verifiable + Mock -CommandName Get-CacheItem -MockWith { return $null } + + $params = @{ + ProjectName = 'TestProject' + RepositoryName = 'TestRepo' + } + New-xAzDoGitRepository @params + + Assert-VerifiableMock + Assert-MockCalled -CommandName New-GitRepository -Exactly -Times 0 + } + + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 new file mode 100644 index 000000000..21c6a6398 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 @@ -0,0 +1,100 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Remove-xAzDoGitRepository' { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoGitRepository.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + Mock -CommandName Get-CacheItem -MockWith { + return @{ + Key = "$ProjectName\" + Value = "RepositoryValue" + } + } + + Mock -CommandName Remove-GitRepository -MockWith { + return @{ + Name = $RepositoryName + } + } + + Mock -CommandName Remove-CacheItem + Mock -CommandName Export-CacheObject + + $params = @{ + ProjectName = "TestProject" + RepositoryName = "TestRepository" + Ensure = "Present" + } + + } + + It 'Calls Get-CacheItem with appropriate parameters for Project' { + Remove-xAzDoGitRepository @params + + Assert-MockCalled -CommandName Get-CacheItem -Times 1 -Exactly -ParameterFilter { + $Key -eq "TestProject" -and $Type -eq "LiveProjects" + } + } + + It 'Calls Get-CacheItem with appropriate parameters for Repository' { + Remove-xAzDoGitRepository @params + + Assert-MockCalled -CommandName Get-CacheItem -Times 1 -Exactly -ParameterFilter { + $Key -eq "TestProject\TestRepository" -and $Type -eq "LiveRepositories" + } + } + + It 'Calls Remove-GitRepository with appropriate parameters' { + Remove-xAzDoGitRepository @params + Assert-MockCalled -CommandName Remove-GitRepository -Exactly 1 + } + + It 'Calls Remove-CacheItem with appropriate parameters' { + Remove-xAzDoGitRepository @params + + Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -Exactly -ParameterFilter { + $Key -eq "TestProject\TestRepository" -and $Type -eq "LiveRepositories" + } + } + + It 'Calls Export-CacheObject with appropriate parameters' { + Remove-xAzDoGitRepository @params + + Assert-MockCalled -CommandName Export-CacheObject -Times 1 -Exactly -ParameterFilter { + $CacheType -eq 'LiveRepositories' -and $Content -eq $AzDoLiveRepositories + } + } + + It 'Fails if Project does not exist in LiveProjects cache' { + + Mock -CommandName Write-Error -Verifiable + Mock -CommandName Get-CacheItem -MockWith { return $null } -ParameterFilter { $Type -eq 'LiveProjects' } + + Remove-xAzDoGitRepository @params | Should -BeNullOrEmpty + Assert-VerifiableMock + + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 new file mode 100644 index 000000000..2e5270e7a --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 @@ -0,0 +1,25 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Set-xAzDoGitRepository' -Skip { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoGitRepository.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 new file mode 100644 index 000000000..d3b3454b1 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 @@ -0,0 +1,160 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Get-xAzDoGroupMember Tests" { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoGroupMember.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + # Mock dependencies + Mock -CommandName Format-AzDoGroupMember -MockWith { return "MockKey" } + Mock -CommandName Get-CacheItem -MockWith { + return @( + [PSCustomObject]@{ originId = "MockMember1" }, + [PSCustomObject]@{ originId = "MockMember2" } + ) + } + + Mock -CommandName Find-AzDoIdentity -MockWith { + param ([string]$Identity) + return [PSCustomObject]@{ originId = $Identity } + } + + } + + It "Handles group not found in live cache and no group members in parameters" { + $params = @{ + GroupName = "TestGroup" + GroupMembers = @() + LookupResult = @{} + Ensure = "Absent" + Force = $false + } + + Mock -CommandName Get-CacheItem -MockWith { $null } + + $result = Get-xAzDoGroupMember @params + $result.status | Should -Be ([DSCGetSummaryState]::Unchanged) + } + + It "Handles group members in parameters but not found in live cache" { + $params = @{ + GroupName = "TestGroup" + GroupMembers = @("Member1") + LookupResult = @{} + Ensure = "Absent" + Force = $false + } + + Mock -CommandName Get-CacheItem -MockWith { $null } + + $result = Get-xAzDoGroupMember @params + $result.status | Should -Be ([DSCGetSummaryState]::NotFound) + } + + It "Handles group members not found in live cache but are defined in parameters" { + $params = @{ + GroupName = "TestGroup" + GroupMembers = @("Member1", "Member2") + LookupResult = @{} + Ensure = "Absent" + Force = $false + } + + Mock -CommandName Get-CacheItem -MockWith { $null } + + $result = Get-xAzDoGroupMember @params + $result.status | Should -Be ([DSCGetSummaryState]::NotFound) + } + + It "Handles no group members in parameters but live cache has members" { + $params = @{ + GroupName = "TestGroup" + GroupMembers = @() + LookupResult = @{} + Ensure = "Absent" + Force = $false + } + + $result = Get-xAzDoGroupMember @params + $result.status | Should -Be ([DSCGetSummaryState]::Missing) + } + + It "Handles same group members in parameters and live cache" { + + Mock -CommandName Compare-Object + + $params = @{ + GroupName = "TestGroup" + GroupMembers = @("MockMember1", "MockMember2") + LookupResult = @{} + Ensure = "Absent" + Force = $false + } + + $result = Get-xAzDoGroupMember @params + $result.status | Should -Be ([DSCGetSummaryState]::Unchanged) + + } + + It "Handles different members in live cache and parameters" { + + $params = @{ + GroupName = "TestGroup" + GroupMembers = @("MockMember1", "NewMember") + LookupResult = @{} + Ensure = "Absent" + Force = $false + } + + $result = Get-xAzDoGroupMember @params + $result.status | Should -Be ([DSCGetSummaryState]::Changed) + $result.propertiesChanged[0].action | Should -Be 'Remove' + $result.propertiesChanged[0].value.originId | Should -Be 'MockMember2' + $result.propertiesChanged[1].action | Should -Be 'Add' + $result.propertiesChanged[1].value.originId | Should -Be 'NewMember' + + } + + # -Force is not used in the function logic directly. + It "Handles different members in live cache and parameters with Force" -skip { + + $params = @{ + GroupName = "TestGroup" + GroupMembers = @("MockMember1", "NewMember") + LookupResult = @{} + Ensure = "Absent" + Force = $true + } + + $result = Get-xAzDoGroupMember @params + $result.status | Should -Be ([DSCGetSummaryState]::Changed) + $result.propertiesChanged[0].action | Should -Be 'Remove' + $result.propertiesChanged[0].value.originId | Should -Be 'MockMember2' + $result.propertiesChanged[1].action | Should -Be 'Add' + $result.propertiesChanged[1].value.originId | Should -Be 'NewMember' + + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 new file mode 100644 index 000000000..d2754a81c --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 @@ -0,0 +1,118 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "New-xAzDoGroupMember" { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + Remove-Variable -Name AzDoLiveGroupMembers -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + $global:AzDoLiveGroupMembers = @{} + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoGroupMember.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + + Mock -CommandName Find-AzDoIdentity -MockWith { + param ($Identity) + return [PSCustomObject]@{ + displayName = $Identity + originId = [guid]::NewGuid().ToString() + principalName = 'mockPrincipalName' + } + } + + Mock -CommandName Get-CacheObject -MockWith { + param ($CacheType) + return @() + } + + Mock -CommandName New-DevOpsGroupMember -MockWith { + param ($params, $MemberIdentity) + return $MemberIdentity + } + + Mock -CommandName Add-CacheItem + Mock -CommandName Set-CacheObject + + } + + Context "When valid parameters are passed" { + + It "Should call Find-AzDoIdentity for the group name" { + $GroupName = 'TestGroup' + $GroupMembers = @('User1', 'User2') + + New-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers + + Assert-MockCalled -CommandName Find-AzDoIdentity -Times 3 -Exactly -Scope It + } + + It "Should add members to the group" { + $GroupName = 'TestGroup' + $GroupMembers = @('User1', 'User2') + + New-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers + + Assert-MockCalled -CommandName New-DevOpsGroupMember -Times 2 -Exactly -Scope It + } + + It "Should cache group members" { + $GroupName = 'TestGroup' + $GroupMembers = @('User1', 'User2') + + New-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers + + Assert-MockCalled -CommandName Add-CacheItem -Times 1 -Exactly -Scope It + Assert-MockCalled -CommandName Set-CacheObject -Times 1 -Exactly -Scope It + } + } + + Context "When no members are found" { + + BeforeAll { + Mock -CommandName 'Find-AzDoIdentity' -MockWith { + param ($Identity) + return $null + } + } + + It "Should write an error when no identities are found" { + Mock -CommandName Write-Warning + $GroupName = 'TestGroup' + $GroupMembers = @('User1', 'User2') + + $result = New-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers + Assert-MockCalled -Times 1 -ParameterFilter { $Message -like "*Unable to find identity for member*" } -CommandName Write-Warning + } + + It "Should write a warning when no members are found" { + Mock -CommandName Write-Warning + Mock -CommandName Find-AzDoIdentity -ParameterFilter { ($Identity -eq 'User1') -or ($Identity -eq 'User2') } + $GroupName = 'TestGroup' + $GroupMembers = @('User1', 'User2') + + New-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers + + Assert-MockCalled -CommandName 'Write-Warning' -ParameterFilter { $Message -like "*No group members found*" } + + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 new file mode 100644 index 000000000..cd2f77c41 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 @@ -0,0 +1,139 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Remove-xAzDoGroupMember Tests' { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + Remove-Variable -Name AzDoLiveGroupMembers -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + $global:AzDoLiveGroupMembers = @{} + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoGroupMember.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + Mock -CommandName Find-AzDoIdentity -MockWith { + return @{ principalName = 'mockUser@domain.com' } + } + + Mock -CommandName Get-CacheItem -MockWith { + return @( + @{ principalName = 'userA@domain.com' }, + @{ principalName = 'userB@domain.com' } + ) + } + + Mock -CommandName Remove-DevOpsGroupMember -MockWith { + return @{ Result = 'Success' } + } + + Mock -CommandName Write-Warning + Mock -CommandName Remove-CacheItem + Mock -CommandName Set-CacheObject + Mock -CommandName Format-AzDoProjectName -MockWith { return '[TestProjectName]\GroupName' } + + } + + BeforeEach { + $Global:DSCAZDO_OrganizationName = 'MockOrganization' + $Global:AzDoLiveGroupMembers = 'MockMembers' + } + + Context "when functioning correctly" { + It 'Should remove group members correctly' { + $GroupName = 'TestGroup' + $result = Remove-xAzDoGroupMember -GroupName $GroupName + + Assert-MockCalled -CommandName Find-AzDoIdentity -Times 1 + Assert-MockCalled -CommandName Get-CacheItem -Times 1 + Assert-MockCalled -CommandName Remove-DevOpsGroupMember -Times 2 + + } + + It 'Should update the cache' { + Mock -CommandName Write-Warning + + $GroupName = 'TestGroup' + $result = Remove-xAzDoGroupMember -GroupName $GroupName + + Assert-MockCalled -CommandName Write-Warning -ParameterFilter { $Message -like '*No group members found*'} -Exactly 0 + Assert-MockCalled -CommandName Remove-CacheItem -Times 1 + Assert-MockCalled -CommandName Set-CacheObject -Times 1 + + } + + It 'Should handle an empty members list' { + Mock -CommandName Get-CacheItem -MockWith { + return @() + } + Mock -CommandName Write-Warning + + $GroupName = 'TestGroup' + $result = Remove-xAzDoGroupMember -GroupName $GroupName + + Assert-MockCalled -CommandName Find-AzDoIdentity -Exactly 1 + Assert-MockCalled -CommandName Remove-DevOpsGroupMember -Exactly 0 + Assert-MockCalled -CommandName Remove-CacheItem -Exactly 1 + Assert-MockCalled -CommandName Set-CacheObject -Exactly 1 + + } + + It 'Should handle a bad members list' { + + $GroupName = 'TestGroup' + + Mock -CommandName Find-AzDoIdentity -MockWith { + return @(' ', $null) + } -ParameterFilter { $Identity -ne $GroupName } + + Mock -CommandName Write-Warning + + $result = Remove-xAzDoGroupMember -GroupName $GroupName + + Assert-MockCalled -CommandName Write-Warning -ParameterFilter { $Message -like '*Unable to find identity*'} -Exactly 2 + Assert-MockCalled -CommandName Remove-DevOpsGroupMember -Exactly 0 + Assert-MockCalled -CommandName Remove-CacheItem -Exactly 1 + Assert-MockCalled -CommandName Set-CacheObject -Exactly 1 + } + + } + + Context "when failing" { + + It 'Should handle a missing group identity' { + Mock -CommandName Find-AzDoIdentity -MockWith { + return $null + } + + Mock -CommandName Write-Warning + + $GroupName = 'TestGroup' + $result = Remove-xAzDoGroupMember -GroupName $GroupName + + Assert-MockCalled -CommandName Write-Warning -ParameterFilter { $Message -like '*Unable to find identity*'} -Exactly 1 + Assert-MockCalled -CommandName Remove-DevOpsGroupMember -Exactly 0 + Assert-MockCalled -CommandName Remove-CacheItem -Exactly 0 + Assert-MockCalled -CommandName Set-CacheObject -Exactly 0 + } + + } + + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 new file mode 100644 index 000000000..e18444f1c --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 @@ -0,0 +1,228 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Set-xAzDoGroupMember' { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + Remove-Variable -Name AzDoLiveGroupMembers -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + $global:AzDoLiveGroupMembers = @{} + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoGroupMember.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + + Mock -CommandName Find-AzDoIdentity -MockWith { + return @{ principalName = "GroupName"; originId = "GroupOriginId"; displayName = "Test Group" } + } + Mock -CommandName Format-AzDoProjectName -MockWith { + return "FormattedProjectName" + } + Mock -CommandName Get-CacheItem -MockWith { + return @() + } + + Mock -CommandName Get-Cacheitem -ParameterFilter { $Type -eq 'LiveGroupMembers' } -MockWith { + return @( + @{ principalName = 'user1'; originId = 'user1OriginId'; displayName = 'User 1' }, + @{ principalName = 'user2'; originId = 'user2OriginId'; displayName = 'User 2' } + ) + } + + Mock -CommandName New-DevOpsGroupMember -MockWith { + return $true + } + Mock -CommandName Remove-DevOpsGroupMember -MockWith { + return $true + } + Mock -CommandName Add-CacheItem + Mock -CommandName Set-CacheObject + + $LookupResult = @{ + propertiesChanged = @( + @{ action = 'Add'; value = @{ principalName = 'user1'; originId = 'user1OriginId'; displayName = 'User 1' } }, + @{ action = 'Remove'; value = @{ principalName = 'user2'; originId = 'user2OriginId'; displayName = 'User 2' } } + ) + } + + } + + Context 'When adding a group member' { + + It 'Should call New-DevOpsGroupMember with correct parameters' { + + Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + + $params = @{ + GroupIdentity = @{ + principalName = "GroupName"; + originId = "GroupOriginId"; + displayName = "Test Group" + } + ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + MemberIdentity = @{ + principalName = 'user1'; + originId = 'user1OriginId'; + displayName = 'User 1' + } + } + + Assert-MockCalled -CommandName 'New-DevOpsGroupMember' -Exactly 1 + } + + It 'Should call Remove-DevOpsGroupMember with correct parameters' { + Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + + $params = @{ + GroupIdentity = @{ + principalName = "GroupName"; + originId = "GroupOriginId"; + displayName = "Test Group" + } + ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + MemberIdentity = @{ + principalName = 'user2'; + originId = 'user2OriginId'; + displayName = 'User 2' + } + } + + Assert-MockCalled -CommandName 'Remove-DevOpsGroupMember' -Exactly 1 + } + + it "should add and remove members" { + Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + + $params = @{ + GroupIdentity = @{ + principalName = "GroupName"; + originId = "GroupOriginId"; + displayName = "Test Group" + } + ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + MemberIdentity = @{ + principalName = 'user1'; + originId = 'user1OriginId'; + displayName = 'User 1' + } + } + + Assert-MockCalled -CommandName 'New-DevOpsGroupMember' -Exactly 1 + + $params = @{ + GroupIdentity = @{ + principalName = "GroupName"; + originId = "GroupOriginId"; + displayName = "Test Group" + } + ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + MemberIdentity = @{ + principalName = 'user2'; + originId = 'user2OriginId'; + displayName = 'User 2' + } + } + + Assert-MockCalled -CommandName 'Remove-DevOpsGroupMember' -Exactly 1 + + } + + } + + Context "when a circular reference is detected" { + + it "should ignore adding the member" { + + Mock Write-Warning -Verifiable + + $LookupResult = @{ + propertiesChanged = @( + @{ action = 'Add'; value = @{ principalName = 'user1'; originId = 'GroupOriginId'; displayName = 'User 1' } }, + @{ action = 'Remove'; value = @{ principalName = 'user2'; originId = 'GroupOriginId'; displayName = 'User 2' } } + ) + } + + Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + + Assert-MockCalled -CommandName 'New-DevOpsGroupMember' -Exactly 0 + Assert-MockCalled -CommandName 'Remove-DevOpsGroupMember' -Exactly 0 + Assert-VerifiableMock + + } + + it "should ignore removing the member" { + + Mock Write-Warning -Verifiable + + $LookupResult = @{ + propertiesChanged = @( + @{ action = 'Add'; value = @{ principalName = 'user1'; originId = 'GroupOriginId'; displayName = 'User 1' } }, + @{ action = 'Remove'; value = @{ principalName = 'user2'; originId = 'GroupOriginId'; displayName = 'User 2' } } + ) + } + + Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + + Assert-MockCalled -CommandName 'New-DevOpsGroupMember' -Exactly 0 + Assert-MockCalled -CommandName 'Remove-DevOpsGroupMember' -Exactly 0 + Assert-VerifiableMock + + } + + } + + Context "when functions called return '`$null'" { + + it "should not start when Get-CacheItem returns `$null" { + + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'LiveGroupMembers' } + Mock -CommandName Write-Error + + Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + + Assert-MockCalled -CommandName 'Write-Error' -Exactly 1 -ParameterFilter { $Message -like '*LiveGroupMembers cache for group*' } + Assert-MockCalled -CommandName 'New-DevOpsGroupMember' -Exactly 0 + Assert-MockCalled -CommandName 'Remove-DevOpsGroupMember' -Exactly 0 + + } + + it "should not call New-DevOpsGroupMember" { + + Mock -CommandName New-DevOpsGroupMember -MockWith { return $null } + + Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + + Assert-MockCalled -CommandName 'New-DevOpsGroupMember' -Exactly 1 + + } + + it "should not call Remove-DevOpsGroupMember" { + + Mock -CommandName Remove-DevOpsGroupMember -MockWith { return $null } + + Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + + Assert-MockCalled -CommandName 'Remove-DevOpsGroupMember' -Exactly 1 + + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 new file mode 100644 index 000000000..f314f3cd7 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 @@ -0,0 +1,79 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Test-xAzDoGroupMember' -skip { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + Remove-Variable -Name AzDoLiveGroupMembers -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + $global:AzDoLiveGroupMembers = @{} + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-xAzDoGroupMember.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + $GroupName = "TestGroup" + $GroupMembers = @('User1', 'User2') + $LookupResult = @{ User1 = 'ID1'; User2 = 'ID2' } + $Ensure = 'Present' + $Force = $true + + } + + It 'Should accept mandatory GroupName parameter' { + { Test-xAzDoGroupMember -GroupName $GroupName } | Should -Not -Throw + } + + It 'Should accept optional GroupMembers parameter' { + { Test-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers } | Should -Not -Throw + } + + It 'Should accept optional LookupResult parameter' { + { Test-xAzDoGroupMember -GroupName $GroupName -LookupResult $LookupResult } | Should -Not -Throw + } + + It 'Should accept optional Ensure parameter' { + { Test-xAzDoGroupMember -GroupName $GroupName -Ensure $Ensure } | Should -Not -Throw + } + + It 'Should accept Force switch parameter' { + { Test-xAzDoGroupMember -GroupName $GroupName -Force } | Should -Not -Throw + { Test-xAzDoGroupMember -GroupName $GroupName -Force:$false } | Should -Not -Throw + } + + It 'Should fail without mandatory GroupName parameter' { + { Test-xAzDoGroupMember } | Should -Throw + } + + It 'Should handle null or empty GroupMembers parameter' { + { Test-xAzDoGroupMember -GroupName $GroupName -GroupMembers $null } | Should -Not -Throw + { Test-xAzDoGroupMember -GroupName $GroupName -GroupMembers @() } | Should -Not -Throw + } + + It 'Should handle null or empty LookupResult parameter' { + { Test-xAzDoGroupMember -GroupName $GroupName -LookupResult $null } | Should -Not -Throw + { Test-xAzDoGroupMember -GroupName $GroupName -LookupResult @{} } | Should -Not -Throw + } + + It 'Should return the expected result' { + $result = Test-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers -LookupResult $LookupResult -Ensure $Ensure -Force + $result | Should -Be $return + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 new file mode 100644 index 000000000..35f6b3d9a --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 @@ -0,0 +1,137 @@ +$currentFile = $MyInvocation.MyCommand.Path + + +# Tests are currently disabled. +Describe 'Get-xAzDoGroupPermission' -skip { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoGroupMember.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + + + # Mock dependencies + Mock -CommandName Get-CacheItem -MockWith { + + switch ($Type) { + 'LiveGroups' { + return @{ + id = 'mockOriginId' + name = 'mockOriginName' + } + } + 'LiveProjects' { + return @{ + id = 'mockProjectId' + name = 'mockProjectName' + } + } + 'SecurityNamespaces' { + return @{ + namespaceId = 'mockSecurityNamespaceId' + name = 'mockSecurityNamespaceName' + } + } + } + } + + + Mock -CommandName Get-DevOpsACL -MockWith { + return @( + @{ + Token = @{ + Type = 'GroupPermission' + GroupId = 'mockOriginId' + ProjectId= 'mockProjectId' + } + } + ) + } + + Mock -CommandName ConvertTo-FormattedACL -MockWith { + return @( + @{ + Token = @{ + Type = 'GroupPermission' + GroupId = 'mockOriginId' + ProjectId= 'mockProjectId' + } + } + ) + } + + Mock -CommandName ConvertTo-ACL -MockWith { + return @{ + aces = @{ + Count = 1 + } + token = @{ + Type = 'GroupPermission' + } + } + } + + Mock -CommandName Test-ACLListforChanges -MockWith { + return @{ + propertiesChanged = @('property1', 'property2') + status = 'Compliant' + reason = 'No changes detected' + } + } + + Mock -CommandName Write-Warning + + } + + It 'Should return group result with correct properties when valid GroupName is provided' { + + $result = Get-xAzDoGroupPermission -GroupName 'Project\Group' -isInherited $true + + $result | Should -Not -BeNullOrEmpty + $result.project | Should -Be 'Project' + $result.groupName | Should -Be 'Group' + $result.propertiesChanged | Should -Contain 'property1' + $result.status | Should -Be 'Unchanged' + + } + + It 'Should not throw an error when GroupName is invalid' { + $result = Get-xAzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true + $result | Should -BeNullOrEmpty + } + + It 'Should return null when no ACEs found for the group' { + Mock -CommandName 'ConvertTo-ACL' -MockWith { + param ($Permissions, $SecurityNamespace, $isInherited, $OrganizationName, $TokenName) + return @{ + aces = @{ + Count = 0 + } + } + } + + $result = Get-xAzDoGroupPermission -GroupName 'Project\Group' -isInherited $true + $result | Should -BeNullOrEmpty + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 new file mode 100644 index 000000000..87e187989 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 @@ -0,0 +1,120 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Resource is currently disabled +Describe 'New-xAzDoGroupPermission' -skip { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoGroupMember.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + # Mock dependencies + Mock -CommandName Get-CacheItem -MockWith { + param ($Key, $Type) + switch ($Type) + { + 'SecurityNamespaces' { + return @{ namespaceId = 'mockNamespaceId' } + } + 'LiveProjects' { + return @{ id = 'mockProjectId' } + } + 'LiveGroups' { + return @{ id = 'mockGroupId' } + } + 'LiveACLList' { + return @{} + } + default { + return $null + } + } + } + + Mock -CommandName ConvertTo-ACLHashtable -MockWith { + param ($ReferenceACLs, $DescriptorACLList, $DescriptorMatchToken) + return @{ + aces = @{ + Count = 1 + } + } + } + + Mock -CommandName Set-xAzDoPermission + + } + + It 'Should set permissions when valid GroupName is provided' { + $LookupResult = @{ + propertiesChanged = @('property1', 'property2') + } + $Permissions = @( + @{ + PermissionBit = 'Read' + DisplayName = 'Read' + } + ) + + New-xAzDoGroupPermission -GroupName 'Project\Group' -isInherited $true -Permissions $Permissions -LookupResult $LookupResult -Ensure 'Present' -Force:$true + + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Identity'; Type = 'SecurityNamespaces' } -Times 1 + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Project'; Type = 'LiveProjects' } -Times 1 + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = '[Project]\Group'; Type = 'LiveGroups' } -Times 1 + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'mockNamespaceId'; Type = 'LiveACLList' } -Times 1 + Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Times 1 + Assert-MockCalled -CommandName Set-xAzDoPermission -Times 1 + } + + It 'Should throw a warning when GroupName is invalid' { + { New-xAzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true } | Should -Throw + } + + It 'Should handle case where no LookupResult is provided' { + $result = New-xAzDoGroupPermission -GroupName 'Project\Group' -isInherited $true -Ensure 'Present' -Force:$true + + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Identity'; Type = 'SecurityNamespaces' } -Times 1 + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Project'; Type = 'LiveProjects' } -Times 1 + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = '[Project]\Group'; Type = 'LiveGroups' } -Times 1 + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'mockNamespaceId'; Type = 'LiveACLList' } -Times 1 + Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Times 1 + Assert-MockCalled -CommandName Set-xAzDoPermission -Times 1 + } + + It 'Should not call Set-xAzDoPermission if no ACLs are found' { + Mock -CommandName ConvertTo-ACLHashtable -MockWith { + return @{ + aces = @{ + Count = 0 + } + } + } + + $LookupResult = @{ + propertiesChanged = @('property1', 'property2') + } + + New-xAzDoGroupPermission -GroupName 'Project\Group' -isInherited $true -LookupResult $LookupResult -Ensure 'Present' -Force:$true + + Assert-MockCalled -CommandName Set-xAzDoPermission -Times 0 + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 new file mode 100644 index 000000000..329b1c998 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 @@ -0,0 +1,100 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Tests are currently disabled. +Describe 'Remove-xAzDoGroupPermission' -skip { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoGroupPermission.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + + # Mock dependencies + Mock -CommandName Get-CacheItem -MockWith { + param ($Key, $Type) + switch ($Type) { + 'SecurityNamespaces' { return @{ namespaceId = 'mockNamespaceId' } } + 'LiveProjects' { return @{ id = 'mockProjectId' } } + 'LiveRepositories' { return @{ id = 'mockRepositoryId' } } + 'LiveACLList' { return @( + @{ token = 'repoV2/mockProjectId/mockRepositoryId' }, + @{ token = 'repoV2/anotherProject/anotherRepo' } + ) } + default { return $null } + } + } + + Mock -CommandName Remove-xAzDoPermission -MockWith {} + + } + + It 'Should remove permissions when valid GroupName is provided' { + Remove-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Ensure 'Present' -Force:$true + + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Identity'; Type = 'SecurityNamespaces' } -Times 1 + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Project'; Type = 'LiveProjects' } -Times 1 + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Project\Repository'; Type = 'LiveRepositories' } -Times 1 + Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'mockNamespaceId'; Type = 'LiveACLList' } -Times 1 + Assert-MockCalled -CommandName Remove-xAzDoPermission -Times 1 + } + + It 'Should throw a warning when GroupName is invalid' { + { Remove-xAzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true } | Should -Throw + } + + It 'Should handle case where no matching ACLs are found' { + Mock -CommandName Get-CacheItem -MockWith { + param ($Key, $Type) + if ($Type -eq 'LiveACLList') { + return @( + @{ token = 'repoV2/anotherProject/anotherRepo' } + ) + } + return @{ + namespaceId = 'mockNamespaceId' + id = 'mockProjectId' + } + } + + Remove-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Ensure 'Present' -Force:$true + + Assert-MockCalled -CommandName Remove-xAzDoPermission -Times 0 + } + + It 'Should not call Remove-xAzDoPermission if no ACLs are found' { + Mock -CommandName Get-CacheItem -MockWith { + param ($Key, $Type) + if ($Type -eq 'LiveACLList') { + return @() + } + return @{ + namespaceId = 'mockNamespaceId' + id = 'mockProjectId' + } + } + + Remove-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Ensure 'Present' -Force:$true + + Assert-MockCalled -CommandName Remove-xAzDoPermission -Times 0 + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 new file mode 100644 index 000000000..6ccad77fd --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 @@ -0,0 +1,129 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Tests are currently disabled. +Describe 'Set-xAzDoGroupPermission' -skip { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoGroupPermission.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + + # Mock dependencies + Mock -CommandName Get-CacheItem -MockWith { + param ( + [string]$Key, + [string]$Type + ) + switch ($Type) + { + 'SecurityNamespaces' { + return @{ namespaceId = 'mockNamespaceId' } + } + 'LiveProjects' { + return @{ id = 'mockProjectId' } + } + 'LiveACLList' { + return @( + @{ token = 'repoV2/mockProjectId/mockRepositoryId' }, + @{ token = 'repoV2/anotherProject/anotherRepo' } + ) + } + default { + return $null + } + } + } + + Mock -CommandName ConvertTo-ACLHashtable -MockWith { + param ( + [HashTable]$ReferenceACLs, + [Array]$DescriptorACLList, + [string]$DescriptorMatchToken + ) + return @{ serializedACLs = 'mockSerializedACLs' } + } + + Mock -CommandName Set-xAzDoPermission + + } + + It 'Should throw a warning when GroupName is invalid' { + { Set-xAzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true } | Should -Throw + } + + It 'Should set permissions when valid GroupName is provided' { + $LookupResult = @{ + propertiesChanged = @{} + } + + Set-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Permissions @{} -LookupResult $LookupResult -Ensure 'Present' -Force:$true + + Assert-MockCalled -CommandName Get-CacheItem -Exactly -Times 1 -Scope It -ParameterFilter { + $Key -eq 'Identity' -and $Type -eq 'SecurityNamespaces' + } + Assert-MockCalled -CommandName Get-CacheItem -Exactly -Times 1 -Scope It -ParameterFilter { + $Key -eq $ProjectName -and $Type -eq 'LiveProjects' + } + Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly -Times 1 -Scope It + } + + It 'Should call ConvertTo-ACLHashtable with correct parameters' { + $LookupResult = @{ + propertiesChanged = @{} + } + + Set-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Permissions @{} -LookupResult $LookupResult -Ensure 'Present' -Force:$true + + Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Exactly -Times 1 -Scope It -ParameterFilter { + $ReferenceACLs -eq $LookupResult.propertiesChanged -and + $DescriptorACLList -eq (Get-CacheItem -Key 'mockNamespaceId' -Type 'LiveACLList') -and + $DescriptorMatchToken -eq ('repoV2/mockProjectId/mockRepositoryId') + } + } + + It 'Should not call Set-xAzDoPermission if no ACLs are found' { + Mock -CommandName Get-CacheItem -MockWith { + param ( + [string]$Key, + [string]$Type + ) + if ($Type -eq 'LiveACLList') { + return @() + } + return @{ + namespaceId = 'mockNamespaceId' + id = 'mockProjectId' + } + } + + $LookupResult = @{ + propertiesChanged = @{} + } + + Set-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Permissions @{} -LookupResult $LookupResult -Ensure 'Present' -Force:$true + + Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly -Times 0 -Scope It + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 new file mode 100644 index 000000000..3022f284b --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 @@ -0,0 +1,139 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-xAzDoOrganizationGroup' { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoOrganizationGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + Mock -CommandName Format-AzDoGroup -MockWith { return "[$Global:DSCAZDO_OrganizationName]_$GroupName" } + Mock -CommandName Get-CacheItem -MockWith { + switch($Type) { + 'LiveGroups' { + return @{ originId = '123'; description = 'Test Group'; name = 'TestGroup' } + } + 'Group' { + return @{ originId = '123'; description = 'Test Group'; name = 'TestGroup'} + } + } + } + + } + + Context 'When group is present in live cache and local cache with same originId' -skip { + BeforeAll { + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'LiveGroups' } -MockWith { + return @{ originId = '123'; description = 'Test Group'; name = 'TestGroup' } + } + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'Group' } -MockWith { + return @{ originId = '123'; description = 'Test Group'; name = 'TestGroup' } + } + } + + It 'should return unchanged status if properties match' { + $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' + $result.status | Should -Be 'Unchanged' + $result.Ensure | Should -Be 'Present' + } + + It 'should return changed status if properties differ' { + $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'New Description' + $result.status | Should -Be 'Changed' + $result.propertiesChanged | Should -Contain 'Description' + } + } + + Context 'When group is renamed' { + BeforeAll { + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'LiveGroups' } -MockWith { + return @{ originId = '123'; description = 'Test Group'; name = 'NewTestGroup' } + } + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'Group' } -MockWith { + return @{ originId = '123'; description = 'Test Group'; name = 'OldTestGroup' } + } + } + + It 'should detect renamed group' { + $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' + $result.status | Should -Be 'Changed' + $result.propertiesChanged[0] | Should -Be 'Name' + } + } + + Context 'When group is missing in live cache but present in local cache' { + BeforeAll { + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'LiveGroups' } -MockWith { + return $null + } + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'Group' } -MockWith { + return @{ originId = '123'; description = 'Test Group'; name = 'TestGroup' } + } + } + + It 'should return not found status' { + $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' + $result.status | Should -Be 'NotFound' + $result.propertiesChanged | Should -Be @('description', 'displayName') + } + } + + Context 'When group is present in live cache but missing in local cache' { + BeforeAll { + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'LiveGroups' } -MockWith { + return @{ originId = '123'; description = 'Test Group'; name = 'TestGroup' } + } + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'Group' } -MockWith { + return $null + } + } + + It 'should return changed' { + $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' + $result.status | Should -Be 'Changed' + } + + It 'should return changed if properties differ' { + $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'New Description' + $result.status | Should -Be 'Changed' + $result.propertiesChanged | Should -Contain 'description' + } + } + + Context 'When both live cache and local cache are missing the group' { + BeforeAll { + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'LiveGroups' } -MockWith { + return $null + } + Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'Group' } -MockWith { + return $null + } + } + + It 'should return not found status' { + $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' + $result.status | Should -Be 'NotFound' + $result.propertiesChanged | Should -Be @('description', 'displayName') + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 new file mode 100644 index 000000000..f21e740c3 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 @@ -0,0 +1,76 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'New-xAzDoOrganizationGroup' { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoOrganizationGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + # Mock the external functions used within New-xAzDoOrganizationGroup + Mock -CommandName New-DevOpsGroup -MockWith { + return @{ + principalName = "testPrincipalName" + } + } + + Mock -CommandName Refresh-CacheIdentity + Mock -CommandName Add-CacheItem + Mock -CommandName Set-CacheObject + + } + + Context 'When GroupName is provided' { + It 'Should create a new DevOps group with correct parameters' { + $params = @{ + GroupName = 'TestGroup' + GroupDescription = 'Test Description' + } + + $result = New-xAzDoOrganizationGroup @params + + Assert-MockCalled -CommandName New-DevOpsGroup -Exactly -Times 1 + Assert-MockCalled -CommandName Refresh-CacheIdentity -Exactly -Times 1 + Assert-MockCalled -CommandName Add-CacheItem -Exactly -Times 1 + Assert-MockCalled -CommandName Set-CacheObject -Exactly -Times 1 + } + } + + Context 'Verbose logs' { + It 'Should log verbose messages' { + + Mock -CommandName Write-Verbose + + $params = @{ + GroupName = 'TestGroup' + GroupDescription = 'Test Description' + Verbose = $true + } + + $verboseOutput = New-xAzDoOrganizationGroup @params + + Assert-MockCalled -CommandName Write-Verbose -ParameterFilter { $Message -like "*Creating a new DevOps group with GroupName: 'TestGroup', GroupDescription: 'Test Description'*" } + Assert-MockCalled -CommandName Write-Verbose -ParameterFilter { $Message -like "*Updated global AzDoGroup cache object.*" } + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 new file mode 100644 index 000000000..d23c13c32 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 @@ -0,0 +1,127 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Remove-xAzDoOrganizationGroup' { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + Remove-Variable -Name AZDOLiveGroups -Scope Global + Remove-Variable -Name AzDoGroup -Scope Global + } + + BeforeAll { + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoOrganizationGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + # Mock the external functions used within Remove-xAzDoOrganizationGroup + Mock -CommandName Remove-DevOpsGroup + Mock -CommandName Remove-CacheItem + Mock -CommandName Set-CacheObject + + } + + BeforeEach { + # Reset global variables before each test + $Global:DSCAZDO_OrganizationName = "TestOrg" + $Global:AZDOLiveGroups = @{} + $Global:AzDoGroup = @{} + } + + Context 'When no cache items exist' { + It 'Should return without performing any operations' { + $lookupResult = @{ + liveCache = $null + localCache = $null + } + + Remove-xAzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult + + Assert-MockCalled -CommandName Remove-DevOpsGroup -Times 0 + Assert-MockCalled -CommandName Remove-CacheItem -Times 0 + Assert-MockCalled -CommandName Set-CacheObject -Times 0 + } + } + + Context 'When group is found in live cache' { + It 'Should remove the group and update the caches' { + $lookupResult = @{ + liveCache = @{ + Descriptor = 'LiveDescriptor' + principalName = 'livePrincipalName' + } + localCache = $null + } + + Remove-xAzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult + + Assert-MockCalled -CommandName Remove-DevOpsGroup -Exactly -Times 1 -ParameterFilter { + $GroupDescriptor -eq 'LiveDescriptor' -and + $ApiUri -eq 'https://vssps.dev.azure.com/TestOrg' + } + + Assert-MockCalled -CommandName Remove-CacheItem -Exactly -Times 2 + Assert-MockCalled -CommandName Set-CacheObject -Exactly -Times 2 + } + } + + Context 'When group is found in local cache but not in live cache' { + It 'Should remove the group and update the caches' { + $lookupResult = @{ + liveCache = $null + localCache = @{ + Descriptor = 'LocalDescriptor' + principalName = 'localPrincipalName' + } + } + + Remove-xAzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult + + Assert-MockCalled -CommandName Remove-DevOpsGroup -Exactly -Times 1 -ParameterFilter { + $GroupDescriptor -eq 'LocalDescriptor' -and + $ApiUri -eq 'https://vssps.dev.azure.com/TestOrg' + } + + Assert-MockCalled -CommandName Remove-CacheItem -Exactly -Times 2 + Assert-MockCalled -CommandName Set-CacheObject -Exactly -Times 2 + } + } + + Context 'When both live and local cache are present' { + It 'Should prioritize live cache and remove the group' { + $lookupResult = @{ + liveCache = @{ + Descriptor = 'LiveDescriptor' + principalName = 'livePrincipalName' + } + localCache = @{ + Descriptor = 'LocalDescriptor' + principalName = 'localPrincipalName' + } + } + + Remove-xAzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult + + Assert-MockCalled -CommandName Remove-DevOpsGroup -Exactly -Times 1 -ParameterFilter { + $GroupDescriptor -eq 'LiveDescriptor' -and + $ApiUri -eq 'https://vssps.dev.azure.com/TestOrg' + } + + Assert-MockCalled -CommandName Remove-CacheItem -Exactly -Times 2 + Assert-MockCalled -CommandName Set-CacheObject -Exactly -Times 2 + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 new file mode 100644 index 000000000..33ce9a8cf --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 @@ -0,0 +1,175 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Set-xAzDoOrganizationGroup' { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + Remove-Variable -Name AzDoGroup -Scope Global + } + + BeforeAll { + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoOrganizationGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + # Mock the external functions used within Set-xAzDoOrganizationGroup + Mock -CommandName Set-DevOpsGroup -MockWith { + return @{ + principalName = 'testPrincipalName' + descriptor = 'testDescriptor' + } + } + + Mock -CommandName Refresh-CacheIdentity + Mock -CommandName Remove-CacheItem + Mock -CommandName Add-CacheItem + Mock -CommandName Set-CacheObject + Mock -CommandName Write-Warning + + } + BeforeEach { + # Reset global variables before each test + $Global:DSCAZDO_OrganizationName = "TestOrg" + $Global:AzDoGroup = @{} + } + + Context 'When group has been renamed' { + It 'Should write a warning and return without setting the group' { + $lookupResult = @{ + Status = [DSCGetSummaryState]::Renamed + liveCache = @{ + descriptor = 'liveDescriptor' + } + localCache = @{ + principalName = 'localPrincipalName' + } + } + + $result = Set-xAzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult + + $result | Should -BeNullOrEmpty + Assert-MockCalled -CommandName Set-DevOpsGroup -Times 0 -Scope It + Assert-MockCalled -CommandName Refresh-CacheIdentity -Times 0 -Scope It + Assert-MockCalled -CommandName Remove-CacheItem -Times 0 -Scope It + Assert-MockCalled -CommandName Add-CacheItem -Times 0 -Scope It + Assert-MockCalled -CommandName Set-CacheObject -Times 0 -Scope It + } + } + + Context 'When group needs to be set' { + It 'Should set the group and update the caches' { + $lookupResult = @{ + Status = [DSCGetSummaryState]::None + liveCache = @{ + descriptor = 'liveDescriptor' + } + localCache = @{ + principalName = 'localPrincipalName' + } + } + + $result = Set-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Description' -LookupResult $lookupResult + + $result | Should -Not -BeNullOrEmpty + $result.principalName | Should -Be 'testPrincipalName' + + Assert-MockCalled -CommandName Set-DevOpsGroup -Exactly -Times 1 -ParameterFilter { + $ApiUri -eq 'https://vssps.dev.azure.com/TestOrg' -and + $GroupName -eq 'TestGroup' -and + $GroupDescription -eq 'Test Description' -and + $GroupDescriptor -eq 'liveDescriptor' + } + + Assert-MockCalled -CommandName Refresh-CacheIdentity -Exactly -Times 1 -ParameterFilter { + $Identity.principalName -eq 'testPrincipalName' -and + $CacheType -eq 'LiveGroups' + } + + Assert-MockCalled -CommandName Remove-CacheItem -Exactly -Times 1 -ParameterFilter { + $Key -eq 'localPrincipalName' -and + $Type -eq 'Group' + } + + Assert-MockCalled -CommandName Add-CacheItem -Exactly -Times 1 -ParameterFilter { + $Key -eq 'testPrincipalName' -and + $Type -eq 'Group' + } + + Assert-MockCalled -CommandName Set-CacheObject -Exactly -Times 1 -ParameterFilter { + $CacheType -eq 'Group' + } + } + } + + Context 'When there is no local cache' { + It 'Should set the group and update the caches without removing any cache item' { + $lookupResult = @{ + Status = [DSCGetSummaryState]::None + liveCache = @{ + descriptor = 'liveDescriptor' + } + localCache = $null + } + + $result = Set-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Description' -LookupResult $lookupResult + + $result | Should -Not -BeNullOrEmpty + $result.principalName | Should -Be 'testPrincipalName' + + Assert-MockCalled -CommandName Set-DevOpsGroup -Exactly -Times 1 -ParameterFilter { + $ApiUri -eq 'https://vssps.dev.azure.com/TestOrg' -and + $GroupName -eq 'TestGroup' -and + $GroupDescription -eq 'Test Description' -and + $GroupDescriptor -eq 'liveDescriptor' + } + + Assert-MockCalled -CommandName Refresh-CacheIdentity -Exactly -Times 1 -ParameterFilter { + $Identity.principalName -eq 'testPrincipalName' -and + $CacheType -eq 'LiveGroups' + } + + Assert-MockCalled -CommandName Remove-CacheItem -Times 0 -Scope It + Assert-MockCalled -CommandName Add-CacheItem -Exactly -Times 1 -ParameterFilter { + $Key -eq 'testPrincipalName' -and + $Type -eq 'Group' + } + + Assert-MockCalled -CommandName Set-CacheObject -Exactly -Times 1 -ParameterFilter { + $CacheType -eq 'Group' + } + } + } + + Context 'When an exception occurs while setting the group' { + + It 'Should throw the exception' { + + Mock -CommandName Set-DevOpsGroup -MockWith { + throw 'An error occurred' + } + + $lookupResult = @{ + Status = [DSCGetSummaryState]::None + liveCache = @{ + descriptor = 'liveDescriptor' + } + localCache = $null + } + + { Set-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Description' -LookupResult $lookupResult } | Should -Throw 'An error occurred' + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 new file mode 100644 index 000000000..00734e148 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 @@ -0,0 +1,77 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Pester tests +# Not required to run in the pipeline +Describe "Test-xAzDoOrganizationGroup" -skip { + + BeforeAll { + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-xAzDoOrganizationGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Mock the external functions used within Test-xAzDoOrganizationGroup + Mock -CommandName Test-xAzDoOrganizationGroup + + } + + Context "when the group exists" { + It "should return true" { + # Mock Test-xAzDoOrganizationGroup function to simulate group existence + Mock -CommandName Test-xAzDoOrganizationGroup -MockWith { + param ( + [string]$GroupName, + [string]$Pat, + [string]$ApiUri + ) + return $true + } + + $result = Test-xAzDoOrganizationGroup -GroupName 'ExistingGroup' -Pat 'dummyPat' -ApiUri 'https://dev.azure.com/myorg' + $result | Should -Be $true + } + } + + Context "when the group does not exist" { + It "should return false" { + # Mock Test-xAzDoOrganizationGroup function to simulate group non-existence + Mock -CommandName Test-xAzDoOrganizationGroup -MockWith { + param ( + [string]$GroupName, + [string]$Pat, + [string]$ApiUri + ) + return $false + } + + $result = Test-xAzDoOrganizationGroup -GroupName 'NonExistentGroup' -Pat 'dummyPat' -ApiUri 'https://dev.azure.com/myorg' + $result | Should -Be $false + } + } + + Context "when there is an empty GroupName parameter" { + It "should throw an error" { + { Test-xAzDoOrganizationGroup -GroupName '' -Pat 'dummyPat' -ApiUri 'https://dev.azure.com/myorg' } | Should -Throw + } + } + + Context "when there is an empty Pat parameter" { + It "should throw an error" { + { Test-xAzDoOrganizationGroup -GroupName 'ExistingGroup' -Pat '' -ApiUri 'https://dev.azure.com/myorg' } | Should -Throw + } + } + + Context "when there is an empty ApiUri parameter" { + It "should throw an error" { + { Test-xAzDoOrganizationGroup -GroupName 'ExistingGroup' -Pat 'dummyPat' -ApiUri '' } | Should -Throw + } + } + +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 new file mode 100644 index 000000000..927a4fd26 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 @@ -0,0 +1,117 @@ +$currentFile = $MyInvocation.MyCommand.Path +# Pester tests for Get-xAzDoProject + +Describe "Get-xAzDoProject" { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + # Set the organization name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoProject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + # Define common mock responses + $mockProject = @{ + ProjectName = 'ExistingProject' + description = 'ExistingDescription' + SourceControlType = 'Git' + Visibility = 'Private' + } + + $mockProcessTemplate = @{ + ProcessTemplate = 'Agile' + } + + Mock -CommandName Test-AzDevOpsProjectName -MockWith { return $true } + Mock -CommandName Write-Warning + + } + + Context "when the project exists" { + BeforeEach { + # Mock Get-CacheItem to return an existing project + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'ExistingProject' -and $Type -eq 'LiveProjects' } -MockWith { return $mockProject } + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'Agile' -and $Type -eq 'LiveProcesses' } -MockWith { return $mockProcessTemplate } + } + + It "should return the project details with status unchanged" { + $result = Get-xAzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' + $result.Status | Should -BeNullOrEmpty + $result.ProjectName | Should -Be 'ExistingProject' + $result.ProjectDescription | Should -Be 'ExistingDescription' + } + + It "should return status changed when descriptions differ" { + $result = Get-xAzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'NewDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' + $result.Status | Should -Be 'Changed' + $result.propertiesChanged | Should -Contain 'Description' + } + + It "should return status changed when visibility differs" { + $result = Get-xAzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Public' + $result.Status | Should -Be 'Changed' + $result.propertiesChanged | Should -Contain 'Visibility' + } + } + + Context "when the project does not exist" { + BeforeEach { + # Mock Get-CacheItem to return null for non-existing project + Mock -CommandName Get-CacheItem -ParameterFilter { $true } -MockWith { return $null } + } + + It "should return status NotFound" { + $result = Get-xAzDoProject -ProjectName 'NonExistentProject' -ProjectDescription 'AnyDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' + $result.Status | Should -Be 'NotFound' + } + } + + Context "when the process template does not exist" { + BeforeEach { + # Mock Get-CacheItem to return null for non-existing process template + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'ExistingProject' -and $Type -eq 'LiveProjects' } -MockWith { return $mockProject } + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'NonExistentTemplate' -and $Type -eq 'LiveProcesses' } -MockWith { return $null } + } + + It "should throw an error" { + { Get-xAzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'NonExistentTemplate' -Visibility 'Private' } | Should -Throw + } + } + + Context "when source control type differs" { + BeforeEach { + # Mock Get-CacheItem to return an existing project with different source control type + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'ExistingProject' -and $Type -eq 'LiveProjects' } -MockWith { + $mockProject.SourceControlType = 'Tfvc' + return $mockProject + } + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'Agile' -and $Type -eq 'LiveProcesses' } -MockWith { return $mockProcessTemplate } + } + + It "should warn about source control type conflict" { + $result = Get-xAzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' + $result.Status | Should -BeNullOrEmpty + $result.ProjectName | Should -Be 'ExistingProject' + $result.SourceControlType | Should -Be 'Git' + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 new file mode 100644 index 000000000..a9c7fd4ac --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 @@ -0,0 +1,143 @@ +$currentFile = $MyInvocation.MyCommand.Path +# Pester tests for New-xAzDoProject + +Describe "New-xAzDoProject" { + + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + # Set the organization name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoProject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + # Define common mock responses + $mockProcessTemplate = @{ + id = '12345' + ProcessTemplate = 'Agile' + } + + $mockProjectJob = @{ + url = 'https://dev.azure.com/TestOrg/_apis/projects/ExistingProject' + } + + Mock -CommandName Test-AzDevOpsProjectName -MockWith { return $true } + + } + + Context "when parameters are valid" { + BeforeEach { + # Mock Get-CacheItem to return a process template + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'Agile' -and $Type -eq 'LiveProcesses' } -MockWith { return $mockProcessTemplate } + + # Mock New-DevOpsProject to simulate project creation + Mock -CommandName New-DevOpsProject -MockWith { return $mockProjectJob } + + # Mock Wait-DevOpsProject to simulate waiting for project creation + Mock -CommandName Wait-DevOpsProject + + # Mock AzDoAPI_0_ProjectCache to simulate cache refresh + Mock -CommandName AzDoAPI_0_ProjectCache + + } + + It "should create a new project with specified parameters" { + New-xAzDoProject -ProjectName 'NewProject' -ProjectDescription 'New Project Description' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' + + # Validate that Get-CacheItem was called with correct parameters + Assert-MockCalled -CommandName Get-CacheItem -Exactly 1 -ParameterFilter { $Key -eq 'Agile' -and $Type -eq 'LiveProcesses' } + + # Validate that New-DevOpsProject was called with correct parameters + Assert-MockCalled -CommandName New-DevOpsProject -Exactly 1 -ParameterFilter { + ($organization -eq 'TestOrganization') -and + ($projectName -eq 'NewProject') -and + ($description -eq 'New Project Description') -and + ($sourceControlType -eq 'Git') -and + ($processTemplateId -eq '12345') -and + ($visibility -eq 'Private') + } + + # Validate that Wait-DevOpsProject was called with correct parameters + Assert-MockCalled -CommandName Wait-DevOpsProject -Exactly 1 -ParameterFilter { + $ProjectURL -eq 'https://dev.azure.com/TestOrg/_apis/projects/ExistingProject' -and + $OrganizationName -eq 'TestOrganization' + } + + # Validate that AzDoAPI_0_ProjectCache was called with correct parameters + Assert-MockCalled -CommandName AzDoAPI_0_ProjectCache -Exactly 1 -ParameterFilter { + $OrganizationName -eq 'TestOrganization' + } + } + } + + Context "when process template does not exist" { + BeforeEach { + # Mock Get-CacheItem to return null for non-existing process template + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'NonExistentTemplate' -and $Type -eq 'LiveProcesses' } -MockWith { return $null } + } + + It "should throw an error if process template is not found" { + { New-xAzDoProject -ProjectName 'NewProject' -ProjectDescription 'New Project Description' -SourceControlType 'Git' -ProcessTemplate 'NonExistentTemplate' -Visibility 'Private' } | Should -Throw + } + } + + Context "when force parameter is used" -skip { + BeforeEach { + # Mock Get-CacheItem to return a process template + Mock -CommandName Get-CacheItem -ParameterFilter { $Key -eq 'Agile' -and $Type -eq 'LiveProcesses' } -MockWith { return $mockProcessTemplate } + + # Mock New-DevOpsProject to simulate project creation + Mock -CommandName New-DevOpsProject -MockWith { return $mockProjectJob } + + # Mock Wait-DevOpsProject to simulate waiting for project creation + Mock -CommandName Wait-DevOpsProject + + # Mock AzDoAPI_0_ProjectCache to simulate cache refresh + Mock -CommandName AzDoAPI_0_ProjectCache + } + + It "should create a new project even if it already exists when -Force is used" { + New-xAzDoProject -ProjectName 'NewProject' -ProjectDescription 'New Project Description' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' -Force + + # Validate that New-DevOpsProject was called with correct parameters + Assert-MockCalled -CommandName New-DevOpsProject -Exactly 1 -ParameterFilter { + $organization -eq 'TestOrg' -and + $projectName -eq 'NewProject' -and + $description -eq 'New Project Description' -and + $sourceControlType -eq 'Git' -and + $processTemplateId -eq '12345' -and + $visibility -eq 'Private' + } + + # Validate that Wait-DevOpsProject was called with correct parameters + Assert-MockCalled -CommandName Wait-DevOpsProject -Exactly 1 -ParameterFilter { + $ProjectURL -eq 'https://dev.azure.com/TestOrg/_apis/projects/ExistingProject' -and + $OrganizationName -eq 'TestOrg' + } + + # Validate that AzDoAPI_0_ProjectCache was called with correct parameters + Assert-MockCalled -CommandName AzDoAPI_0_ProjectCache -Exactly 1 -ParameterFilter { + $OrganizationName -eq 'TestOrg' + } + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 new file mode 100644 index 000000000..e8771863c --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 @@ -0,0 +1,97 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Remove-xAzDoProject" { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + # Set the organization name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoProject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + Mock -CommandName Test-AzDevOpsProjectName -MockWith { return $true } + Mock -CommandName Get-CacheItem -MockWith { return @{ id = '12345' } } + Mock -CommandName Remove-DevOpsProject + Mock -CommandName Remove-CacheItem + Mock -CommandName Export-CacheObject + + } + + Context "When the project exists in cache" { + + It "Should remove the project from Azure DevOps and update the cache" { + # Arrange + $Global:DSCAZDO_OrganizationName = "TestOrg" + $projectName = "TestProject" + + # Act + Remove-xAzDoProject -ProjectName $projectName + + # Assert + Assert-MockCalled -CommandName Get-CacheItem -Exactly -Times 1 -ParameterFilter { + ($Key -eq $projectName) -and + ($Type -eq 'LiveProjects') + } + + Assert-MockCalled -CommandName Remove-DevOpsProject -Exactly -Times 1 -ParameterFilter { + ($Organization -eq "TestOrg") -and + ($ProjectId -eq '12345') + } + + Assert-MockCalled -CommandName Remove-CacheItem -Exactly -Times 1 -ParameterFilter { + ($Key -eq $projectName) -and + ($Type -eq 'LiveProjects') + } + + Assert-MockCalled -CommandName Export-CacheObject -Exactly -Times 1 -ParameterFilter { + ($CacheType -eq 'LiveProjects') -and + ($Content -eq $AzDoLiveProjects) + } + + } + } + + Context "When the project does not exist in cache" { + + It "Should not attempt to remove the project or update the cache" { + + Mock -CommandName Get-CacheItem + + # Arrange + $Global:DSCAZDO_OrganizationName = "TestOrg" + $projectName = "NonExistentProject" + + # Act + Remove-xAzDoProject -ProjectName $projectName + + # Assert + Assert-MockCalled -CommandName Get-CacheItem -Exactly -Times 1 -ParameterFilter { + $Key -eq $projectName + $Type -eq 'LiveProjects' + } + Assert-MockCalled -CommandName Remove-DevOpsProject -Exactly -Times 0 + Assert-MockCalled -CommandName Remove-CacheItem -Exactly -Times 0 + Assert-MockCalled -CommandName Export-CacheObject -Exactly -Times 0 + + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 new file mode 100644 index 000000000..934419af3 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 @@ -0,0 +1,94 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Set-xAzDoProject" { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + # Set the organization name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoProject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + Mock -CommandName Test-AzDevOpsProjectName -MockWith { + return $true + } + + Mock -CommandName Get-CacheItem -MockWith { + param ($Key, $Type) + if ($Type -eq 'LiveProjects') + { + return @{ id = '12345' } + } + elseif ($Type -eq 'LiveProcesses') + { + return @{ id = '67890' } + } + } + + Mock -CommandName Update-DevOpsProject -MockWith { + return @{ url = "http://devopsprojecturl" } + } + + Mock -CommandName Wait-DevOpsProject + Mock -CommandName AzDoAPI_0_ProjectCache + + } + + Context "When setting a project" { + + It "Should update the project in Azure DevOps and refresh the cache" { + # Arrange + $Global:DSCAZDO_OrganizationName = "TestOrg" + $projectName = "TestProject" + $projectDescription = "Test Description" + $sourceControlType = "Git" + $processTemplate = "Agile" + $visibility = "Private" + + # Act + Set-xAzDoProject -ProjectName $projectName -ProjectDescription $projectDescription -SourceControlType $sourceControlType -ProcessTemplate $processTemplate -Visibility $visibility + + # Assert + Assert-MockCalled -CommandName Get-CacheItem -Exactly -Times 1 -ParameterFilter { + ($Key -eq $projectName) -and + ($Type -eq 'LiveProjects') + } + Assert-MockCalled -CommandName Get-CacheItem -Exactly -Times 1 -ParameterFilter { + ($Key -eq $processTemplate) -and + ($Type -eq 'LiveProcesses') + } + Assert-MockCalled -CommandName Update-DevOpsProject -Exactly -Times 1 -ParameterFilter { + ($organization -eq "TestOrg") -and + ($projectId -eq '12345') -and + ($description -eq $projectDescription) -and + ($processTemplateId -eq '67890') + } + Assert-MockCalled -CommandName Wait-DevOpsProject -Exactly -Times 1 -ParameterFilter { + ($ProjectURL -eq "http://devopsprojecturl") -and + ($OrganizationName -eq "TestOrg") + } + Assert-MockCalled -CommandName AzDoAPI_0_ProjectCache -Exactly -Times 1 -ParameterFilter { + $OrganizationName -eq "TestOrg" + } + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 new file mode 100644 index 000000000..7340850d2 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 @@ -0,0 +1,78 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "Test-AzDevOpsProject" -Skip { + + BeforeAll { + + # Set the organization name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-xAzDoProject.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + $fakeUri = "https://dev.azure.com/fakeOrganization/_apis/" + $fakePat = "fakePat" + $fakeProjectId = "fakeProjectId" + $fakeProjectName = "fakeProjectName" + + Mock -CommandName Test-AzDevOpsApiUri -MockWith { + return $true + } + + Mock -CommandName Test-AzDevOpsPat -MockWith { + return $true + } + + Mock -CommandName Test-AzDevOpsProjectId -MockWith { + return $true + } + + Mock -CommandName Test-AzDevOpsProjectName -MockWith { + return $true + } + + Mock -CommandName Get-AzDevOpsProject -MockWith { + return @{ id = $fakeProjectId } + } + + } + + It "Should return true when project exists by ProjectId" { + $result = Test-AzDevOpsProject -ApiUri $fakeUri -Pat $fakePat -ProjectId $fakeProjectId + $result | Should -Be $true + } + + It "Should return true when project exists by ProjectName" { + $result = Test-AzDevOpsProject -ApiUri $fakeUri -Pat $fakePat -ProjectName $fakeProjectName + $result | Should -Be $true + } + + It "Should return true when project exists by ProjectId and ProjectName" { + $result = Test-AzDevOpsProject -ApiUri $fakeUri -Pat $fakePat -ProjectId $fakeProjectId -ProjectName $fakeProjectName + $result | Should -Be $true + } + + It "Should return false when project does not exist" { + Mock -CommandName Get-AzDevOpsProject -MockWith { + param ($ApiUri, $Pat, $ProjectId, $ProjectName) + return $null + } + + $result = Test-AzDevOpsProject -ApiUri $fakeUri -Pat $fakePat -ProjectId $fakeProjectId + $result | Should -Be $false + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 new file mode 100644 index 000000000..a6af3dbee --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 @@ -0,0 +1,148 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Get-xAzDoProjectGroup' { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + # Set the organization name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoProjectGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + $mockProjectName = "TestProject" + $mockGroupName = "TestGroup" + $mockDescription = "TestDescription" + + Mock -CommandName Test-AzDevOpsProjectName -MockWith { return $true } + Mock -CommandName Get-CacheItem -MockWith { + switch ($Type) { + 'LiveProjects' { return @{ originId = 1 } } + 'LiveGroups' { return @{ originId = 1 } } + 'Group' { return @{ originId = 1 } } + } + } + Mock -CommandName Remove-CacheItem + Mock -CommandName Add-CacheItem + Mock -CommandName Format-AzDoGroup -MockWith { + return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + } + + } + + It 'should call Get-CacheItem for livegroup lookup' { + Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName + Assert-MockCalled Get-CacheItem -ParameterFilter { + $Key -eq "$mockProjectName" -and $Type -eq 'LiveProjects' + } -Times 1 + } + + It 'should return correct status when livegroup and localgroup originId differ' { + + Mock -CommandName Get-CacheItem -MockWith { return @{ originId = 1 } } -ParameterFilter { + $Type -eq 'LiveGroups' + } + Mock -CommandName Get-CacheItem -MockWith { return @{ originId = 2 } } -ParameterFilter { + $Type -eq 'Group' + } + Mock -CommandName Find-CacheItem -MockWith { return @{ originId = 1 } } + + Mock -CommandName Format-AzDoGroup -MockWith { + return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + } + + $result = Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName + + $result.status | Should -Be 'Renamed' + } + + It 'should return status NotFound when livegroup is absent but localgroup is present' { + Mock -CommandName Get-CacheItem -MockWith { + param ($Key, $Type) + if ($Type -eq 'LiveGroups') { return $null } + if ($Type -eq 'Group') { return @{originId = 1} } + } + + Mock -CommandName Format-AzDoGroup -MockWith { + return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + } + + $result = Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName + + $result.status | Should -Be 'NotFound' + } + + It 'should return status Changed when properties differ' { + Mock -CommandName Get-CacheItem -MockWith { + return @{description = 'OldDescription'; name = $mockGroupName; originId = 1} + } + Mock -CommandName Format-AzDoGroup -MockWith { + return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + } + + $result = Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName -GroupDescription $mockDescription + + $result.status | Should -Be 'Changed' + $result.propertiesChanged | Should -Contain 'description' + } + + It 'should return status Unchanged when properties are same' { + Mock -CommandName Get-CacheItem -MockWith { + return @{description = $mockDescription; name = $mockGroupName; originId = 1} + } + Mock -CommandName Format-AzDoGroup -MockWith { + return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + } + + $result = Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName -GroupDescription $mockDescription + + $result.status | Should -Be 'Unchanged' + } + + It 'should add current livegroup to cache if localgroup was not present' { + + Mock -CommandName Get-CacheItem -MockWith { + if ($Type -eq 'LiveGroups') { + return @{ + description = $mockDescription + displayName = $mockGroupName + originId = 1 + } + } + elseif ($Type -eq 'Group') { + return $null + } + else { + return $null + } + } + + Mock -CommandName Format-AzDoGroup -MockWith { + return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + } + + Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName -GroupDescription $mockDescription + + Assert-MockCalled -CommandName Add-CacheItem -ParameterFilter { + ($Key -eq ("{0}:{1}" -f $mockProjectName, $mockGroupName)) -and ($Type -eq 'Group') + } -Times 1 + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 new file mode 100644 index 000000000..11acf4d7b --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 @@ -0,0 +1,106 @@ +# Save this script as New-xAzDoProjectGroup.Tests.ps1 + +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'New-xAzDoProjectGroup' { + + BeforeAll { + + # Set the organization name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoProjectGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + # Mocking external dependencies + Mock -CommandName Write-Verbose + Mock -CommandName Write-Warning + Mock -CommandName New-DevOpsGroup -MockWith { + return [PSCustomObject]@{ principalName = "TestPrincipal" } + } + Mock -CommandName Get-CacheItem + Mock -CommandName Add-CacheItem + Mock -CommandName Set-CacheObject + Mock -CommandName Refresh-CacheIdentity + + } + + BeforeEach { + $Global:DSCAZDO_OrganizationName = 'TestOrg' + $Global:AzDoGroup = @() + } + + Context 'when ProjectScopeDescriptor is found' { + BeforeEach { + Mock -CommandName Get-CacheItem -MockWith { + return [PSCustomObject]@{ ProjectDescriptor = 'ProjectDescriptor123' } + } + } + + It 'should create a new DevOps group' { + $params = @{ + GroupName = 'TestGroup' + ProjectName = 'TestProject' + } + + $result = New-xAzDoProjectGroup @params + + Assert-MockCalled Get-CacheItem -Exactly 1 + Assert-MockCalled New-DevOpsGroup -Exactly 1 + Assert-MockCalled Add-CacheItem -Exactly 1 + Assert-MockCalled Set-CacheObject -Exactly 1 + Assert-MockCalled Refresh-CacheIdentity -Exactly 1 + } + + It 'should update caches correctly' { + $params = @{ + GroupName = 'TestGroup' + ProjectName = 'TestProject' + } + + $result = New-xAzDoProjectGroup @params + + Assert-MockCalled Add-CacheItem -Exactly 1 + Assert-MockCalled Set-CacheObject -Exactly 1 + Assert-MockCalled Refresh-CacheIdentity -Exactly 1 + } + } + + Context 'when ProjectScopeDescriptor is not found' { + BeforeEach { + Mock -CommandName Get-CacheItem -MockWith { + return $null + } + } + + It 'should write a warning and abort the group creation' { + $params = @{ + GroupName = 'TestGroup' + ProjectName = 'TestProject' + } + + $result = New-xAzDoProjectGroup @params + + Assert-MockCalled Get-CacheItem -Exactly 1 + Assert-MockCalled Write-Warning -Exactly 1 + Assert-MockCalled New-DevOpsGroup -Exactly 0 + Assert-MockCalled Add-CacheItem -Exactly 0 + Assert-MockCalled Set-CacheObject -Exactly 0 + Assert-MockCalled Refresh-CacheIdentity -Exactly 0 + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 new file mode 100644 index 000000000..5932bee84 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 @@ -0,0 +1,121 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe 'Remove-xAzDoProjectGroup' { + + AfterAll { + # Clean up + Remove-Variable -Name DSCAZDO_OrganizationName -ErrorAction SilentlyContinue + } + + BeforeAll { + + # Set the organization name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoProjectGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + $mockProjectName = "TestProject" + $mockGroupName = "TestGroup" + $mockDescription = "TestDescription" + + # Mocking external functions that are called within the function + Mock -CommandName Remove-DevOpsGroup -Verifiable + Mock -CommandName Remove-CacheItem -Verifiable + Mock -CommandName Set-CacheObject -Verifiable + + } + + Context 'When LookupResult has no cache items' { + It 'Should return without calling any other functions' { + $LookupResult = @{ + liveCache = $null + localCache = $null + } + + $result = Remove-xAzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult + + Assert-MockCalled -CommandName Remove-DevOpsGroup -Times 0 -Exactly + Assert-MockCalled -CommandName Remove-CacheItem -Times 0 -Exactly + Assert-MockCalled -CommandName Set-CacheObject -Times 0 -Exactly + } + } + + Context 'When LookupResult has liveCache but no localCache' { + + It 'Should call Remove-DevOpsGroup and remove cache items' { + $LookupResult = @{ + liveCache = @{ + Descriptor = 'liveDescriptor' + principalName = 'livePrincipal' + } + localCache = $null + } + + $result = Remove-xAzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult + + Assert-MockCalled -CommandName Remove-DevOpsGroup -Times 1 -Exactly + Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -ParameterFilter { $type -eq 'LiveGroups' } + Assert-MockCalled -CommandName Set-CacheObject -Times 1 -Exactly -ParameterFilter { $cacheType -eq 'LiveGroups' } + Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -Exactly -ParameterFilter { $type -eq 'Group' } + Assert-MockCalled -CommandName Set-CacheObject -Times 1 -Exactly -ParameterFilter { $cacheType -eq 'Group' } + } + } + + Context 'When LookupResult has both liveCache and localCache' { + It 'Should use liveCache descriptor and principal name' { + $LookupResult = @{ + liveCache = @{ + Descriptor = 'liveDescriptor' + principalName = 'livePrincipal' + } + localCache = @{ + Descriptor = 'localDescriptor' + principalName = 'localPrincipal' + } + } + + $result = Remove-xAzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult + + Assert-MockCalled -CommandName Remove-DevOpsGroup -Times 1 + Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -ParameterFilter { $type -eq 'LiveGroups' } + Assert-MockCalled -CommandName Set-CacheObject -Times 1 -ParameterFilter { $cacheType -eq 'LiveGroups' } + Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -ParameterFilter { $type -eq 'Group' } + Assert-MockCalled -CommandName Set-CacheObject -Times 1 -ParameterFilter { $cacheType -eq 'Group' } + } + } + + Context 'When only localCache exists' { + It 'Should use localCache descriptor and principal name' { + $LookupResult = @{ + liveCache = $null + localCache = @{ + Descriptor = 'localDescriptor' + principalName = 'localPrincipal' + } + } + + $result = Remove-xAzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult + + Assert-MockCalled -CommandName Remove-DevOpsGroup -Times 1 + Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -ParameterFilter { $type -eq 'LiveGroups' } + Assert-MockCalled -CommandName Set-CacheObject -Times 1 -ParameterFilter { $cacheType -eq 'LiveGroups' } + Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -ParameterFilter { $type -eq 'Group' } + Assert-MockCalled -CommandName Set-CacheObject -Times 1 -ParameterFilter { $cacheType -eq 'Group' } + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 new file mode 100644 index 000000000..36f1d2e75 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 @@ -0,0 +1,129 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Import the module containing Set-xAzDoProjectGroup if it's in a different file. +# . .\path\to\your\module.psm1 + +Describe 'Set-xAzDoProjectGroup' { + + AfterAll { + # Clean up + Remove-Variable -Name DSCAZDO_OrganizationName -ErrorAction SilentlyContinue + } + + BeforeAll { + + # Set the organization name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoProjectGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + + # Mocking external functions that are called within the function + Mock -CommandName Set-DevOpsGroup -MockWith { + return @{ principalName = 'newPrincipal'; descriptor = 'newDescriptor'; } + } + + Mock -CommandName Refresh-CacheIdentity + Mock -CommandName Remove-CacheItem + Mock -CommandName Add-CacheItem + Mock -CommandName Set-CacheObject + Mock -CommandName Write-Warning + + } + + Context 'When LookupResult status is Renamed' { + It 'Should write a warning and return without making any API calls' { + $LookupResult = @{ + Status = [DSCGetSummaryState]::Renamed + liveCache = @{ + descriptor = 'liveDescriptor' + } + } + + $result = Set-xAzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult + + Assert-MockCalled -CommandName Set-DevOpsGroup -Times 0 -Exactly + Assert-MockCalled -CommandName Refresh-CacheIdentity -Times 0 -Exactly + Assert-MockCalled -CommandName Remove-CacheItem -Times 0 -Exactly + Assert-MockCalled -CommandName Add-CacheItem -Times 0 -Exactly + Assert-MockCalled -CommandName Set-CacheObject -Times 0 -Exactly + } + } + + Context 'When updating the group' { + It 'Should call Set-DevOpsGroup with correct parameters and update caches' { + $LookupResult = @{ + Status = [DSCGetSummaryState]::Existing + liveCache = @{ + descriptor = 'liveDescriptor' + } + localCache = @{ + principalName = 'localPrincipal' + } + } + + $Global:DSCAZDO_OrganizationName = 'TestOrg' + + $result = Set-xAzDoProjectGroup -GroupName 'TestGroup' -GroupDescription 'TestDescription' -ProjectName 'TestProject' -LookupResult $LookupResult + + Assert-MockCalled -CommandName Set-DevOpsGroup -Times 1 -Exactly -ParameterFilter { + ($ApiUri -eq "https://vssps.dev.azure.com/TestOrg") -and + ($GroupName -eq 'TestGroup') -and + ($GroupDescription -eq 'TestDescription') -and + ($GroupDescriptor -eq 'liveDescriptor') + } + + Assert-MockCalled -CommandName Refresh-CacheIdentity -Times 1 -Exactly -ParameterFilter { + $key -eq 'newPrincipal' -and + $cacheType -eq 'LiveGroups' + } + + Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -Exactly -ParameterFilter { + $key -eq 'localPrincipal' -and + $type -eq 'Group' + } + + Assert-MockCalled -CommandName Add-CacheItem -Times 1 -Exactly -ParameterFilter { + $key -eq 'newPrincipal' -and + $type -eq 'Group' + } + + Assert-MockCalled -CommandName Set-CacheObject -Times 1 -Exactly -ParameterFilter { + $cacheType -eq 'Group' + } + } + } + + Context 'When LookupResult has no local cache' { + It 'Should not call Remove-CacheItem' { + $LookupResult = @{ + Status = [DSCGetSummaryState]::Existing + liveCache = @{ + descriptor = 'liveDescriptor' + } + localCache = $null + } + + $Global:DSCAZDO_OrganizationName = 'TestOrg' + + $result = Set-xAzDoProjectGroup -GroupName 'TestGroup' -GroupDescription 'TestDescription' -ProjectName 'TestProject' -LookupResult $LookupResult + + Assert-MockCalled -CommandName Remove-CacheItem -Times 0 -Exactly + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 new file mode 100644 index 000000000..071759b37 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 @@ -0,0 +1,124 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Not used +Describe 'Test-xAzDoProjectGroup' -skip { + + AfterAll { + # Clean up + Remove-Variable -Name DSCAZDO_OrganizationName -ErrorAction SilentlyContinue + } + + BeforeAll { + + # Set the organization name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-xAzDoProjectGroup.tests.ps1' + } + + # Load the functions to test + $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Load the summary state + . (Get-ClassFilePath 'DSCGetSummaryState') + . (Get-ClassFilePath '000.CacheItem') + . (Get-ClassFilePath 'Ensure') + + # Mocking external functions that are called within the function + Mock -CommandName 'Get-CacheItem' -MockWith { + param ($Key, $Type) + if ($Key -eq "groupKey" -and $Type -eq 'LiveGroups') { + return $true + } + return $false + } + Mock -CommandName 'Format-AzDoGroup' -MockWith { return "groupKey" } + } + + Context 'When parameters are valid' { + It 'Should return true when group is found in cache' { + $GroupName = 'TestGroup' + $GetResult = @{ + Status = [DSCGetSummaryState]::Unchanged + Current = @{ description = 'Group Description' } + } + + $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result | Should -BeTrue + } + + It 'Should return false when group name and description matches' { + $GroupName = 'TestGroup' + $GroupDescription = 'Group Description' + $GetResult = @{ + Status = [DSCGetSummaryState]::Unchanged + Current = @{ description = $GroupDescription } + } + + $result = Test-xAzDoProjectGroup -GroupName $GroupName -GroupDescription $GroupDescription -GetResult $GetResult + $result | Should -BeFalse + } + + It 'Should return true when status is Changed and group present in both live and cache' { + $GroupName = 'TestGroup' + $GetResult = @{ + Status = [DSCGetSummaryState]::Changed + Current = @{} + Cache = @{} + } + + $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result | Should -BeTrue + } + + It 'Should return true when status is Changed and group present in live but not cache' { + $GroupName = 'TestGroup' + $GetResult = @{ + Status = [DSCGetSummaryState]::Changed + Current = @{} + Cache = $null + } + + $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result | Should -BeTrue + } + + It 'Should return true when status is Changed and group not present in live but in cache' { + $GroupName = 'TestGroup' + $GetResult = @{ + Status = [DSCGetSummaryState]::Changed + Current = $null + Cache = @{} + } + + $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result | Should -BeTrue + } + + It 'Should return false when status is Renamed' { + $GroupName = 'TestGroup' + $GetResult = @{ + Status = [DSCGetSummaryState]::Renamed + } + + $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result | Should -BeFalse + } + + It 'Should return true when group present in cache' { + $GroupName = 'TestGroup' + $GetResult = @{ + Status = [DSCGetSummaryState]::Missing + } + + $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result | Should -BeTrue + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Get-AzDevOpsServicesApiUri.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Get-AzDevOpsServicesApiUri.Tests.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Get-AzDevOpsServicesApiUri.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Get-AzDevOpsServicesApiUri.Tests.disabled diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Get-AzDevOpsServicesUri.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Get-AzDevOpsServicesUri.Tests.disabled similarity index 100% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Get-AzDevOpsServicesUri.Tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Get-AzDevOpsServicesUri.Tests.disabled diff --git a/tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1 b/tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1 new file mode 100644 index 000000000..0183414da --- /dev/null +++ b/tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1 @@ -0,0 +1,109 @@ +Function Split-RecurivePath { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$Path, + [Parameter(Mandatory = $false)] + [int]$Times = 1 + ) + + 1 .. $Times | ForEach-Object { + $Path = Split-Path -Path $Path -Parent + } + + $Path +} + +Function Invoke-BeforeEachFunctions { + param( + [string[]]$FileNames + ) + + # Locate the scriptroot for the module + if ($Global:RepositoryRoot -eq $null) { + $Global:RepositoryRoot = Split-RecurivePath $PSScriptRoot -Times 4 + } + + $ScriptRoot = $Global:RepositoryRoot + + if ($null -eq $Global:TestPaths) { + $Global:TestPaths = Get-ChildItem -LiteralPath $ScriptRoot -Recurse -File -Include *.ps1 | Where-Object { + ($_.FullName -notlike "*Tests.ps1") -and + ($_.FullName -notlike '*\output\*') -and + ($_.FullName -notlike '*\tests\*') + } + } + + # Perform a lookup for all BeforeEach FileNames + $BeforeEachPath = @() + ForEach ($FileName in $FileNames) { + $BeforeEachPath += $Global:TestPaths | Where-Object { $_.Name -eq $FileName } + } + + return $BeforeEachPath + +} + +Function Find-Functions { + param( + [String]$TestFilePath + ) + + $files = @() + + # + # Using the File path of the test file, work out the function that is being tested + $FunctionName = (Get-Item -LiteralPath $TestFilePath).BaseName -replace '\.tests$', '' + $files += "$($FunctionName).ps1" + + + # + # Load the function into the AST and look for the mock commands. + + # Parse the PowerShell script file + $AST = [System.Management.Automation.Language.Parser]::ParseFile($TestFilePath, [ref]$null, [ref]$null) + + # Find all the Mock commands + $MockCommands = $AST.FindAll({ + $args[0] -is [System.Management.Automation.Language.CommandAst] -and + $args[0].CommandElements[0].Value -eq 'Mock' + }, $true) + + # Iterate over the Mock commands and find the CommandName parameter + foreach ($mockCommand in $MockCommands) { + + # Iterate over the CommandElements + foreach ($element in $mockCommand.CommandElements) { + + # Check if the element is a CommandParameterAst and the parameter name is CommandName + if ($element -is [System.Management.Automation.Language.CommandParameterAst] -and $element.ParameterName -eq 'CommandName') { + $null = $element.Parent.Extent.Text -match '(-CommandName\s+(?[^\s]+))|(^Mock (?[^\s]+$))' + $files += "$($matches.Function).ps1" + } + } + } + + # Ignore the following list of functions + $files = $files | Where-Object { $_ -notin @('Write-Error.ps1', 'Write-Output.ps1', 'Write-Verbose.ps1', 'Write-Warning.ps1') } + # Return the unique list of functions + $files = $files | Select-Object -Unique + + $files + +} + +Function Get-ClassFilePath { + param( + [string]$FileName + ) + + $Class = $Global:TestPaths | Where-Object { ($_.Name -eq $FileName) -or ($_.Name -eq "$FileName.ps1") } + return $Class.FullName + +} + +Function Import-Enums { + return ($Global:TestPaths | Where-Object { $_.Directory.Name -eq 'Enum' }) +} + +Export-ModuleMember -Function Split-RecurivePath, Invoke-BeforeEachFunctions, Find-Functions, Get-ClassFilePath, Import-Enums diff --git a/tests/Unit/Modules/_USEFUL.AzureDevOpsDsc.Common.Resources.Functions.Tests.Old.ps1 b/tests/Unit/Modules/_USEFUL.AzureDevOpsDsc.Common.Resources.Functions.Tests.Old.ps1 deleted file mode 100644 index 5da51c672..000000000 --- a/tests/Unit/Modules/_USEFUL.AzureDevOpsDsc.Common.Resources.Functions.Tests.Old.ps1 +++ /dev/null @@ -1,161 +0,0 @@ - -# Initialize tests -. $PSScriptRoot\AzureDevOpsDsc.Common.TestInitialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - Describe 'Resources\Functions' { - - $moduleName = 'AzureDevOpsDsc.Common' - $testCasesValidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Valid' - $testCasesValidResourceNamesForDscResources = $testCasesValidResourceNames | Where-Object { $_.ResourceName -notin @('Operation')} - - Context "When evaluating 'AzureDevOpsDsc.Common' module functions" { - BeforeEach { - $moduleName = 'AzureDevOpsDsc.Common' - } - - Context "When evaluating public, 'ExportedFunctions'" { - - BeforeEach { - [string[]]$exportedFunctionNames = Get-Command -Module $moduleName - - $resourcesFunctionsPublicDirectoryPath = "$PSScriptRoot\..\..\..\..\source\Modules\$moduleName\Resources\Functions\Public" - $resourcesFunctionsPublicTestsDirectoryPath = "$PSScriptRoot\Resources\Functions\Public" - } - - - $testCasesValidResourcePublicFunctionNames = Get-TestCase -ScopeName 'ResourcePublicFunctionName' -TestCaseName 'Valid' - $testCasesValidDscResourcePublicFunctionNames = Get-TestCase -ScopeName 'DscResourcePublicFunctionName' -TestCaseName 'Valid' - - $testCasesValidApiResourcePublicFunctionRequiredParameterNames = Get-TestCase -ScopeName 'ApiResourcePublicFunctionRequiredParameterName' -TestCaseName 'Valid' - - $testCasesValidDscResourcePublicFunctionRequiredParameterNames = Join-TestCaseArray -Expand -TestCaseArray @( - $testCasesValidDscResourcePublicFunctionNames, - $testCasesValidApiResourcePublicFunctionRequiredParameterNames - ) - - $testCasesValidApiResourcePublicFunctionMandatoryParameterNames = Get-TestCase -ScopeName 'ApiResourcePublicFunctionMandatoryParameterName' -TestCaseName 'Valid' - - $testCasesValidDscResourcePublicFunctionMandatoryParameterNames = Join-TestCaseArray -Expand -TestCaseArray @( - $testCasesValidDscResourcePublicFunctionNames, - $testCasesValidApiResourcePublicFunctionMandatoryParameterNames - ) - - $testCasesValidParameterAliasNames = Get-TestCase -ScopeName 'ParameterAliasName' -TestCaseName 'Valid' - - - - Context "When evaluating all public, functions" { - - - # Note: $testCasesExportedFunctionNames contains all exported functions in the module - - #It "Does not return a null value when 'Get-Command' is called - ''" -TestCases $testCasesExportedFunctionNames { - # param ([string]$ExportedFunctionName) - # - # Get-Command "$ExportedFunctionName" | Should -Not -BeNullOrEmpty - #} - - It "When evaluating function parameter, aliases required for DSC Resource functions" { - # TODO - } - - } - - - - Context "When evaluating all public, functions required for DSC Resources" { - - It "Should contain an exported, '' function (specific to the 'ResourceName') - ''" -TestCases $testCasesValidDscResourcePublicFunctionNames { - param ([string]$DscResourcePublicFunctionName) - - $DscResourcePublicFunctionName | Should -BeIn $exportedFunctionNames - } - - It "Should return a '' function/command (specific to the 'ResourceName') from 'Get-Command' - ''" -TestCases $testCasesValidDscResourcePublicFunctionNames { - param ([string]$DscResourcePublicFunctionName) - - Get-Command -Module $moduleName -Name $DscResourcePublicFunctionName | Should -Not -BeNullOrEmpty - } - - It "Should have a '' script ('.ps1') file (specific to the 'ResourceName') - ''" -TestCases $testCasesValidDscResourcePublicFunctionNames { - param ([string]$DscResourcePublicFunctionName) - - $functionScriptPath = Join-Path $resourcesFunctionsPublicDirectoryPath -ChildPath $($DscResourcePublicFunctionName + ".ps1") - Test-Path $functionScriptPath | Should -BeTrue - } - - It "Should have a '' test fixture/script ('.Tests.ps1') file (specific to the 'ResourceName') - ''" -TestCases $testCasesValidDscResourcePublicFunctionNames { - param ([string]$DscResourcePublicFunctionName) - - $functionTestsScriptPath = Join-Path $resourcesFunctionsPublicTestsDirectoryPath -ChildPath $($DscResourcePublicFunctionName + ".Tests.ps1") - Test-Path $functionTestsScriptPath | Should -BeTrue - } - - Context "When evaluating function parameters required for DSC Resource functions" { - - It "Should have a '' function with required, '' parameter - '', ''" -TestCases $testCasesValidDscResourcePublicFunctionRequiredParameterNames { - param ([string]$DscResourcePublicFunctionName, - [string]$ApiResourcePublicFunctionRequiredParameterName) - - $ApiResourcePublicFunctionRequiredParameterName | - Should -BeIn $((Get-CommandParameter -CommandName $DscResourcePublicFunctionName -ModuleName $moduleName).Name) - } - - Context "When evaluating function parameters required for DSC Resource functions that must be 'Mandatory'" { - - It "Should have a '' function with required (and 'Mandatory'), '' parameter - '', ''" -TestCases $testCasesValidDscResourcePublicFunctionMandatoryParameterNames { - param ([string]$DscResourcePublicFunctionName, - [string]$ApiResourcePublicFunctionMandatoryParameterName) - - $ApiResourcePublicFunctionMandatoryParameterName | - Should -BeIn $(((Get-CommandParameterSetParameter -CommandName $DscResourcePublicFunctionName -ModuleName $moduleName) | Where-Object { $_.IsMandatory -eq 1 }).Name) - } - } - } - } - - Context "When evaluating all public, functions required for non-DSC Resources" { - - It "Should contain an exported, '' function (specific to the 'ResourceName') - ''" -TestCases $testCasesValidResourcePublicFunctionNames { - param ([string]$ResourcePublicFunctionName) - - $ResourcePublicFunctionName | Should -BeIn $exportedFunctionNames - } - - It "Should return a '' function/command (specific to the 'ResourceName') from 'Get-Command' - ''" -TestCases $testCasesValidResourcePublicFunctionNames { - param ([string]$ResourcePublicFunctionName) - - Get-Command -Module $moduleName -Name $ResourcePublicFunctionName | Should -Not -BeNullOrEmpty - } - - It "Should have a '' script ('.ps1') file (specific to the 'ResourceName') - ''" -TestCases $testCasesValidResourcePublicFunctionNames { - param ([string]$ResourcePublicFunctionName) - - $functionScriptPath = Join-Path $resourcesFunctionsPublicDirectoryPath -ChildPath $($ResourcePublicFunctionName + ".ps1") - Test-Path $functionScriptPath | Should -BeTrue - } - - It "Should have a '' test fixture/script ('.Tests.ps1') file (specific to the 'ResourceName') - ''" -TestCases $testCasesValidResourcePublicFunctionNames { - param ([string]$ResourcePublicFunctionName) - - $functionTestsScriptPath = Join-Path $resourcesFunctionsPublicTestsDirectoryPath -ChildPath $($ResourcePublicFunctionName + ".Tests.ps1") - Test-Path $functionTestsScriptPath | Should -BeTrue - } - - } - - } - - Context "When evaluating private, module functions" { - - # TODO: - # Should be a 'Test-Id' function present - - } - } - - } -} From ee754e89aca74f951ecb1e44ee7a3ad680aeaac3 Mon Sep 17 00:00:00 2001 From: Michael Zanatta Date: Tue, 22 Oct 2024 09:46:48 +1000 Subject: [PATCH 4/7] Align Code-Base with DSC Coding Standards (#4) * Updated Classes to be in-line with DSC Coding Guidelines Removed 'x' from xAzDoGitPermission * Removed 'x' from xAzDoGitRepository. * Remove 'x' from xAzDoGroupMember * Removed 'x' from xAzDoGroupPermission * Removed 'x' from xAzDoOrganizationGroup * Removed 'x' from xAzDoProject * Removed 'x' from Resource * Removed 'x' from Resources * Adding Documentation * Performing Code Refactoring to Bring it into line with AZDO Coding Guidelines * Please Implicit Mandatory Attribute with 'explicit' * Adding Documentation Fixing Issues * Set Explicit Mandatory Performing Code Refactoring * Code Formatting * Code Refactoring * Update the Cache Initialization Scripts * Code Cleanup of Cache * Fixed String Formatting and Code Blocks in Private Functions * Updating Codebase to support DSC Community Code Guidelines * Code standard changes --------- Co-authored-by: Michael Zanatta --- .gitignore | 1 + CONTRIBUTING.md | 12 +- USAGE.md | 16 +- source/AzureDevOpsDsc.psd1 | 4 +- source/Classes/001.AuthenticationToken.ps1 | 112 ++++++++---- source/Classes/002.PersonalAccessToken.ps1 | 73 +++++++- source/Classes/003.ManagedIdentityToken.ps1 | 72 ++++++-- source/Classes/004.DscResourceBase.ps1 | 6 +- .../005.AzDevOpsApiDscResourceBase.ps1 | 4 - .../Classes/006.AzDevOpsDscResourceBase.ps1 | 25 ++- source/Classes/007.APIRateLimit.ps1 | 56 +++++- ...ission.ps1 => 009.AzDoGroupPermission.ps1} | 21 ++- ...roup.ps1 => 011.AzDoOrganizationGroup.ps1} | 24 +-- ...0.xAzDoProject.ps1 => 020.AzDoProject.ps1} | 19 ++- ...rvices.ps1 => 021.AzDoProjectServices.ps1} | 21 ++- ...jectGroup.ps1 => 022.AzDoProjectGroup.ps1} | 21 ++- ...roupMember.ps1 => 031.AzDoGroupMember.ps1} | 23 +-- ...pository.ps1 => 040.AzDoGitRepository.ps1} | 23 +-- ...rmission.ps1 => 041.AzDoGitPermission.ps1} | 21 ++- source/Enum/DSCGetSummaryState.ps1 | 19 +++ source/Enum/DescriptorType.ps1 | 70 ++++++++ source/Enum/TokenType.ps1 | 27 ++- .../Api/Classes/000.CacheItem.ps1 | 30 ++++ .../Private/Api/ACL/Get-DevOpsACL.ps1 | 7 +- .../Api/ACL/Get-DevOpsDescriptorIdentity.ps1 | 24 +-- .../AzDoPermission/Remove-AzDoPermission.ps1 | 87 ++++++++++ .../AzDoPermission/Remove-xAzDoPermission.ps1 | 50 ------ .../Api/AzDoPermission/Set-AzDoPermission.ps1 | 85 +++++++++ .../AzDoPermission/Set-xAzDoPermission.ps1 | 49 ------ .../Api/Cache/List-DevOpsGitRepository.ps1 | 31 +++- .../Api/Cache/List-DevOpsGroupMembers.ps1 | 35 +++- .../Private/Api/Cache/List-DevOpsGroups.ps1 | 39 ++++- .../Private/Api/Cache/List-DevOpsProcess.ps1 | 42 ++++- .../Private/Api/Cache/List-DevOpsProjects.ps1 | 31 +++- .../Cache/List-DevOpsSecurityNamespaces.ps1 | 26 ++- .../Cache/List-DevOpsServicePrinciples.ps1 | 34 +++- .../Private/Api/Cache/List-UserCache.ps1 | 37 +++- .../Api/GitRepository/New-GitRepository.ps1 | 55 ++++-- .../GitRepository/Remove-GitRepository.ps1 | 45 +++-- .../Private/Api/Group/New-DevOpsGroup.ps1 | 23 +-- .../Private/Api/Group/Remove-DevOpsGroup.ps1 | 14 +- .../Private/Api/Group/Set-DevOpsGroup.ps1 | 30 ++-- .../Api/GroupMember/New-DevOpsGroupMember.ps1 | 52 ++++-- .../GroupMember/Remove-DevOpsGroupMember.ps1 | 53 ++++-- .../Private/Api/Project/New-DevOpsProject.ps1 | 10 +- .../Api/Project/Remove-DevOpsProject.ps1 | 5 +- .../Api/Project/Update-DevOpsProject.ps1 | 5 +- .../Api/Project/Wait-DevOpsProject.ps1 | 15 +- .../Get-ProjectServiceStatus.ps1 | 32 ++++ .../Set-ProjectServiceStatus.ps1 | 28 +++ .../Get-DevOpsSecurityDescriptor.ps1 | 6 +- .../Add-AuthenticationHTTPHeader.ps1 | 37 ++-- .../Get-AzManagedIdentityToken.ps1 | 58 +++++-- .../Update-AzManagedIdentity.ps1 | 8 +- .../Set-AzPersonalAccessToken.ps1 | 70 ++++++-- .../Private/Authentication/Test-AzToken.ps1 | 18 +- .../Functions/Private/Cache/Add-CacheItem.ps1 | 59 ++++--- .../Cache Initalization/0.ProjectCache.ps1 | 20 ++- .../Cache Initalization/1.GroupCache.ps1 | 23 +-- .../Cache/Cache Initalization/2.UserCache.ps1 | 29 +++- .../3.GroupMemberCache.ps1 | 30 +++- .../4.GitRepositoryCache.ps1 | 23 +++ .../5.PermissionsCache.ps1 | 25 ++- .../6.ServicePrinciple.ps1 | 21 +++ .../7.IdentitySubjectDescriptors.ps1 | 38 ++++- .../8.ProcessTemplates.ps1 | 23 +++ .../Private/Cache/Export-CacheObject.ps1 | 26 +-- .../Private/Cache/Find-CacheItem.ps1 | 7 +- .../Functions/Private/Cache/Get-CacheItem.ps1 | 42 ++--- .../Private/Cache/Get-CacheObject.ps1 | 29 ++-- .../Private/Cache/Import-CacheObject.ps1 | 21 ++- .../Private/Cache/Initialize-CacheObject.ps1 | 45 +++-- .../Private/Cache/Refresh-AzDoCache.ps1 | 24 ++- .../Private/Cache/Refresh-CacheIdentity.ps1 | 35 +++- .../Private/Cache/Refresh-CacheObject.ps1 | 25 ++- .../Private/Cache/Remove-CacheItem.ps1 | 11 +- .../Private/Cache/Set-CacheObject.ps1 | 5 +- .../Private/Helper/ACL/ConvertTo-ACEList.ps1 | 16 +- .../Helper/ACL/ConvertTo-ACETokenList.ps1 | 14 +- .../Private/Helper/ACL/ConvertTo-ACL.ps1 | 6 +- .../Helper/ACL/ConvertTo-ACLHashtable.ps1 | 16 +- .../Helper/ACL/ConvertTo-FormattedACL.ps1 | 25 ++- .../Helper/ACL/ConvertTo-FormattedToken.ps1 | 5 +- .../Private/Helper/ACL/Format-ACEs.ps1 | 2 +- .../Helper/ACL/Get-BitwiseOrResult.ps1 | 15 +- .../Private/Helper/ACL/Group-ACEs.ps1 | 34 +++- .../Private/Helper/ACL/New-ACLToken.ps1 | 37 ++-- .../Private/Helper/ACL/Parse-ACLToken.ps1 | 20 ++- .../Private/Helper/ACL/Resolve-ACLToken.ps1 | 24 +++ .../Helper/ACL/Test-ACLListforChanges.ps1 | 21 ++- .../API/Test-AzDevOpsApiHttpRequestHeader.ps1 | 10 +- .../Helper/API/Test-AzDevOpsApiResourceId.ps1 | 2 +- .../API/Test-AzDevOpsApiTimeoutExceeded.ps1 | 1 + .../Helper/API/Test-AzDevOpsApiUri.ps1 | 5 +- .../Helper/API/Wait-AzDevOpsApiResource.ps1 | 18 +- .../Private/Helper/ConvertTo-Base64String.ps1 | 5 +- .../Private/Helper/Find-AzDoIdentity.ps1 | 66 +++++-- .../Private/Helper/Find-Identity.ps1 | 75 +++++--- .../Private/Helper/Format-AzDoGroup.ps1 | 8 +- .../Private/Helper/Format-AzDoGroupMember.ps1 | 20 ++- .../Private/Helper/Format-AzDoProjectName.ps1 | 55 +++++- .../Private/Helper/Format-DescriptorType.ps1 | 32 +++- .../Helper/Get-AzDevOpsApiResourceUri.ps1 | 6 - .../Private/Helper/Get-AzDoCacheObjects.ps1 | 16 ++ .../Helper/Invoke-AzDevOpsApiRestMethod.ps1 | 25 +-- .../Private/Helper/Logging/Initialize-Log.txt | 3 +- .../Logging/Proxy Functions/Write-Error.ps1 | 30 +++- .../Logging/Proxy Functions/Write-Verbose.ps1 | 28 ++- .../Logging/Proxy Functions/Write-Warning.ps1 | 29 +++- .../Private/Helper/Logging/Write-Log.txt | 4 +- .../Private/Helper/New-AzDevOpsACLToken.ps1 | 18 +- .../Helper/New-InvalidOperationException.ps1 | 42 ++++- .../Private/Helper/System/New-Thread.ps1 | 26 ++- .../AzureDevOpsDsc.Common.psd1 | 84 ++++----- .../AzureDevOpsDsc.Common.psm1 | 7 +- .../Private/Test-AzDevOpsPatCredential.ps1 | 4 +- .../000.LocalizedDataAzACLTokenPatten.ps1 | 53 +++--- ...001.LocalizedDataAzResourceTokenPatten.ps1 | 51 +++--- ...002.LocalizedDataAzSerializationPatten.ps1 | 50 ++++-- .../Private/Test-AzDevOpsOrganizationName.ps1 | 15 +- .../Functions/Private/Test-AzDevOpsPat.ps1 | 8 +- .../Test-AzDevOpsProjectDescription.ps1 | 14 +- .../Private/Test-AzDevOpsProjectName.ps1 | 4 +- .../Functions/Private/Test-ObjectProperty.ps1 | 16 +- .../Private/Wait-AzDevOpsOperation.ps1 | 2 +- .../Get-AzDoGitPermission.ps1} | 101 +++++++---- .../New-AzDoGitPermission.ps1 | 96 +++++++++++ .../Remove-AzDoGitPermission.ps1} | 43 ++--- .../Set-AzDoGitPermission.ps1} | 23 +-- .../Get-AzDoGitRepository.ps1 | 103 +++++++++++ .../New-AzDoGitRepository.ps1 | 97 +++++++++++ .../Remove-AzDoGitRepository.ps1 | 87 ++++++++++ .../Set-AzDoGitRepository.ps1 | 67 ++++++++ .../Get-AzDoGroupMember.ps1} | 87 +++++++--- .../AzDoGroupMember/New-AzDoGroupMember.ps1 | 143 ++++++++++++++++ .../Remove-AzDoGroupMember.ps1 | 119 +++++++++++++ .../AzDoGroupMember/Set-AzDoGroupMember.ps1 | 161 ++++++++++++++++++ .../AzDoGroupMember/Test-AzDoGroupMember.ps1 | 48 ++++++ .../Get-AzDoGroupPermission.ps1} | 93 +++++++--- .../New-AzDoGroupPermission.ps1 | 104 +++++++++++ .../Remove-AzDoGroupPermission.ps1 | 112 ++++++++++++ .../Set-AzDoGroupPermission.ps1 | 104 +++++++++++ .../Get-AzDoOrganizationGroup.ps1} | 145 ++++++++++------ .../New-AzDoOrganizationGroup.ps1 | 81 +++++++++ .../Remove-AzDoOrganizationGroup.ps1} | 46 ++++- .../Set-AzDoOrganizationGroup.ps1 | 104 +++++++++++ .../Test-AzDoOrganizationGroup.ps1} | 54 +++--- .../Public/AzDoProject/Get-AzDoProject.ps1 | 152 +++++++++++++++++ .../New-AzDoProject.ps1} | 42 ++++- .../Public/AzDoProject/Remove-AzDoProject.ps1 | 101 +++++++++++ .../Set-AzDoProject.ps1} | 40 ++++- .../Public/AzDoProject/Test-AzDoProject.ps1 | 80 +++++++++ .../Get-AzDoProjectGroup.ps1} | 94 +++++----- .../AzDoProjectGroup/New-AzDoProjectGroup.ps1 | 108 ++++++++++++ .../Remove-AzDoProjectGroup.ps1} | 47 ++++- .../Set-AzDoProjectGroup.ps1} | 61 +++++-- .../Test-AzDoProjectGroup.ps1} | 49 +++--- .../Get-AzDoProjectServices.ps1} | 72 +++++++- .../New-AzDoProjectServices.ps1 | 91 ++++++++++ .../Remove-AzDoProjectServices.ps1 | 96 +++++++++++ .../Set-AzDoProjectServices.ps1} | 53 +++++- .../Test-AzDoProjectServices.ps1 | 96 +++++++++++ .../Public/Get-AzDevOpsOperation.ps1 | 8 +- .../Public/New-AzDoAuthenticationProvider.ps1 | 51 +++--- .../New-xAzDoGitPermission.ps1 | 60 ------- .../Get-xAzDoGitRepository.ps1 | 68 -------- .../New-xAzDoGitRepository.ps1 | 57 ------- .../Remove-xAzDoGitRepository.ps1 | 54 ------ .../Set-xAzDoGitRepository.ps1 | 31 ---- .../xAzDoGroupMember/New-xAzDoGroupMember.ps1 | 103 ----------- .../Remove-xAzDoGroupMember.ps1 | 91 ---------- .../xAzDoGroupMember/Set-xAzDoGroupMember.ps1 | 124 -------------- .../Test-xAzDoGroupMember.ps1 | 28 --- .../New-xAzDoGroupPermission.ps1 | 70 -------- .../Remove-xAzDoGroupPermission.ps1 | 73 -------- .../Set-xAzDoGroupPermission.ps1 | 70 -------- .../New-xAzDoOrganizationGroup.ps1 | 53 ------ .../Set-xAzDoOrganizationGroup.ps1 | 69 -------- .../Public/xAzDoProject/Get-xAzDoProject.ps1 | 118 ------------- .../xAzDoProject/Remove-xAzDoProject.ps1 | 62 ------- .../Public/xAzDoProject/Test-xAzDoProject.ps1 | 44 ----- .../New-xAzDoProjectGroup.ps1 | 64 ------- .../New-xAzDoProjectServices.ps1 | 48 ------ .../Remove-xAzDoProjectServices.ps1 | 48 ------ .../Test-xAzDoProjectServices.ps1 | 48 ------ .../Public/Initalize-AzDevOpsCache.ps1 | 14 +- .../Functions/Public/Refresh-Cache.ps1 | 1 + source/WikiSource/Home.md | 16 +- ...oGitPermission.md => AzDoGitPermission.md} | 24 +-- ...oGitRepository.md => AzDoGitRepository.md} | 16 +- ...xAzDoGroupMember.md => AzDoGroupMember.md} | 18 +- ...upPermission.md => AzDoGroupPermission.md} | 27 +-- ...ationGroup.md => AzDoOrganizationGroup.md} | 18 +- .../{xAzDoProject.md => AzDoProject.md} | 18 +- ...zDoProjectGroup.md => AzDoProjectGroup.md} | 18 +- ...jectServices.md => AzDoProjectServices.md} | 16 +- tests/Integration/Invoke-Tests.ps1 | 2 +- .../Resources/xAzDoGitPermission.tests.ps1 | 4 +- .../Resources/xAzDoGitRepository.tests.ps1 | 4 +- .../Resources/xAzDoGroupMember.tests.ps1 | 6 +- .../Resources/xAzDoGroupPermission.tests.ps1 | 4 +- ...zDoOrganizationGroup.Description.tests.ps1 | 4 +- ...oOrganizationGroup.NoDescription.tests.ps1 | 4 +- .../xAzDoProject.Description.tests.ps1 | 4 +- .../xAzDoProject.NoDescription.tests.ps1 | 4 +- .../xAzDoProjectGroup.Description.tests.ps1 | 4 +- .../Resources/xAzDoProjectServices.tests.ps1 | 4 +- .../Integration/Supporting/API/Add-Header.ps1 | 5 +- .../Supporting/API/ConvertTo-Base64String.ps1 | 2 +- .../Supporting/API/Get-MIToken.ps1 | 7 +- .../Supporting/API/Invoke-APIRestMethod.ps1 | 8 +- .../Supporting/API/New-AuthProvider.ps1 | 18 +- .../Supporting/APICalls/List-DevOpsGroups.ps1 | 5 +- .../APICalls/List-DevOpsProjects.ps1 | 2 +- .../APICalls/Remove-DevOpsGroup.ps1 | 4 +- .../APICalls/Remove-DevOpsProject.ps1 | 2 +- .../Functions/SupportingFunctions.ps1 | 6 +- .../Supporting/Initalize-TestFramework.ps1 | 10 +- tests/Unit/Classes/Classes.BeforeAll.ps1 | 2 +- .../009.xAzDoGroupPermission.tests.ps1 | 20 +-- .../011.xAzDoOrganizationGroup.tests.ps1 | 10 +- .../Resources/022.xAzDoProjectGroup.tests.ps1 | 12 +- .../Resources/031.xAzDoGroupMember.tests.ps1 | 12 +- .../040.xAzDoGitRepository.tests.ps1 | 12 +- .../041.xAzDoGitPermission.tests.ps1 | 12 +- .../Remove-xAzDoPermission.tests.ps1 | 8 +- .../Set-xAzDoPermission.tests.ps1 | 14 +- .../Private/Helper/Format-AzDoGroup.tests.ps1 | 6 +- .../Helper/System/New-Thread.tests.ps1 | 2 +- .../Get-xAzDoGitPermission.tests.ps1 | 38 ++--- .../New-xAzDoGitPermission.tests.ps1 | 30 ++-- .../Remove-xAzDoGitPermission.tests.ps1 | 30 ++-- .../Set-xAzDoGitPermission.tests.ps1 | 22 +-- .../Get-xAzDoGitRepository.tests.ps1 | 10 +- .../New-xAzDoGitRepository.tests.ps1 | 20 +-- .../Remove-xAzDoGitRepository.tests.ps1 | 16 +- .../Set-xAzDoGitRepository.tests.ps1 | 4 +- .../Get-xAzDoGroupMember.tests.ps1 | 18 +- .../New-xAzDoGroupMember.tests.ps1 | 14 +- .../Remove-xAzDoGroupMember.tests.ps1 | 14 +- .../Set-xAzDoGroupMember.tests.ps1 | 20 +-- .../Test-xAzDoGroupMember.tests.ps1 | 28 +-- .../Get-xAzDoGroupPermission.tests.ps1 | 10 +- .../New-xAzDoGroupPermission.tests.ps1 | 22 +-- .../Remove-xAzDoGroupPermission.tests.ps1 | 22 +-- .../Set-xAzDoGroupPermission.tests.ps1 | 20 +-- .../Get-xAzDoOrganizationGroup.tests.ps1 | 18 +- .../New-xAzDoOrganizationGroup.tests.ps1 | 10 +- .../Remove-xAzDoOrganizationGroup.tests.ps1 | 14 +- .../Set-xAzDoOrganizationGroup.tests.ps1 | 14 +- .../Test-xAzDoOrganizationGroup.tests.ps1 | 26 +-- .../xAzDoProject/Get-xAzDoProject.tests.ps1 | 18 +- .../xAzDoProject/New-xAzDoProject.tests.ps1 | 12 +- .../Remove-xAzDoProject.tests.ps1 | 8 +- .../xAzDoProject/Set-xAzDoProject.tests.ps1 | 6 +- .../xAzDoProject/Test-xAzDoProject.tests.ps1 | 2 +- .../Get-xAzDoProjectGroup.tests.ps1 | 30 ++-- .../New-xAzDoProjectGroup.tests.ps1 | 12 +- .../Remove-xAzDoProjectGroup.tests.ps1 | 12 +- .../Set-xAzDoProjectGroup.tests.ps1 | 12 +- .../Test-xAzDoProjectGroup.tests.ps1 | 18 +- .../Modules/TestHelpers/CommonTestHelper.psm1 | 4 +- 262 files changed, 5987 insertions(+), 3087 deletions(-) rename source/Classes/{009.xAzDoGroupPermission.ps1 => 009.AzDoGroupPermission.ps1} (78%) rename source/Classes/{011.xAzDoOrganizationGroup.ps1 => 011.AzDoOrganizationGroup.ps1} (75%) rename source/Classes/{020.xAzDoProject.ps1 => 020.AzDoProject.ps1} (83%) rename source/Classes/{021.xAzDoProjectServices.ps1 => 021.AzDoProjectServices.ps1} (81%) rename source/Classes/{022.xAzDoProjectGroup.ps1 => 022.AzDoProjectGroup.ps1} (81%) rename source/Classes/{031.xAzDoGroupMember.ps1 => 031.AzDoGroupMember.ps1} (73%) rename source/Classes/{040.xAzDoGitRepository.ps1 => 040.AzDoGitRepository.ps1} (74%) rename source/Classes/{041.xAzDoGitPermission.ps1 => 041.AzDoGitPermission.ps1} (75%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-AzDoPermission.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-AzDoPermission.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitPermission/Get-xAzDoGitPermission.ps1 => AzDoGitPermission/Get-AzDoGitPermission.ps1} (50%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/New-AzDoGitPermission.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitPermission/Remove-xAzDoGitPermission.ps1 => AzDoGitPermission/Remove-AzDoGitPermission.ps1} (61%) rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitPermission/Set-xAzDoGitPermission.ps1 => AzDoGitPermission/Set-AzDoGitPermission.ps1} (73%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Get-AzDoGitRepository.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/New-AzDoGitRepository.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Remove-AzDoGitRepository.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Set-AzDoGitRepository.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupMember/Get-xAzDoGroupMember.ps1 => AzDoGroupMember/Get-AzDoGroupMember.ps1} (62%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Remove-AzDoGroupMember.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Set-AzDoGroupMember.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Test-AzDoGroupMember.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupPermission/Get-xAzDoGroupPermission.ps1 => AzDoGroupPermission/Get-AzDoGroupPermission.ps1} (53%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/New-AzDoGroupPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Remove-AzDoGroupPermission.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Set-AzDoGroupPermission.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.ps1 => AzDoOrganizationGroup/Get-AzDoOrganizationGroup.ps1} (61%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/New-AzDoOrganizationGroup.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.ps1 => AzDoOrganizationGroup/Remove-AzDoOrganizationGroup.ps1} (50%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Set-AzDoOrganizationGroup.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.ps1 => AzDoOrganizationGroup/Test-AzDoOrganizationGroup.ps1} (78%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProject/New-xAzDoProject.ps1 => AzDoProject/New-AzDoProject.ps1} (52%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Remove-AzDoProject.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProject/Set-xAzDoProject.ps1 => AzDoProject/Set-AzDoProject.ps1} (51%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectGroup/Get-xAzDoProjectGroup.ps1 => AzDoProjectGroup/Get-AzDoProjectGroup.ps1} (76%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/New-AzDoProjectGroup.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectGroup/Remove-xAzDoProjectGroup.ps1 => AzDoProjectGroup/Remove-AzDoProjectGroup.ps1} (54%) rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectGroup/Set-xAzDoProjectGroup.ps1 => AzDoProjectGroup/Set-AzDoProjectGroup.ps1} (50%) rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectGroup/Test-xAzDoProjectGroup.ps1 => AzDoProjectGroup/Test-AzDoProjectGroup.ps1} (79%) rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectServices/Get-xAzDoProjectServices.ps1 => AzDoProjectServices/Get-AzDoProjectServices.ps1} (62%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/New-AzDoProjectServices.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Remove-AzDoProjectServices.ps1 rename source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectServices/Set-xAzDoProjectServices.ps1 => AzDoProjectServices/Set-AzDoProjectServices.ps1} (51%) create mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Test-AzDoProjectServices.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/New-xAzDoProjectServices.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Remove-xAzDoProjectServices.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Test-xAzDoProjectServices.ps1 rename source/WikiSource/Resources/{xAzDoGitPermission.md => AzDoGitPermission.md} (89%) rename source/WikiSource/Resources/{xAzDoGitRepository.md => AzDoGitRepository.md} (84%) rename source/WikiSource/Resources/{xAzDoGroupMember.md => AzDoGroupMember.md} (85%) rename source/WikiSource/Resources/{xAzDoGroupPermission.md => AzDoGroupPermission.md} (79%) rename source/WikiSource/Resources/{xAzDoOrganizationGroup.md => AzDoOrganizationGroup.md} (80%) rename source/WikiSource/Resources/{xAzDoProject.md => AzDoProject.md} (86%) rename source/WikiSource/Resources/{xAzDoProjectGroup.md => AzDoProjectGroup.md} (83%) rename source/WikiSource/Resources/{xAzDoProjectServices.md => AzDoProjectServices.md} (87%) diff --git a/.gitignore b/.gitignore index 27c9e187a..8e00f8522 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ CodeCoverage.JaCoCo.xml Temp/* TestProject.yml TestProject2.yml +settings.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d0765878..2697e8361 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -240,7 +240,7 @@ class ClassName : AzDevOpsDscResourceBase [ClassName] Get() { - return [xAzDoProject]$($this.GetDscCurrentStateProperties()) + return [AzDoProject]$($this.GetDscCurrentStateProperties()) } hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() @@ -281,7 +281,7 @@ This format helps users understand each property and method of your resource, pr ### Naming the Resource -The resource name must start with the prefix xAzDo, such as xAzDoProject. +The resource name must start with the prefix xAzDo, such as AzDoProject. ### Guidelines for Documentation @@ -409,7 +409,7 @@ Here's an example: Function Get-ClassName { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$ResourceParameter, @@ -430,13 +430,13 @@ The `Get` function performs the lookup of the resource properties and calculates The changed contents are stored within the `LookupResult.PropertiesChanged` property. ```PowerShell -Function Get-xAzDoProjectServices { +Function Get-AzDoProjectServices { [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$ProjectName, @@ -634,7 +634,7 @@ Write-Verbose "[New-GitRepository] Creating new repository '$($RepositoryName)' # Define parameters for creating a new DevOps group $params = @{ - ApiUri = "{0}/{1}/_apis/git/repositories?api-version={2}" -f $ApiUri, $Project.name, $ApiVersion + ApiUri = '{0}/{1}/_apis/git/repositories?api-version={2}' -f $ApiUri, $Project.name, $ApiVersion Method = 'POST' ContentType = 'application/json' Body = @{ diff --git a/USAGE.md b/USAGE.md index e02ceaaa8..4774a83de 100644 --- a/USAGE.md +++ b/USAGE.md @@ -73,14 +73,14 @@ Here is an example of how to invoke a resource using the module: 1. Invoke the DSC Resource: ```powershell - Invoke-DscResource -Name 'xAzDoProjectGroup' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' + Invoke-DscResource -Name 'AzDoProjectGroup' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' ``` By following these steps, you can successfully set up and use the module with Azure DevOps. -## Implementation using `xAzDoDSCDatum` +## Implementation using `AzDO-DSC-LCM` -[Current Source](https://github.com/ZanattaMichael/xAzDoDSCDatum) +[Current Source](https://github.com/ZanattaMichael/AzDO-DSC-LCM) This module includes a custom Local Configuration Manager (LCM) built on Datum. By utilizing YAML resource files, similar to Ansible playbooks, administrators can manage their environment using Configuration as Code (CaC). @@ -96,7 +96,7 @@ variables: { resources: - name: Project - type: AzureDevOpsDsc/xAzDoProject + type: AzureDevOpsDsc/AzDoProject properties: projectName: $ProjectName projectDescription: $ProjectDescription @@ -115,9 +115,9 @@ variables: resources: - name: Project Services - type: AzureDevOpsDsc/xAzDoProjectServices + type: AzureDevOpsDsc/AzDoProjectServices dependsOn: - - AzureDevOpsDsc/xAzDoProject/Project + - AzureDevOpsDsc/AzDoProject/Project properties: projectName: $ProjectName BuildPipelines: disabled @@ -128,7 +128,7 @@ resources: - **Parameters**: This section is reserved for any input parameters that the configuration might require. - **Variables**: Here, you can define reusable variables such as `ProjectName` and `ProjectDescription`. -- **Resources**: This section defines the actual resources to be managed. In this example, we have a resource named "Project Services" of type `AzureDevOpsDsc/xAzDoProjectServices`. +- **Resources**: This section defines the actual resources to be managed. In this example, we have a resource named "Project Services" of type `AzureDevOpsDsc/AzDoProjectServices`. #### Resource Properties @@ -136,4 +136,4 @@ resources: - `BuildPipelines`: Set to `disabled`. - `AzureArtifact`: Set to `disabled`. -The `dependsOn` attribute ensures that the "Project Services" resource will only be configured after the `AzureDevOpsDsc/xAzDoProject/Project` resource has been set up. +The `dependsOn` attribute ensures that the "Project Services" resource will only be configured after the `AzureDevOpsDsc/AzDoProject/Project` resource has been set up. diff --git a/source/AzureDevOpsDsc.psd1 b/source/AzureDevOpsDsc.psd1 index 28c0d54d8..bbd9f6392 100644 --- a/source/AzureDevOpsDsc.psd1 +++ b/source/AzureDevOpsDsc.psd1 @@ -42,8 +42,8 @@ DscResourcesToExport = @( 'xAzDevOpsProject', - 'xAzDoOrganizationGroup', - 'xAzDoProjectGroup' + 'AzDoOrganizationGroup', + 'AzDoProjectGroup' ) RequiredAssemblies = @() diff --git a/source/Classes/001.AuthenticationToken.ps1 b/source/Classes/001.AuthenticationToken.ps1 index 8a0b23d05..80729f76e 100644 --- a/source/Classes/001.AuthenticationToken.ps1 +++ b/source/Classes/001.AuthenticationToken.ps1 @@ -1,66 +1,118 @@ +<# + .SYNOPSIS + Represents an authentication token with methods to manage and retrieve the token securely. + .DESCRIPTION + The AuthenticationToken class encapsulates an authentication token, providing methods to convert a SecureString to a String, + test the call stack for specific functions, and ensure that the Get() method is called only by authorized functions. -Class AuthenticationToken { + .PROPERTIES + [TokenType]$tokenType + The type of the token. - [TokenType]$tokenType - hidden [bool]$linux = $isLinux - hidden [SecureString]$access_token + hidden [bool]$linux + Indicates if the environment is Linux. + + hidden [SecureString]$access_token + The secure access token. + + .METHODS + hidden [String] ConvertFromSecureString([SecureString]$SecureString) + Converts a SecureString to a plain String. + + hidden [Bool] TestCallStack([String]$name) + Tests the call stack to check if a specific function is in the call stack. + + TestCaller() + Ensures that the Get() method is called only by authorized functions and not within certain contexts. + + [String] Get() + Retrieves the access token after ensuring that the calling function is authorized to do so. + + .NOTES + The class is designed to prevent unauthorized access to the access token and to ensure that the token is handled securely. +#> + +class AuthenticationToken +{ + [TokenType] $tokenType + hidden [bool] $linux = $isLinux + hidden [SecureString] $access_token # Function to convert a SecureString to a String - hidden [String]ConvertFromSecureString([SecureString]$SecureString) { + hidden [String] ConvertFromSecureString([SecureString] $SecureString) + { # Convert a SecureString to a String $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString) - $String = ($this.linux) ? [System.Runtime.InteropServices.Marshal]::PtrToStringUni($BSTR) : - [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) - [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) - return $String + $plainTextString = $( + if ($this.linux) + { + [System.Runtime.InteropServices.Marshal]::PtrToStringUni($BSTR) + } else { + [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) + } + [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) + ) + return $plainTextString + } # Function to test the call stack - hidden [Bool]TestCallStack([String]$name) { - + hidden [Bool] TestCallStack([String] $name) + { # Get the call stack Write-Verbose "[AuthenticationToken] Getting the call stack." - $CallStack = Get-PSCallStack # Check if any of the callers in the call stack is Invoke-DSCResource - foreach ($stackFrame in $callStack) { - if ($stackFrame.Command -eq $name) { + foreach ($stackFrame in $CallStack) + { + if ($stackFrame.Command -eq $name) + { Write-Verbose "[AuthenticationToken] The calling function is $name." return $true } } - return $false - } # Function to prevent unauthorized access to the Get() method - TestCaller() { - # + TestCaller() + { # Prevent Execution and Writing to Files and Pipeline Variables. - # Token can only be called within Test-AzAuthenticationToken. Test to see if the calling function is Test-AzAuthenticationToken + <# + The Get() method can only be called within the following functions: + - Add-AuthenticationHTTPHeader + - Invoke-AzDevOpsApiRestMethod + - New-AzDoAuthenticationProvider + #> if ( - (-not($this.TestCallStack('Add-AuthenticationHTTPHeader'))) -and - (-not($this.TestCallStack('Invoke-AzDevOpsApiRestMethod'))) -and - (-not($this.TestCallStack('New-AzDoAuthenticationProvider'))) - ) { + (-not($this.TestCallStack('Add-AuthenticationHTTPHeader'))) -and + (-not($this.TestCallStack('Invoke-AzDevOpsApiRestMethod'))) -and + (-not($this.TestCallStack('New-AzDoAuthenticationProvider'))) + ) + { # Token can only be called within Invoke-AzDevOpsApiRestMethod. Test to see if the calling function is Invoke-AzDevOpsApiRestMethod throw "[AuthenticationToken][Access Denied] The Get() method can only be called within AzureDevOpsDsc.Common." } # Token cannot be returned within a Write-* function. Test to see if the calling function is Write-* - if ($this.TestCallStack('Write-')) { throw "[AuthenticationToken][Access Denied] The Get() method cannot be called within a Write-* function." } - # Token cannot be written to a file. Test to see if the calling function is Out-File - if ($this.TestCallStack('Out-File')) { throw "[AuthenticationToken][Access Denied] The Get() method cannot be called within Out-File." } + if ($this.TestCallStack('Write-')) + { + throw "[AuthenticationToken][Access Denied] The Get() method cannot be called within a Write-* function." + } + # Token cannot be written to a file. Test to see if the calling function is Out-File + if ($this.TestCallStack('Out-File')) + { + throw "[AuthenticationToken][Access Denied] The Get() method cannot be called within Out-File." + } } # Return the access token - [String] Get() { - + [String] Get() + { # Verbose output Write-Verbose "[AuthenticationToken] Getting the access token:" Write-Verbose "[AuthenticationToken] Ensuring that the calling function is allowed to call the Get() method." @@ -68,11 +120,9 @@ Class AuthenticationToken { # Test the caller $this.TestCaller() - Write-Verbose "[AuthenticationToken] Token Retrival Successful." + Write-Verbose "[AuthenticationToken] Token Retrieval Successful." # Return the access token return ($this.ConvertFromSecureString($this.access_token)) - } - } diff --git a/source/Classes/002.PersonalAccessToken.ps1 b/source/Classes/002.PersonalAccessToken.ps1 index df42f72f9..da231279e 100644 --- a/source/Classes/002.PersonalAccessToken.ps1 +++ b/source/Classes/002.PersonalAccessToken.ps1 @@ -1,15 +1,38 @@ -Class PersonalAccessToken : AuthenticationToken { +<# +.SYNOPSIS +Represents a Personal Access Token (PAT) used for authentication. +.DESCRIPTION +The `PersonalAccessToken` class inherits from the `AuthenticationToken` class and provides methods to handle Personal Access Tokens. +It includes constructors for initializing the token using a plain text string or a secure string, and a method to check if the token is expired. +.CONSTRUCTORS +PersonalAccessToken([String]$PersonalAccessToken) + Initializes a new instance of the `PersonalAccessToken` class using a plain text string. - PersonalAccessToken([String]$PersonalAccessToken) { +PersonalAccessToken([SecureString]$SecureStringPersonalAccessToken) + Initializes a new instance of the `PersonalAccessToken` class using a secure string. + +.METHODS +[Bool]isExpired() + Checks if the Personal Access Token is expired. Always returns $false as Personal Access Tokens do not expire. + +.NOTES +The `PersonalAccessToken` class sets the token type to `PersonalAccessToken` and converts the plain text token to a secure string if necessary. +#> +class PersonalAccessToken : AuthenticationToken +{ + + PersonalAccessToken([String]$PersonalAccessToken) + { $this.tokenType = [TokenType]::PersonalAccessToken $this.access_token = ConvertTo-Base64String -InputObject ":$($PersonalAccessToken)" | ConvertTo-SecureString -AsPlainText -Force } - PersonalAccessToken([SecureString]$SecureStringPersonalAccessToken) { + PersonalAccessToken([SecureString]$SecureStringPersonalAccessToken) + { $this.tokenType = [TokenType]::PersonalAccessToken $this.access_token = $SecureStringPersonalAccessToken } @@ -19,22 +42,54 @@ Class PersonalAccessToken : AuthenticationToken { return $false } - } -# Function to create a new PersonalAccessToken object -Function global:New-PersonalAccessToken ([String]$PersonalAccessToken, [SecureString]$SecureStringPersonalAccessToken) { +<# +Creates a new PersonalAccessToken object. + +.DESCRIPTION +This function creates a new PersonalAccessToken object using either a plain text personal access token or a secure string personal access token. + +.PARAMETER PersonalAccessToken +A plain text personal access token. + +.PARAMETER SecureStringPersonalAccessToken +A secure string personal access token. + +.RETURNS +Returns a new instance of the PersonalAccessToken object. + +.EXAMPLE +$token = New-PersonalAccessToken -PersonalAccessToken "your-token-here" +Creates a new PersonalAccessToken object using a plain text token. + +.EXAMPLE +$secureToken = ConvertTo-SecureString "your-token-here" -AsPlainText -Force +$token = New-PersonalAccessToken -SecureStringPersonalAccessToken $secureToken +Creates a new PersonalAccessToken object using a secure string token. + +.NOTES +If neither a plain text personal access token nor a secure string personal access token is provided, an error is thrown. +#> + +Function global:New-PersonalAccessToken ([String]$PersonalAccessToken, [SecureString]$SecureStringPersonalAccessToken) +{ # Verbose output Write-Verbose "[PersonalAccessToken] Creating a new ManagedIdentityToken object." - if ($PersonalAccessToken) { + if ($PersonalAccessToken) + { # Create a new PersonalAccessToken object return [PersonalAccessToken]::New($PersonalAccessToken) - } elseif ($SecureStringPersonalAccessToken) { + } + elseif ($SecureStringPersonalAccessToken) + { # Create a new PersonalAccessToken object return [PersonalAccessToken]::New($SecureStringPersonalAccessToken) - } else { + } + else + { throw "Error. A Personal Access Token or SecureString Personal Access Token must be provided." } diff --git a/source/Classes/003.ManagedIdentityToken.ps1 b/source/Classes/003.ManagedIdentityToken.ps1 index 914372771..102e2e261 100644 --- a/source/Classes/003.ManagedIdentityToken.ps1 +++ b/source/Classes/003.ManagedIdentityToken.ps1 @@ -1,6 +1,41 @@ +<# +.SYNOPSIS + Represents a managed identity token used for authentication. + +.DESCRIPTION + The ManagedIdentityToken class inherits from the AuthenticationToken class and provides functionality to handle managed identity tokens. + It includes methods to validate the token object, check if the token is expired, and retrieve the access token. + +.PARAMETER ManagedIdentityTokenObj + A PSCustomObject containing the managed identity token details. It must include the following properties: + - access_token + - expires_on + - expires_in + - resource + - token_type + +.NOTES + The class includes a constructor to initialize the token properties, a method to validate the token object, a method to check if the token is expired, and a method to retrieve the access token. + +.EXAMPLE + $tokenObj = [PSCustomObject]@{ + access_token = "your_access_token" + expires_on = 1625097600 + expires_in = 3600 + resource = "https://management.azure.com/" + token_type = "Bearer" + } + + $managedIdentityToken = New-ManagedIdentityToken -ManagedIdentityTokenObj $tokenObj + if (-not $managedIdentityToken.isExpired()) { + $accessToken = $managedIdentityToken.Get() + Write-Output "Access Token: $accessToken" + } +#> -Class ManagedIdentityToken : AuthenticationToken { +class ManagedIdentityToken : AuthenticationToken +{ [DateTime]$expires_on [Int]$expires_in @@ -8,12 +43,15 @@ Class ManagedIdentityToken : AuthenticationToken { [String]$token_type # Constructor - ManagedIdentityToken([PSCustomObject]$ManagedIdentityTokenObj) { - + ManagedIdentityToken([PSCustomObject]$ManagedIdentityTokenObj) + { $this.tokenType = [TokenType]::ManagedIdentity # Validate that ManagedIdentityTokenObj is a HashTable and Contains the correct keys - if (-not $this.isValid($ManagedIdentityTokenObj)) { throw "[ManagedIdentityToken] The ManagedIdentityTokenObj is not valid." } + if (-not $this.isValid($ManagedIdentityTokenObj)) + { + throw "[ManagedIdentityToken] The ManagedIdentityTokenObj is not valid." + } $epochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) @@ -27,17 +65,18 @@ Class ManagedIdentityToken : AuthenticationToken { } # Function to validate the ManagedIdentityTokenObj - Hidden [Bool]isValid($ManagedIdentityTokenObj) { - - # Write-Verbose + Hidden [Bool]isValid($ManagedIdentityTokenObj) + { Write-Verbose "[ManagedIdentityToken] Validating the ManagedIdentityTokenObj." # Assuming these are the keys we expect in the hashtable $expectedKeys = @('access_token', 'expires_on', 'expires_in', 'resource', 'token_type') # Check if all expected keys exist in the hashtable - foreach ($key in $expectedKeys) { - if (-not $ManagedIdentityTokenObj."$key") { + foreach ($key in $expectedKeys) + { + if (-not $ManagedIdentityTokenObj."$key") + { Write-Verbose "[ManagedIdentityToken] The hashtable does not contain the expected property: $key" return $false } @@ -48,15 +87,19 @@ Class ManagedIdentityToken : AuthenticationToken { return $true } - [Bool]isExpired() { + [Bool]isExpired() + { # Remove 10 seconds from the expires_on time to account for clock skew. - if ($this.expires_on.AddSeconds(-10) -lt (Get-Date)) { return $true } + if ($this.expires_on.AddSeconds(-10) -lt (Get-Date)) + { + return $true + } return $false } # Return the access token - [String] Get() { - + [String] Get() + { # Verbose output Write-Verbose "[ManagedIdentityToken] Getting the access token:" Write-Verbose "[ManagedIdentityToken] Ensuring that the calling function is allowed to call the Get() method." @@ -74,7 +117,8 @@ Class ManagedIdentityToken : AuthenticationToken { } # Function to create a new ManagedIdentityToken object -Function global:New-ManagedIdentityToken ([PSCustomObject]$ManagedIdentityTokenObj) { +Function global:New-ManagedIdentityToken ([PSCustomObject]$ManagedIdentityTokenObj) +{ # Verbose output Write-Verbose "[ManagedIdentityToken] Creating a new ManagedIdentityToken object." diff --git a/source/Classes/004.DscResourceBase.ps1 b/source/Classes/004.DscResourceBase.ps1 index 9f9e9e33b..0da01e030 100644 --- a/source/Classes/004.DscResourceBase.ps1 +++ b/source/Classes/004.DscResourceBase.ps1 @@ -29,8 +29,7 @@ class DscResourceBase [System.Reflection.PropertyInfo]$propertyInfo = $_ $PropertyName = $_.Name - $propertyInfo.GetCustomAttributes($true) | - ForEach-Object { + $propertyInfo.GetCustomAttributes($true) | ForEach-Object { if ($_.TypeId.Name -eq 'DscPropertyAttribute' -and $_.Key -eq $true) @@ -67,8 +66,7 @@ class DscResourceBase $propertyInfo = $_ $PropertyName = $_.Name - $propertyInfo.GetCustomAttributes($true) | - ForEach-Object { + $propertyInfo.GetCustomAttributes($true) | ForEach-Object { if ($_.TypeId.Name -eq 'DscPropertyAttribute') { diff --git a/source/Classes/005.AzDevOpsApiDscResourceBase.ps1 b/source/Classes/005.AzDevOpsApiDscResourceBase.ps1 index 273c4b69e..1757977f3 100644 --- a/source/Classes/005.AzDevOpsApiDscResourceBase.ps1 +++ b/source/Classes/005.AzDevOpsApiDscResourceBase.ps1 @@ -13,8 +13,6 @@ class AzDevOpsApiDscResourceBase : DscResourceBase return $this.GetType().ToString().Replace('AzDevOps','') } - - <# .NOTES When creating an object via the Azure DevOps API, the ID (if provided) is ignored @@ -37,8 +35,6 @@ class AzDevOpsApiDscResourceBase : DscResourceBase return "$($this.ResourceName)Id" } - - <# .NOTES When creating an object via the Azure DevOps API, the 'Key' of the object will be diff --git a/source/Classes/006.AzDevOpsDscResourceBase.ps1 b/source/Classes/006.AzDevOpsDscResourceBase.ps1 index aeaa5457d..d6ce90676 100644 --- a/source/Classes/006.AzDevOpsDscResourceBase.ps1 +++ b/source/Classes/006.AzDevOpsDscResourceBase.ps1 @@ -57,7 +57,8 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase # # Determine the type of Token (PersonalAccessToken or ManagedIdentity) - switch ($tokenObject.tokenType.ToString()) { + switch ($tokenObject.tokenType.ToString()) + { # If the Token is empty { [String]::IsNullOrEmpty($_) } { @@ -84,7 +85,8 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase # # Initialize the cache objects. Don't delete the cache objects since they are used by other resources. - Get-AzDoCacheObjects | ForEach-Object { + Get-AzDoCacheObjects | ForEach-Object + { Initialize-CacheObject -CacheType $_ -BypassFileCheck -Debug Write-Verbose "[AzDevOpsDscResourceBase] Initialized cache object of type: $_" } @@ -132,7 +134,6 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase $props."$_" = $this."$_" } - $props.LookupResult = $this.GetDscCurrentStateResourceObject($getParameters) $props.Ensure = $props.LookupResult.Ensure @@ -274,16 +275,16 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase elseif ($RequiredAction -eq [RequiredAction]::Remove) { - return $desiredStateParameters + return $desiredStateParameters - return @{ - ApiUri = $DesiredStateProperties.ApiUri - Pat = $DesiredStateProperties.Pat - Force = $true + return @{ + ApiUri = $DesiredStateProperties.ApiUri + Pat = $DesiredStateProperties.Pat + Force = $true - # Set this from the 'Current' state as we would expect this to have an existing key/ID value to use - "$IdPropertyName" = $CurrentStateProperties."$IdPropertyName" - } + # Set this from the 'Current' state as we would expect this to have an existing key/ID value to use + "$IdPropertyName" = $CurrentStateProperties."$IdPropertyName" + } } # If the desired state/action is to add/new or update/set the resource, start with the values in the $DesiredStateProperties variable, and amend elseif ($RequiredAction -in @([RequiredAction]::New, [RequiredAction]::Set)) @@ -301,14 +302,12 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase $desiredStateParameters.Remove($IdPropertyName) } - # Do not need/want this passing as a parameter (the action taken will determine the desired state) $desiredStateParameters.Remove('Ensure') # Add this to 'Force' subsequent function call $desiredStateParameters.Force = $true - # Some DSC properties are only supported for 'New' and 'Remove' actions, but not 'Set' ones (these need to be removed) [System.String[]]$unsupportedForSetPropertyNames = $this.GetDscResourcePropertyNamesWithNoSetSupport() diff --git a/source/Classes/007.APIRateLimit.ps1 b/source/Classes/007.APIRateLimit.ps1 index ddbd06a37..94480ebc9 100644 --- a/source/Classes/007.APIRateLimit.ps1 +++ b/source/Classes/007.APIRateLimit.ps1 @@ -1,14 +1,51 @@ -Class APIRateLimit { +<# +.SYNOPSIS + Represents the API rate limit information. + +.DESCRIPTION + The APIRateLimit class encapsulates the details of API rate limiting, including retry-after duration, + remaining rate limit, and rate limit reset time. It provides constructors to initialize these properties + and a method to validate the input hashtable. + +.PROPERTIES + [Int]$retryAfter + The duration (in seconds) to wait before retrying the API request. + + [Int]$xRateLimitRemaining + The number of remaining API requests allowed in the current rate limit window. + + [Int]$xRateLimitReset + The time (in Unix epoch format) when the rate limit will reset. + +.METHODS + [void] APIRateLimit([HashTable]$APIRateLimitObj) + Constructor that initializes the APIRateLimit object using a hashtable containing the rate limit details. + Throws an error if the hashtable is not valid. + + [void] APIRateLimit([int]$retryAfter) + Constructor that initializes the APIRateLimit object with a specified retry-after duration. + + [Bool] isValid([HashTable]$APIRateLimitObj) + Validates that the provided hashtable contains the expected keys for rate limit information. + Returns $true if the hashtable is valid, otherwise returns $false. +#> + +class APIRateLimit +{ [Int]$retryAfter = 0 [Int]$xRateLimitRemaining = 0 [Int]$xRateLimitReset = 0 # Constructor - APIRateLimit([HashTable]$APIRateLimitObj) { + APIRateLimit([HashTable]$APIRateLimitObj) + { # Validate that APIRateLimitObj is a HashTable and Contains the correct keys - if (-not $this.isValid($APIRateLimitObj)) { throw "The APIRateLimitObj is not valid." } + if (-not $this.isValid($APIRateLimitObj)) + { + throw "The APIRateLimitObj is not valid." + } # Convert X-RateLimit-Reset from Unix Time to DateTime $epochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) @@ -21,21 +58,25 @@ Class APIRateLimit { } # Constructor with retryAfter Parameters - APIRateLimit($retryAfter) { + APIRateLimit($retryAfter) + { # Set the properties of the class $this.retryAfter = [int]$retryAfter } - Hidden [Bool]isValid($APIRateLimitObj) { + Hidden [Bool]isValid($APIRateLimitObj) + { # Assuming these are the keys we expect in the hashtable $expectedKeys = @('Retry-After', 'X-RateLimit-Remaining', 'X-RateLimit-Reset') # Check if all expected keys exist in the hashtable - foreach ($key in $expectedKeys) { - if (-not $APIRateLimitObj.ContainsKey($key)) { + foreach ($key in $expectedKeys) + { + if (-not $APIRateLimitObj.ContainsKey($key)) + { Write-Warning "[APIRateLimit] The hashtable does not contain the expected key: $key" return $false } @@ -47,5 +88,4 @@ Class APIRateLimit { } - } diff --git a/source/Classes/009.xAzDoGroupPermission.ps1 b/source/Classes/009.AzDoGroupPermission.ps1 similarity index 78% rename from source/Classes/009.xAzDoGroupPermission.ps1 rename to source/Classes/009.AzDoGroupPermission.ps1 index 3a1a48b50..e06eba722 100644 --- a/source/Classes/009.xAzDoGroupPermission.ps1 +++ b/source/Classes/009.AzDoGroupPermission.ps1 @@ -3,7 +3,7 @@ This class represents a DSC resource for managing Azure DevOps project group permissions. .DESCRIPTION - The xAzDoGroupPermission class is a DSC resource that allows you to manage permissions for a group in an Azure DevOps project. + The AzDoGroupPermission class is a DSC resource that allows you to manage permissions for a group in an Azure DevOps project. .NOTES Author: Your Name @@ -25,13 +25,13 @@ Specifies the permissions to be assigned to the group. This should be an array of hashtables, where each hashtable represents a permission. .EXAMPLE - This example shows how to use the xAzDoGroupPermission resource to manage permissions for a group in an Azure DevOps project. + This example shows how to use the AzDoGroupPermission resource to manage permissions for a group in an Azure DevOps project. Configuration Example { Import-DscResource -ModuleName AzDevOpsDsc Node localhost { - xAzDoGroupPermission GroupPermission { + AzDoGroupPermission GroupPermission { GroupName = 'MyGroup' ProjectName = 'MyProject' Permissions = @( @@ -53,7 +53,7 @@ # RESOURCE IS CURRENTLY DISABLED #[DscResource()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] -class xAzDoGroupPermission : AzDevOpsDscResourceBase +class AzDoGroupPermission : AzDevOpsDscResourceBase { [DscProperty(Key, Mandatory)] [Alias('Name')] @@ -66,14 +66,14 @@ class xAzDoGroupPermission : AzDevOpsDscResourceBase [DscProperty()] [HashTable[]]$Permissions - xAzDoGroupPermission() + AzDoGroupPermission() { $this.Construct() } - [xAzDoGroupPermission] Get() + [AzDoGroupPermission] Get() { - return [xAzDoGroupPermission]$($this.GetDscCurrentStateProperties()) + return [AzDoGroupPermission]$($this.GetDscCurrentStateProperties()) } hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() @@ -89,7 +89,10 @@ class xAzDoGroupPermission : AzDevOpsDscResourceBase } # If the resource object is null, return the properties - if ($null -eq $CurrentResourceObject) { return $properties } + if ($null -eq $CurrentResourceObject) + { + return $properties + } $properties.GroupName = $CurrentResourceObject.GroupName $properties.isInherited = $CurrentResourceObject.isInherited @@ -97,7 +100,7 @@ class xAzDoGroupPermission : AzDevOpsDscResourceBase $properties.lookupResult = $CurrentResourceObject.lookupResult $properties.Ensure = $CurrentResourceObject.Ensure - Write-Verbose "[xAzDoGroupPermission] Current state properties: $($properties | Out-String)" + Write-Verbose "[AzDoGroupPermission] Current state properties: $($properties | Out-String)" return $properties } diff --git a/source/Classes/011.xAzDoOrganizationGroup.ps1 b/source/Classes/011.AzDoOrganizationGroup.ps1 similarity index 75% rename from source/Classes/011.xAzDoOrganizationGroup.ps1 rename to source/Classes/011.AzDoOrganizationGroup.ps1 index 2cb018d90..1f404c22c 100644 --- a/source/Classes/011.xAzDoOrganizationGroup.ps1 +++ b/source/Classes/011.AzDoOrganizationGroup.ps1 @@ -3,7 +3,7 @@ This class represents an Azure DevOps organization group. .DESCRIPTION - The xAzDoOrganizationGroup class is a DSC resource that allows you to manage Azure DevOps organization groups. + The AzDoOrganizationGroup class is a DSC resource that allows you to manage Azure DevOps organization groups. It provides properties to specify the group name, display name, and description. .NOTES @@ -27,9 +27,9 @@ None. .EXAMPLE - This example shows how to create an instance of the xAzDoOrganizationGroup class: + This example shows how to create an instance of the AzDoOrganizationGroup class: - $organizationGroup = [xAzDoOrganizationGroup]::new() + $organizationGroup = [AzDoOrganizationGroup]::new() $organizationGroup.GroupName = "MyGroup" $organizationGroup.GroupDescription = "This is my group." @@ -39,7 +39,7 @@ [DscResource()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] -class xAzDoOrganizationGroup : AzDevOpsDscResourceBase +class AzDoOrganizationGroup : AzDevOpsDscResourceBase { [DscProperty(Key, Mandatory)] [Alias('Name')] @@ -49,14 +49,14 @@ class xAzDoOrganizationGroup : AzDevOpsDscResourceBase [Alias('Description')] [System.String]$GroupDescription - xAzDoOrganizationGroup() + AzDoOrganizationGroup() { $this.Construct() } - [xAzDoOrganizationGroup] Get() + [AzDoOrganizationGroup] Get() { - return [xAzDoOrganizationGroup]$($this.GetDscCurrentStateProperties()) + return [AzDoOrganizationGroup]$($this.GetDscCurrentStateProperties()) } hidden [HashTable] getDscCurrentAPIState() @@ -67,7 +67,7 @@ class xAzDoOrganizationGroup : AzDevOpsDscResourceBase GroupDescription = $this.GroupDescription } - return Get-xAzDoOrganizationGroup @params + return Get-AzDoOrganizationGroup @params } @@ -83,7 +83,10 @@ class xAzDoOrganizationGroup : AzDevOpsDscResourceBase } # If the resource object is null, return the properties - if ($null -eq $CurrentResourceObject) { return $properties } + if ($null -eq $CurrentResourceObject) + { + return $properties + } $properties.GroupName = $CurrentResourceObject.GroupName $properties.GroupDescription = $CurrentResourceObject.GroupDescription @@ -91,9 +94,8 @@ class xAzDoOrganizationGroup : AzDevOpsDscResourceBase $properties.LookupResult = $CurrentResourceObject.LookupResult #$properties.Reasons = $CurrentResourceObject.LookupResult.Reasons - Write-Verbose "[xAzDoOrganizationGroup] Current state properties: $($properties | Out-String)" + Write-Verbose "[AzDoOrganizationGroup] Current state properties: $($properties | Out-String)" return $properties } - } diff --git a/source/Classes/020.xAzDoProject.ps1 b/source/Classes/020.AzDoProject.ps1 similarity index 83% rename from source/Classes/020.xAzDoProject.ps1 rename to source/Classes/020.AzDoProject.ps1 index 7a7fd147a..d0bc95d22 100644 --- a/source/Classes/020.xAzDoProject.ps1 +++ b/source/Classes/020.AzDoProject.ps1 @@ -3,7 +3,7 @@ This class represents an Azure DevOps project. .DESCRIPTION - The xAzDoProject class is used to define and manage Azure DevOps projects. It inherits from the AzDevOpsDscResourceBase class. + The AzDoProject class is used to define and manage Azure DevOps projects. It inherits from the AzDevOpsDscResourceBase class. .NOTES Author: Your Name @@ -34,7 +34,7 @@ None .EXAMPLE - $project = [xAzDoProject]::Get() + $project = [AzDoProject]::Get() $project.ProjectName = 'MyProject' $project.ProjectDescription = 'This is a sample project' $project.SourceControlType = 'Git' @@ -46,7 +46,7 @@ [DscResource()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] -class xAzDoProject : AzDevOpsDscResourceBase +class AzDoProject : AzDevOpsDscResourceBase { [DscProperty(Key, Mandatory)] [Alias('Name')] @@ -68,14 +68,14 @@ class xAzDoProject : AzDevOpsDscResourceBase [ValidateSet('Public', 'Private')] [System.String]$Visibility = 'Private' - xAzDoProject() + AzDoProject() { $this.Construct() } - [xAzDoProject] Get() + [AzDoProject] Get() { - return [xAzDoProject]$($this.GetDscCurrentStateProperties()) + return [AzDoProject]$($this.GetDscCurrentStateProperties()) } @@ -91,7 +91,10 @@ class xAzDoProject : AzDevOpsDscResourceBase } # If the resource object is null, return the properties - if ($null -eq $CurrentResourceObject) { return $properties } + if ($null -eq $CurrentResourceObject) + { + return $properties + } $properties.ProjectName = $CurrentResourceObject.ProjectName $properties.ProjectDescription = $CurrentResourceObject.ProjectDescription @@ -101,7 +104,7 @@ class xAzDoProject : AzDevOpsDscResourceBase $properties.LookupResult = $CurrentResourceObject.LookupResult $properties.Ensure = $CurrentResourceObject.Ensure - Write-Verbose "[xAzDoGroupPermission] Current state properties: $($properties | Out-String)" + Write-Verbose "[AzDoGroupPermission] Current state properties: $($properties | Out-String)" return $properties diff --git a/source/Classes/021.xAzDoProjectServices.ps1 b/source/Classes/021.AzDoProjectServices.ps1 similarity index 81% rename from source/Classes/021.xAzDoProjectServices.ps1 rename to source/Classes/021.AzDoProjectServices.ps1 index 7b6e99e1b..805bad0e9 100644 --- a/source/Classes/021.xAzDoProjectServices.ps1 +++ b/source/Classes/021.AzDoProjectServices.ps1 @@ -3,7 +3,7 @@ This class represents an Azure DevOps project and its associated services. .DESCRIPTION - The xAzDoProjectServices class is a DSC resource that allows you to manage the services associated with an Azure DevOps project. It provides properties to enable or disable various services such as Git repositories, work boards, build pipelines, test plans, and Azure artifacts. + The AzDoProjectServices class is a DSC resource that allows you to manage the services associated with an Azure DevOps project. It provides properties to enable or disable various services such as Git repositories, work boards, build pipelines, test plans, and Azure artifacts. .PARAMETER ProjectName The name of the Azure DevOps project. @@ -24,13 +24,13 @@ Specifies whether Azure artifacts are enabled or disabled for the project. Valid values are 'Enabled' and 'Disabled'. The default value is 'Enabled'. .EXAMPLE - This example shows how to use the xAzDoProjectServices resource to manage the services of an Azure DevOps project. + This example shows how to use the AzDoProjectServices resource to manage the services of an Azure DevOps project. Configuration Example { Import-DscResource -ModuleName xAzDevOpsDSC Node localhost { - xAzDoProjectServices ProjectServices { + AzDoProjectServices ProjectServices { ProjectName = 'MyProject' GitRepositories = 'Enabled' WorkBoards = 'Disabled' @@ -50,7 +50,7 @@ [DscResource()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] -class xAzDoProjectServices : AzDevOpsDscResourceBase +class AzDoProjectServices : AzDevOpsDscResourceBase { [DscProperty(Mandatory, Key)] [Alias('Name')] @@ -81,14 +81,14 @@ class xAzDoProjectServices : AzDevOpsDscResourceBase [ValidateSet('Enabled', 'Disabled')] [System.String]$AzureArtifact = 'Enabled' - xAzDoProjectServices() + AzDoProjectServices() { $this.Construct() } - [xAzDoProjectServices] Get() + [AzDoProjectServices] Get() { - return [xAzDoProjectServices]$($this.GetDscCurrentStateProperties()) + return [AzDoProjectServices]$($this.GetDscCurrentStateProperties()) } hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() @@ -103,7 +103,10 @@ class xAzDoProjectServices : AzDevOpsDscResourceBase } # If the resource object is null, return the properties - if ($null -eq $CurrentResourceObject) { return $properties } + if ($null -eq $CurrentResourceObject) + { + return $properties + } $properties.ProjectName = $CurrentResourceObject.ProjectName $properties.GitRepositories = $CurrentResourceObject.GitRepositories @@ -114,7 +117,7 @@ class xAzDoProjectServices : AzDevOpsDscResourceBase $properties.Ensure = $CurrentResourceObject.Ensure $properties.LookupResult = $CurrentResourceObject.LookupResult - Write-Verbose "[xAzDoProjectGroup] Current state properties: $($properties | Out-String)" + Write-Verbose "[AzDoProjectGroup] Current state properties: $($properties | Out-String)" return $properties } diff --git a/source/Classes/022.xAzDoProjectGroup.ps1 b/source/Classes/022.AzDoProjectGroup.ps1 similarity index 81% rename from source/Classes/022.xAzDoProjectGroup.ps1 rename to source/Classes/022.AzDoProjectGroup.ps1 index 22e90c89e..3927ca579 100644 --- a/source/Classes/022.xAzDoProjectGroup.ps1 +++ b/source/Classes/022.AzDoProjectGroup.ps1 @@ -3,7 +3,7 @@ This class represents an Azure DevOps project group. .DESCRIPTION - The xAzDoProjectGroup class is a DSC resource that allows you to manage Azure DevOps project groups. + The AzDoProjectGroup class is a DSC resource that allows you to manage Azure DevOps project groups. .NOTES Author: Michael Zanatta @@ -24,7 +24,7 @@ .EXAMPLE This example shows how to create a new project group: - $projectGroup = [xAzDoProjectGroup]::new() + $projectGroup = [AzDoProjectGroup]::new() $projectGroup.ProjectName = "MyProject" $projectGroup.GroupName = "MyGroup" $projectGroup.GroupDescription = "This is my project group." @@ -43,7 +43,7 @@ [DscResource()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] -class xAzDoProjectGroup : AzDevOpsDscResourceBase +class AzDoProjectGroup : AzDevOpsDscResourceBase { [DscProperty(Key, Mandatory)] [Alias('Name')] @@ -57,14 +57,14 @@ class xAzDoProjectGroup : AzDevOpsDscResourceBase [Alias('Description')] [System.String]$GroupDescription - xAzDoProjectGroup() + AzDoProjectGroup() { $this.Construct() } - [xAzDoProjectGroup] Get() + [AzDoProjectGroup] Get() { - return [xAzDoProjectGroup]$($this.GetDscCurrentStateProperties()) + return [AzDoProjectGroup]$($this.GetDscCurrentStateProperties()) } hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() @@ -81,7 +81,7 @@ class xAzDoProjectGroup : AzDevOpsDscResourceBase ProjectName = $this.ProjectName } - return Get-xAzDoProjectGroup @params + return Get-AzDoProjectGroup @params } @@ -92,7 +92,10 @@ class xAzDoProjectGroup : AzDevOpsDscResourceBase } # If the resource object is null, return the properties - if ($null -eq $CurrentResourceObject) { return $properties } + if ($null -eq $CurrentResourceObject) + { + return $properties + } $properties.GroupName = $CurrentResourceObject.GroupName $properties.GroupDescription = $CurrentResourceObject.GroupDescription @@ -101,7 +104,7 @@ class xAzDoProjectGroup : AzDevOpsDscResourceBase $properties.LookupResult = $CurrentResourceObject.LookupResult #$properties.Reasons = $CurrentResourceObject.LookupResult.Reasons - Write-Verbose "[xAzDoProjectGroup] Current state properties: $($properties | Out-String)" + Write-Verbose "[AzDoProjectGroup] Current state properties: $($properties | Out-String)" return $properties } diff --git a/source/Classes/031.xAzDoGroupMember.ps1 b/source/Classes/031.AzDoGroupMember.ps1 similarity index 73% rename from source/Classes/031.xAzDoGroupMember.ps1 rename to source/Classes/031.AzDoGroupMember.ps1 index 00c918e08..b4efc3a37 100644 --- a/source/Classes/031.xAzDoGroupMember.ps1 +++ b/source/Classes/031.AzDoGroupMember.ps1 @@ -3,7 +3,7 @@ This class represents a DSC resource for managing Azure DevOps group members. .DESCRIPTION - The xAzDoGroupMember class is a DSC resource that allows you to manage the members of an Azure DevOps group. + The AzDoGroupMember class is a DSC resource that allows you to manage the members of an Azure DevOps group. It inherits from the AzDevOpsDscResourceBase class. .NOTES @@ -20,13 +20,13 @@ An array of strings representing the members of the Azure DevOps group. .EXAMPLE - This example shows how to use the xAzDoGroupMember resource to add members to an Azure DevOps group. + This example shows how to use the AzDoGroupMember resource to add members to an Azure DevOps group. Configuration Example { - Import-DscResource -ModuleName xAzDoGroupMember + Import-DscResource -ModuleName AzDoGroupMember Node localhost { - xAzDoGroupMember GroupMember { + AzDoGroupMember GroupMember { GroupName = 'MyGroup' GroupMembers = @('User1', 'User2', 'User3') Ensure = 'Present' @@ -44,7 +44,7 @@ [DscResource()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] -class xAzDoGroupMember : AzDevOpsDscResourceBase +class AzDoGroupMember : AzDevOpsDscResourceBase { [DscProperty(Key, Mandatory)] [Alias('Name')] @@ -54,14 +54,14 @@ class xAzDoGroupMember : AzDevOpsDscResourceBase [Alias('Members')] [System.String[]]$GroupMembers - xAzDoGroupMember() + AzDoGroupMember() { $this.Construct() } - [xAzDoGroupMember] Get() + [AzDoGroupMember] Get() { - return [xAzDoGroupMember]$($this.GetDscCurrentStateProperties()) + return [AzDoGroupMember]$($this.GetDscCurrentStateProperties()) } hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() @@ -76,14 +76,17 @@ class xAzDoGroupMember : AzDevOpsDscResourceBase } # If the resource object is null, return the properties - if ($null -eq $CurrentResourceObject) { return $properties } + if ($null -eq $CurrentResourceObject) + { + return $properties + } $properties.GroupName = $CurrentResourceObject.GroupName $properties.GroupMembers = $CurrentResourceObject.GroupMembers $properties.Ensure = $CurrentResourceObject.Ensure $properties.LookupResult = $CurrentResourceObject.LookupResult - Write-Verbose "[xAzDoProjectGroup] Current state properties: $($properties | Out-String)" + Write-Verbose "[AzDoProjectGroup] Current state properties: $($properties | Out-String)" return $properties } diff --git a/source/Classes/040.xAzDoGitRepository.ps1 b/source/Classes/040.AzDoGitRepository.ps1 similarity index 74% rename from source/Classes/040.xAzDoGitRepository.ps1 rename to source/Classes/040.AzDoGitRepository.ps1 index 25fe4003f..80d5f3470 100644 --- a/source/Classes/040.xAzDoGitRepository.ps1 +++ b/source/Classes/040.AzDoGitRepository.ps1 @@ -3,7 +3,7 @@ This class represents an Azure DevOps Git repository. .DESCRIPTION - The xAzDoGitRepository class is a DSC resource that allows you to manage Azure DevOps Git repositories. + The AzDoGitRepository class is a DSC resource that allows you to manage Azure DevOps Git repositories. It inherits from the AzDevOpsDscResourceBase class. .NOTES @@ -23,12 +23,12 @@ The source repository URL. .EXAMPLE - This example shows how to use the xAzDoGitRepository resource to ensure that a Git repository exists in an Azure DevOps project. + This example shows how to use the AzDoGitRepository resource to ensure that a Git repository exists in an Azure DevOps project. Configuration Example { - Import-DscResource -ModuleName xAzDoGitRepository + Import-DscResource -ModuleName AzDoGitRepository - xAzDoGitRepository MyGitRepository { + AzDoGitRepository MyGitRepository { ProjectName = 'MyProject' GitRepositoryName = 'MyRepository' SourceRepository = 'https://github.com/MyUser/MyRepository.git' @@ -45,7 +45,7 @@ [DscResource()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] -class xAzDoGitRepository : AzDevOpsDscResourceBase +class AzDoGitRepository : AzDevOpsDscResourceBase { [DscProperty(Mandatory)] [Alias('Name')] @@ -59,14 +59,14 @@ class xAzDoGitRepository : AzDevOpsDscResourceBase [Alias('Source')] [System.String]$SourceRepository - xAzDoGitRepository() + AzDoGitRepository() { $this.Construct() } - [xAzDoGitRepository] Get() + [AzDoGitRepository] Get() { - return [xAzDoGitRepository]$($this.GetDscCurrentStateProperties()) + return [AzDoGitRepository]$($this.GetDscCurrentStateProperties()) } hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() @@ -81,7 +81,10 @@ class xAzDoGitRepository : AzDevOpsDscResourceBase } # If the resource object is null, return the properties - if ($null -eq $CurrentResourceObject) { return $properties } + if ($null -eq $CurrentResourceObject) + { + return $properties + } $properties.ProjectName = $CurrentResourceObject.ProjectName $properties.RepositoryName = $CurrentResourceObject.RepositoryName @@ -89,7 +92,7 @@ class xAzDoGitRepository : AzDevOpsDscResourceBase $properties.Ensure = $CurrentResourceObject.Ensure $properties.LookupResult = $CurrentResourceObject.LookupResult - Write-Verbose "[xAzDoProjectGroup] Current state properties: $($properties | Out-String)" + Write-Verbose "[AzDoProjectGroup] Current state properties: $($properties | Out-String)" return $properties } diff --git a/source/Classes/041.xAzDoGitPermission.ps1 b/source/Classes/041.AzDoGitPermission.ps1 similarity index 75% rename from source/Classes/041.xAzDoGitPermission.ps1 rename to source/Classes/041.AzDoGitPermission.ps1 index 5ef142792..65a8e51a2 100644 --- a/source/Classes/041.xAzDoGitPermission.ps1 +++ b/source/Classes/041.AzDoGitPermission.ps1 @@ -3,7 +3,7 @@ This class represents an Azure DevOps DSC resource for managing Git permissions. .DESCRIPTION - The xAzDoGitPermission class is a DSC resource that allows you to manage Git permissions in Azure DevOps. It inherits from the AzDevOpsDscResourceBase class and provides properties and methods for managing Git permissions. + The AzDoGitPermission class is a DSC resource that allows you to manage Git permissions in Azure DevOps. It inherits from the AzDevOpsDscResourceBase class and provides properties and methods for managing Git permissions. .PARAMETER ProjectName Specifies the name of the Azure DevOps project. @@ -24,13 +24,13 @@ https://github.com/Azure/AzureDevOpsDSC .EXAMPLE - This example shows how to use the xAzDoGitPermission class to manage Git permissions in Azure DevOps. + This example shows how to use the AzDoGitPermission class to manage Git permissions in Azure DevOps. Configuration Example { Import-DscResource -ModuleName AzureDevOpsDSC Node localhost { - xAzDoGitPermission GitPermission { + AzDoGitPermission GitPermission { ProjectName = 'MyProject' RepositoryName = 'MyRepository' PermissionsList = @('Read', 'Contribute') @@ -43,7 +43,7 @@ [DscResource()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSDSCStandardDSCFunctionsInResource', '', Justification='Test() and Set() method are inherited from base, "AzDevOpsDscResourceBase" class')] -class xAzDoGitPermission : AzDevOpsDscResourceBase +class AzDoGitPermission : AzDevOpsDscResourceBase { [DscProperty(Key, Mandatory)] [Alias('Name')] @@ -60,14 +60,14 @@ class xAzDoGitPermission : AzDevOpsDscResourceBase [DscProperty()] [HashTable[]]$Permissions - xAzDoGitPermission() + AzDoGitPermission() { $this.Construct() } - [xAzDoGitPermission] Get() + [AzDoGitPermission] Get() { - return [xAzDoGitPermission]$($this.GetDscCurrentStateProperties()) + return [AzDoGitPermission]$($this.GetDscCurrentStateProperties()) } hidden [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() @@ -82,7 +82,10 @@ class xAzDoGitPermission : AzDevOpsDscResourceBase } # If the resource object is null, return the properties - if ($null -eq $CurrentResourceObject) { return $properties } + if ($null -eq $CurrentResourceObject) + { + return $properties + } $properties.ProjectName = $CurrentResourceObject.ProjectName $properties.RepositoryName = $CurrentResourceObject.RepositoryName @@ -91,7 +94,7 @@ class xAzDoGitPermission : AzDevOpsDscResourceBase $properties.lookupResult = $CurrentResourceObject.lookupResult $properties.Ensure = $CurrentResourceObject.Ensure - Write-Verbose "[xAzDoGitPermission] Current state properties: $($properties | Out-String)" + Write-Verbose "[AzDoGitPermission] Current state properties: $($properties | Out-String)" return $properties } diff --git a/source/Enum/DSCGetSummaryState.ps1 b/source/Enum/DSCGetSummaryState.ps1 index 8010f7ccc..1c7e731a0 100644 --- a/source/Enum/DSCGetSummaryState.ps1 +++ b/source/Enum/DSCGetSummaryState.ps1 @@ -1,3 +1,22 @@ +<# +.SYNOPSIS + Enumeration for DSC Get Summary State. + +.DESCRIPTION + This enumeration defines the possible states for DSC (Desired State Configuration) summary results. + +.MEMBERS + Changed + Indicates that the state has changed. + Unchanged + Indicates that the state is unchanged. + NotFound + Indicates that the state was not found. + Renamed + Indicates that the state has been renamed. + Missing + Indicates that the state is missing. +#> enum DSCGetSummaryState { # Changed diff --git a/source/Enum/DescriptorType.ps1 b/source/Enum/DescriptorType.ps1 index df9d33a7b..608b6027d 100644 --- a/source/Enum/DescriptorType.ps1 +++ b/source/Enum/DescriptorType.ps1 @@ -1,3 +1,73 @@ +<# +.SYNOPSIS +Defines an enumeration for various descriptor types used in Azure DevOps. + +.DESCRIPTION +The `DescriptorType` enumeration lists various descriptor types that are used within Azure DevOps. These descriptors represent different services, features, and components within the Azure DevOps environment. + +.ENUMERATION MEMBERS +- Analytics +- AnalyticsViews +- ReleaseManagement +- AuditLog +- Identity +- WorkItemTrackingAdministration +- DistributedTask +- GitRepositories +- VersionControlItems2 +- EventSubscriber +- WorkItemTrackingProvision +- ServiceEndpoints +- ServiceHooks +- Collection +- Proxy +- Plan +- Process +- AccountAdminSecurity +- Library +- Environment +- Project +- EventSubscription +- ProjectAnalysisLanguageMetrics +- Tagging +- MetaTask +- Iteration +- WorkItemQueryFolders +- Favorites +- Registry +- Graph +- ViewActivityPaneSecurity +- Job +- EventPublish +- WorkItemTracking +- StrongBox +- Server +- TestManagement +- SettingEntries +- BuildAdministration +- Location +- Boards +- OrganizationLevelData +- UtilizationPermissions +- WorkItemsHub +- WebPlatform +- VersionControlPrivileges +- Workspaces +- CrossProjectWidgetView +- WorkItemTrackingConfiguration +- DiscussionThreads +- BoardsExternalIntegration +- DataProvider +- Social +- Security +- IdentityPicker +- ServicingOrchestration +- Build +- DashboardsPrivileges +- CSS +- VersionControlItems + +#> Enum DescriptorType { Analytics diff --git a/source/Enum/TokenType.ps1 b/source/Enum/TokenType.ps1 index 45d522ef5..0c6163f04 100644 --- a/source/Enum/TokenType.ps1 +++ b/source/Enum/TokenType.ps1 @@ -1,4 +1,29 @@ -Enum TokenType { +<# +.SYNOPSIS +Defines the types of tokens used for authentication. + +.DESCRIPTION +The TokenType enumeration specifies the different types of tokens that can be used for authentication purposes. This includes Managed Identity, Personal Access Token, and Certificate. + +.ENUMERATION MEMBERS +ManagedIdentity + Represents a managed identity token used for authentication. + +PersonalAccessToken + Represents a personal access token used for authentication. + +Certificate + Represents a certificate used for authentication. + +.EXAMPLE +# To use the TokenType enumeration: +$tokenType = [TokenType]::ManagedIdentity + +.NOTES +This enumeration is part of the AzureDevOpsDsc module. +#> +enum TokenType +{ ManagedIdentity PersonalAccessToken Certificate diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 index b908de738..c3ddd5345 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 @@ -1,3 +1,33 @@ +<# +.SYNOPSIS +Represents a cache item with a key, value, and creation timestamp. + +.DESCRIPTION +The CacheItem class is used to store a key-value pair along with the timestamp when the item was created. +It ensures that the key is not null or empty upon instantiation. + +.PARAMETER Key +The key associated with the cache item. It must be a non-empty string. + +.PARAMETER Value +The value associated with the cache item. It can be any object. + +.PARAMETER created +The timestamp when the cache item was created. It is automatically set to the current date and time upon instantiation. + +.CONSTRUCTOR +CacheItem([string] $Key, [object] $Value) +Creates a new instance of the CacheItem class with the specified key and value. +Throws an exception if the key is null or empty. + +.EXAMPLE +$cacheItem = [CacheItem]::new("exampleKey", "exampleValue") +Creates a new CacheItem instance with the key "exampleKey" and the value "exampleValue". + +.NOTES +Author: Your Name +#> + class CacheItem { [string] $Key diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.ps1 index cc6bf9429..b4c2cbd64 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.ps1 @@ -1,10 +1,10 @@ function Get-DevOpsACL { param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$OrganizationName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String]$SecurityDescriptorId, [Parameter()] @@ -12,10 +12,9 @@ function Get-DevOpsACL $ApiVersion = $(Get-AzDevOpsApiVersion -Default) ) - # # Construct the URL for the API call $params = @{ - Uri = "https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}" -f $OrganizationName, $SecurityDescriptorId, $ApiVersion + Uri = 'https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}' -f $OrganizationName, $SecurityDescriptorId, $ApiVersion Method = 'Get' } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.ps1 index 51bfb43ab..192d20ad8 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.ps1 @@ -20,18 +20,18 @@ Get-DevOpsDescriptorIdentity -OrganizationName "MyOrg" -SubjectDescriptor "subje This example retrieves the identity associated with the subject descriptor "subject:abcd1234" in the Azure DevOps organization "MyOrg". #> -Function Get-DevOpsDescriptorIdentity { - +Function Get-DevOpsDescriptorIdentity +{ [CmdletBinding(DefaultParameterSetName = 'Default')] param( - [Parameter(Mandatory, ParameterSetName = 'Default')] - [Parameter(Mandatory, ParameterSetName = 'Descriptors')] + [Parameter(Mandatory = $true, ParameterSetName = 'Default')] + [Parameter(Mandatory = $true, ParameterSetName = 'Descriptors')] [string]$OrganizationName, - [Parameter(Mandatory, ParameterSetName = 'Default')] + [Parameter(Mandatory = $true, ParameterSetName = 'Default')] [String]$SubjectDescriptor, - [Parameter(Mandatory, ParameterSetName = 'Descriptors')] + [Parameter(Mandatory = $true, ParameterSetName = 'Descriptors')] [String]$Descriptor, [Parameter(ParameterSetName = 'Default')] @@ -41,23 +41,27 @@ Function Get-DevOpsDescriptorIdentity { ) # Determine the query parameter based on the parameter set - if ($SubjectDescriptor) { + if ($SubjectDescriptor) + { $query = "subjectDescriptors=$SubjectDescriptor" - } else { + } + else + { $query = "descriptors=$Descriptor" } # # Construct the URL for the API call $params = @{ - Uri = "https://vssps.dev.azure.com/{0}/_apis/identities?{1}&api-version={2}" -f $OrganizationName, $query, $ApiVersion + Uri = 'https://vssps.dev.azure.com/{0}/_apis/identities?{1}&api-version={2}' -f $OrganizationName, $query, $ApiVersion Method = 'Get' } # Invoke the REST API call $identity = Invoke-AzDevOpsApiRestMethod @params - if (($null -eq $identity.value) -or ($identity.count -gt 1)) { + if (($null -eq $identity.value) -or ($identity.count -gt 1)) + { return $null } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-AzDoPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-AzDoPermission.ps1 new file mode 100644 index 000000000..07e032f87 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-AzDoPermission.ps1 @@ -0,0 +1,87 @@ +<# +.SYNOPSIS +Removes access control lists (ACLs) for a specified token in an Azure DevOps organization. + +.DESCRIPTION +The Remove-AzDoPermission function removes ACLs for a specified token within a given security namespace in an Azure DevOps organization. It constructs the appropriate API endpoint and invokes a REST method to delete the ACLs. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization. + +.PARAMETER SecurityNamespaceID +The ID of the security namespace. + +.PARAMETER TokenName +The name of the token for which the ACLs should be removed. + +.PARAMETER ApiVersion +The version of the Azure DevOps API to use. If not specified, the default API version is used. + +.EXAMPLE +Remove-AzDoPermission -OrganizationName "MyOrg" -SecurityNamespaceID "12345" -TokenName "MyToken" + +This example removes the ACLs for the token "MyToken" in the security namespace with ID "12345" within the "MyOrg" organization. + +.NOTES +This function uses the Invoke-AzDevOpsApiRestMethod function to perform the REST API call. Ensure that the necessary permissions are in place to delete ACLs in the specified Azure DevOps organization. + +#> +Function Remove-AzDoPermission +{ + param( + [Parameter(Mandatory = $true)] + [string]$OrganizationName, + + [Parameter(Mandatory = $true)] + [string]$SecurityNamespaceID, + + [Parameter(Mandatory = $true)] + [string]$TokenName, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + Write-Verbose "[Remove-AzDoPermission] Started." + + # Define a hashtable to store parameters for the Invoke-AzDevOpsApiRestMethod function. + $params = @{ + <# + Construct the Uri using string formatting with the -f operator. + It includes the API endpoint, group identity, member identity, and the API version. + #> + Uri = 'https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?tokens={2}&recurse=False&api-version={3}' -f $OrganizationName, + $SecurityNamespaceID, + $TokenName, + $ApiVersion + # Set the method to DELETE. + Method = 'DELETE' + } + + try + { + <# + Call the Invoke-AzDevOpsApiRestMethod function with the parameters defined above. + The "@" symbol is used to pass the hashtable as splatting parameters. + #> + Write-Verbose "[Remove-AzDoPermission] Attempting to invoke REST method to remove ACLs." + $member = Invoke-AzDevOpsApiRestMethod @params + + if ($member -ne $true) + { + Write-Error "[Remove-AzDoPermission] Failed to remove ACLs." + } + else + { + Write-Verbose "[Remove-AzDoPermission] ACLs removed successfully." + } + + } + catch + { + # If an exception occurs, write an error message to the console with details about the issue. + Write-Error "[Remove-AzDoPermission] Failed to add member to group: $($_.Exception.Message)" + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.ps1 deleted file mode 100644 index 9c4c92b51..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -Function Remove-xAzDoPermission -{ - param( - [Parameter(Mandatory)] - [string]$OrganizationName, - - [Parameter(Mandatory)] - [string]$SecurityNamespaceID, - - [Parameter(Mandatory)] - [string]$TokenName, - - [Parameter()] - [String] - $ApiVersion = $(Get-AzDevOpsApiVersion -Default) - ) - - Write-Verbose "[Remove-xAzDoPermission] Started." - - # Define a hashtable to store parameters for the Invoke-AzDevOpsApiRestMethod function. - $params = @{ - # Construct the Uri using string formatting with the -f operator. - # It includes the API endpoint, group identity, member identity, and the API version. - Uri = "https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?tokens={2}&recurse=False&api-version={3}" -f $OrganizationName, - $SecurityNamespaceID, - $TokenName, - $ApiVersion - # Set the method to DELETE. - Method = 'DELETE' - } - - - try { - # Call the Invoke-AzDevOpsApiRestMethod function with the parameters defined above. - # The "@" symbol is used to pass the hashtable as splatting parameters. - Write-Verbose "[Remove-xAzDoPermission] Attempting to invoke REST method to remove ACLs." - $member = Invoke-AzDevOpsApiRestMethod @params - if ($member -ne $true) { - Write-Error "[Remove-xAzDoPermission] Failed to remove ACLs." - } else { - Write-Verbose "[Remove-xAzDoPermission] ACLs removed successfully." - } - - } catch { - # If an exception occurs, write an error message to the console with details about the issue. - Write-Error "[Remove-xAzDoPermission] Failed to add member to group: $($_.Exception.Message)" - } - - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-AzDoPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-AzDoPermission.ps1 new file mode 100644 index 000000000..84c593aec --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-AzDoPermission.ps1 @@ -0,0 +1,85 @@ +<# +.SYNOPSIS +Sets Azure DevOps permissions by invoking a REST API method. + +.DESCRIPTION +The Set-AzDoPermission function sets permissions in Azure DevOps by sending a POST request to the specified API endpoint. +It constructs the URI using the organization name, security namespace ID, and API version. The function serializes the +Access Control Lists (ACLs) and sends them in the body of the request. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization. + +.PARAMETER SecurityNamespaceID +The ID of the security namespace. + +.PARAMETER SerializedACLs +The serialized Access Control Lists (ACLs) to be set. + +.PARAMETER ApiVersion +The version of the Azure DevOps API to use. Defaults to the value returned by Get-AzDevOpsApiVersion -Default. + +.EXAMPLE +Set-AzDoPermission -OrganizationName "MyOrg" -SecurityNamespaceID "12345" -SerializedACLs $acls + +This example sets the permissions for the specified organization and security namespace using the provided ACLs. + +.NOTES +The function uses the Invoke-AzDevOpsApiRestMethod to send the request. If an error occurs during the request, +an error message is written to the console. +#> + +Function Set-AzDoPermission +{ + param( + [Parameter(Mandatory = $true)] + [string]$OrganizationName, + + [Parameter(Mandatory = $true)] + [string]$SecurityNamespaceID, + + [Parameter(Mandatory = $true)] + [Object]$SerializedACLs, + + [Parameter()] + [String] + $ApiVersion = $(Get-AzDevOpsApiVersion -Default) + ) + + Write-Verbose "[Set-AzDoPermission] Started." + + # Define a hashtable to store parameters for the Invoke-AzDevOpsApiRestMethod function. + + $params = @{ + <# + Construct the Uri using string formatting with the -f operator. + It includes the API endpoint, group identity, member identity, and the API version. + #> + Uri = 'https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}' -f $OrganizationName, + $SecurityNamespaceID, + $ApiVersion + # Set the method to PUT. + Method = 'POST' + # Set the body of the request to the serialized ACLs. + Body = $SerializedACLs | ConvertTo-Json -Depth 4 + } + + Write-Verbose "[Set-AzDoPermission] Body: $($params.Body)" + + try + { + <# + Call the Invoke-AzDevOpsApiRestMethod function with the parameters defined above. + The "@" symbol is used to pass the hashtable as splatting parameters. + #> + Write-Verbose "[Set-AzDoPermission] Attempting to invoke REST method to set ACLs." + $null = Invoke-AzDevOpsApiRestMethod @params + + } + catch + { + # If an exception occurs, write an error message to the console with details about the issue. + Write-Error "[Set-AzDoPermission] Failed to set ACLs: $($_.Exception.Message)" + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.ps1 deleted file mode 100644 index 728ca916a..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.ps1 +++ /dev/null @@ -1,49 +0,0 @@ - - -Function Set-xAzDoPermission -{ - param( - [Parameter(Mandatory)] - [string]$OrganizationName, - - [Parameter(Mandatory)] - [string]$SecurityNamespaceID, - - [Parameter(Mandatory)] - [Object]$SerializedACLs, - - [Parameter()] - [String] - $ApiVersion = $(Get-AzDevOpsApiVersion -Default) - ) - - Write-Verbose "[Set-xAzDoPermission] Started." - - # Define a hashtable to store parameters for the Invoke-AzDevOpsApiRestMethod function. - - $params = @{ - # Construct the Uri using string formatting with the -f operator. - # It includes the API endpoint, group identity, member identity, and the API version. - Uri = "https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}" -f $OrganizationName, - $SecurityNamespaceID, - $ApiVersion - # Set the method to PUT. - Method = 'POST' - # Set the body of the request to the serialized ACLs. - Body = $SerializedACLs | ConvertTo-Json -Depth 4 - } - - Write-Verbose "[Set-xAzDoPermission] Body: $($params.Body)" - - try { - # Call the Invoke-AzDevOpsApiRestMethod function with the parameters defined above. - # The "@" symbol is used to pass the hashtable as splatting parameters. - Write-Verbose "[Set-xAzDoPermission] Attempting to invoke REST method to set ACLs." - $null = Invoke-AzDevOpsApiRestMethod @params - - } catch { - # If an exception occurs, write an error message to the console with details about the issue. - Write-Error "[Set-xAzDoPermission] Failed to set ACLs: $($_.Exception.Message)" - } - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.ps1 index 33afa9d93..f70d7b53c 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.ps1 @@ -1,12 +1,37 @@ +<# +.SYNOPSIS +Retrieves a list of Git repositories from an Azure DevOps project. + +.DESCRIPTION +The List-DevOpsGitRepository function invokes the Azure DevOps REST API to retrieve a list of Git repositories for a specified organization and project. The function returns the list of repositories if available. + +.PARAMETER OrganizationName +Specifies the name of the Azure DevOps organization. This parameter is mandatory. + +.PARAMETER ProjectName +Specifies the name of the Azure DevOps project. This parameter is mandatory. + +.PARAMETER ApiVersion +Specifies the API version to use when making the request. If not provided, the default API version is used. + +.RETURNS +Returns a list of Git repositories if available; otherwise, returns $null. + +.EXAMPLE +PS> List-DevOpsGitRepository -OrganizationName "MyOrg" -ProjectName "MyProject" + +.NOTES +This function requires the Azure DevOps module to be installed and configured. +#> function List-DevOpsGitRepository { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$OrganizationName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String]$ProjectName, [Parameter()] @@ -19,10 +44,10 @@ function List-DevOpsGitRepository Method = 'Get' } - # # Invoke the Rest API to get the groups $repositories = Invoke-AzDevOpsApiRestMethod @params + # Return the groups from the cache if ($null -eq $repositories.value) { return $null diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.ps1 index 60ba0a056..1913744e3 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.ps1 @@ -1,11 +1,36 @@ +<# +.SYNOPSIS +Retrieves the members of a specified Azure DevOps group. + +.DESCRIPTION +The List-DevOpsGroupMembers function retrieves the members of a specified Azure DevOps group by invoking the Azure DevOps REST API. +It requires the organization name and group descriptor as mandatory parameters. Optionally, an API version can be specified. + +.PARAMETER Organization +The name of the Azure DevOps organization. + +.PARAMETER GroupDescriptor +The descriptor of the Azure DevOps group whose members are to be retrieved. + +.PARAMETER ApiVersion +The version of the Azure DevOps API to use. If not specified, the default API version is used. + +.RETURNS +Returns the members of the specified Azure DevOps group. If no members are found, returns $null. + +.EXAMPLE +$list = List-DevOpsGroupMembers -Organization "myOrg" -GroupDescriptor "vssgp.Uy1zNTk3NzA3LTY3NzgtNDk4NC04YjE4LTYxZDE3YjY2YjA3Nw==" +This example retrieves the members of the specified Azure DevOps group in the "myOrg" organization. + +#> function List-DevOpsGroupMembers { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $Organization, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String] $GroupDescriptor, [Parameter()] @@ -14,7 +39,7 @@ function List-DevOpsGroupMembers ) $params = @{ - Uri = "https://vssps.dev.azure.com/{0}/_apis/graph/Memberships/{1}?direction=down" -f $Organization, $GroupDescriptor + Uri = 'https://vssps.dev.azure.com/{0}/_apis/graph/Memberships/{1}?direction=down' -f $Organization, $GroupDescriptor Method = 'Get' } @@ -22,7 +47,9 @@ function List-DevOpsGroupMembers # Invoke the Rest API to get the groups $membership = Invoke-AzDevOpsApiRestMethod @params - if ($null -eq $membership.value) { + # Return the groups from the cache + if ($null -eq $membership.value) + { return $null } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.ps1 index 6fa91d6d6..683ecb4ea 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.ps1 @@ -1,9 +1,35 @@ -Function List-DevOpsGroups { +<# +.SYNOPSIS + Retrieves a list of DevOps groups for a specified organization. + +.DESCRIPTION + This function invokes the Azure DevOps REST API to retrieve a list of groups within a specified organization. + It uses the provided organization name and an optional API version to make the request. + +.PARAMETER Organization + The name of the Azure DevOps organization for which to retrieve the groups. + This parameter is mandatory. + +.PARAMETER ApiVersion + The version of the Azure DevOps API to use for the request. + If not specified, the default API version is used. + +.OUTPUTS + System.Object + Returns an array of groups if found, otherwise returns $null. + +.EXAMPLE + List-DevOpsGroups -Organization "myOrganization" + + This example retrieves the list of DevOps groups for the organization "myOrganization". +#> +Function List-DevOpsGroups +{ [CmdletBinding()] [OutputType([System.Object])] Param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $Organization, [Parameter()] @@ -16,18 +42,15 @@ Function List-DevOpsGroups { Method = 'Get' } - # # Invoke the Rest API to get the groups $groups = Invoke-AzDevOpsApiRestMethod @params - if ($null -eq $groups.value) { + # Return the groups from the cache + if ($null -eq $groups.value) + { return $null } - # - # Perform a lookup to get the group - - # # Return the groups from the cache return $groups.Value diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.ps1 index 9c812057d..0d0f0352f 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.ps1 @@ -1,9 +1,39 @@ -Function List-DevOpsProcess { +<# +.SYNOPSIS + Retrieves the list of DevOps processes for a specified organization. + +.DESCRIPTION + This function invokes the Azure DevOps REST API to retrieve the list of DevOps processes for a given organization. + It uses the specified API version or defaults to the version returned by Get-AzDevOpsApiVersion. + +.PARAMETER Organization + The name of the Azure DevOps organization for which to retrieve the processes. + This parameter is mandatory. + +.PARAMETER ApiVersion + The version of the API to use. If not specified, the default version is used as returned by Get-AzDevOpsApiVersion. + +.OUTPUTS + System.Object + Returns the list of DevOps processes for the specified organization. + +.EXAMPLE + List-DevOpsProcess -Organization "myOrganization" + + This example retrieves the list of DevOps processes for the organization "myOrganization" using the default API version. + +.EXAMPLE + List-DevOpsProcess -Organization "myOrganization" -ApiVersion "6.0" + + This example retrieves the list of DevOps processes for the organization "myOrganization" using API version "6.0". +#> +Function List-DevOpsProcess +{ [CmdletBinding()] [OutputType([System.Object])] Param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $Organization, [Parameter()] @@ -12,15 +42,15 @@ Function List-DevOpsProcess { ) $params = @{ - Uri = "https://dev.azure.com/{0}/_apis/process/processes?api-version={1}" -f $Organization, $ApiVersion + Uri = 'https://dev.azure.com/{0}/_apis/process/processes?api-version={1}' -f $Organization, $ApiVersion Method = 'Get' } - # # Invoke the Rest API to get the groups $groups = Invoke-AzDevOpsApiRestMethod @params - - if ($null -eq $groups.value) { + # Return the groups from the cache + if ($null -eq $groups.value) + { return $null } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.ps1 index 325f17873..31f426a1b 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.ps1 @@ -1,9 +1,36 @@ +<# +.SYNOPSIS + Retrieves a list of DevOps projects for a specified organization. + +.DESCRIPTION + This function invokes the Azure DevOps REST API to retrieve a list of projects + for a given organization. It uses the specified API version or defaults to the + version obtained from Get-AzDevOpsApiVersion. + +.PARAMETER OrganizationName + The name of the Azure DevOps organization for which to list projects. + This parameter is mandatory. + +.PARAMETER ApiVersion + The version of the Azure DevOps API to use. If not specified, the default + version is obtained from Get-AzDevOpsApiVersion. + +.RETURNS + An array of project objects if projects are found, otherwise $null. + +.EXAMPLE + $projects = List-DevOpsProjects -OrganizationName "myOrganization" + This example retrieves the list of projects for the organization "myOrganization". + +.NOTES + This function requires the Az.DevOps module to be installed and imported. +#> function List-DevOpsProjects { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$OrganizationName, [Parameter()] @@ -16,7 +43,6 @@ function List-DevOpsProjects Method = 'Get' } - # # Invoke the Rest API to get the groups $groups = Invoke-AzDevOpsApiRestMethod @params @@ -25,7 +51,6 @@ function List-DevOpsProjects return $null } - # # Return the groups from the cache return $groups.Value diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.ps1 index 798971086..8dfe2ee61 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.ps1 @@ -1,7 +1,27 @@ -Function List-DevOpsSecurityNamespaces { +<# +.SYNOPSIS + Retrieves the security namespaces for a specified Azure DevOps organization. + +.DESCRIPTION + The List-DevOpsSecurityNamespaces function invokes the Azure DevOps REST API to retrieve the security namespaces for a given organization. + It returns the namespaces if available, otherwise returns null. + +.PARAMETER OrganizationName + The name of the Azure DevOps organization for which to retrieve the security namespaces. + +.EXAMPLE + List-DevOpsSecurityNamespaces -OrganizationName "Contoso" + + This example retrieves the security namespaces for the "Contoso" Azure DevOps organization. + +.NOTES + This function uses the Invoke-AzDevOpsApiRestMethod cmdlet to make the REST API call. +#> +Function List-DevOpsSecurityNamespaces +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String]$OrganizationName ) @@ -14,7 +34,6 @@ Function List-DevOpsSecurityNamespaces { Method = 'Get' } - # # Invoke the Rest API to get the groups $namespaces = Invoke-AzDevOpsApiRestMethod @params @@ -23,7 +42,6 @@ Function List-DevOpsSecurityNamespaces { return $null } - # # Return the groups from the cache return $namespaces.Value diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.ps1 index d6a437e16..bc54343ea 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.ps1 @@ -1,7 +1,35 @@ -Function List-DevOpsServicePrinciples { +<# +.SYNOPSIS +Retrieves a list of service principals from an Azure DevOps organization. + +.DESCRIPTION +The List-DevOpsServicePrinciples function calls the Azure DevOps REST API to retrieve a list of service principals for a specified organization. The function requires the organization name and optionally accepts an API version. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization from which to retrieve the service principals. This parameter is mandatory. + +.PARAMETER ApiVersion +The version of the Azure DevOps API to use. If not specified, the default API version is used. + +.EXAMPLE +PS> List-DevOpsServicePrinciples -OrganizationName "myOrganization" + +This example retrieves the list of service principals for the organization named "myOrganization". + +.EXAMPLE +PS> List-DevOpsServicePrinciples -OrganizationName "myOrganization" -ApiVersion "6.0" + +This example retrieves the list of service principals for the organization named "myOrganization" using API version "6.0". + +.RETURNS +The function returns a list of service principals if available; otherwise, it returns $null. + +#> +Function List-DevOpsServicePrinciples +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$OrganizationName, [Parameter()] @@ -23,9 +51,7 @@ Function List-DevOpsServicePrinciples { return $null } - # # Return the groups from the cache return $serviceprincipals.Value - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.ps1 index cd1f9ecdb..f1b761b75 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.ps1 @@ -1,8 +1,38 @@ +<# +.SYNOPSIS +Retrieves the list of users from the Azure DevOps organization. + +.DESCRIPTION +The List-UserCache function invokes the Azure DevOps REST API to retrieve the list of users +for a specified organization. It uses the provided organization name and an optional API version +to make the request. If no API version is specified, it defaults to the version returned by +the Get-AzDevOpsApiVersion function. + +.PARAMETER OrganizationName +Specifies the name of the Azure DevOps organization from which to retrieve the list of users. +This parameter is mandatory. + +.PARAMETER ApiVersion +Specifies the version of the Azure DevOps API to use. If not provided, the default version +returned by the Get-AzDevOpsApiVersion function is used. + +.RETURNS +Returns the list of users from the specified Azure DevOps organization. If no users are found, +returns $null. + +.EXAMPLE +PS> List-UserCache -OrganizationName "myOrganization" +Retrieves the list of users from the "myOrganization" Azure DevOps organization using the default API version. + +.EXAMPLE +PS> List-UserCache -OrganizationName "myOrganization" -ApiVersion "6.0" +Retrieves the list of users from the "myOrganization" Azure DevOps organization using API version 6.0. +#> function List-UserCache { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$OrganizationName, [Parameter()] @@ -15,15 +45,14 @@ function List-UserCache Method = 'Get' } - # # Invoke the Rest API to get the groups $users = Invoke-AzDevOpsApiRestMethod @params - if ($null -eq $users.value) { + if ($null -eq $users.value) + { return $null } - # # Return the groups from the cache return $users.Value diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.ps1 index a1fa38139..24b721bc3 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.ps1 @@ -1,18 +1,53 @@ -Function New-GitRepository { +<# +.SYNOPSIS +Creates a new Git repository in an Azure DevOps project. + +.DESCRIPTION +The `New-GitRepository` function creates a new Git repository within a specified Azure DevOps project. +It uses the Azure DevOps REST API to perform the operation. + +.PARAMETER ApiUri +The base URI of the Azure DevOps API. + +.PARAMETER Project +The project object containing the project details. This should include at least the project name and ID. + +.PARAMETER RepositoryName +The name of the new Git repository to be created. + +.PARAMETER SourceRepository +(Optional) The source repository to use for the new repository. + +.PARAMETER ApiVersion +(Optional) The API version to use for the Azure DevOps REST API. Defaults to the version returned by `Get-AzDevOpsApiVersion -Default`. + +.OUTPUTS +System.Management.Automation.PSObject[] +Returns the created repository object if successful. + +.EXAMPLE +PS> New-GitRepository -ApiUri "https://dev.azure.com/organization" -Project $project -RepositoryName "NewRepo" + +This example creates a new Git repository named "NewRepo" in the specified Azure DevOps project. + +.NOTES +This function requires the `Invoke-AzDevOpsApiRestMethod` function to be defined and available in the session. +#> +Function New-GitRepository +{ [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( - - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('URI')] [System.String]$ApiUri, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [Object]$Project, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Repository')] [System.String]$RepositoryName, @@ -23,14 +58,13 @@ Function New-GitRepository { [Parameter()] [String] $ApiVersion = $(Get-AzDevOpsApiVersion -Default) - ) Write-Verbose "[New-GitRepository] Creating new repository '$($RepositoryName)' in project '$($Project.name)'" # Define parameters for creating a new DevOps group $params = @{ - ApiUri = "{0}/{1}/_apis/git/repositories?api-version={2}" -f $ApiUri, $Project.name, $ApiVersion + ApiUri = '{0}/{1}/_apis/git/repositories?api-version={2}' -f $ApiUri, $Project.name, $ApiVersion Method = 'POST' ContentType = 'application/json' Body = @{ @@ -42,15 +76,16 @@ Function New-GitRepository { } # Try to invoke the REST method to create the group and return the result - try { + try + { $repo = Invoke-AzDevOpsApiRestMethod @params Write-Verbose "[New-GitRepository] Repository Created: '$($repo.name)'" return $repo } # Catch any exceptions and write an error message - catch { + catch + { Write-Error "[New-GitRepository] Failed to Create Repository: $_" } - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.ps1 index 42ce64043..6e07225d3 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.ps1 @@ -1,44 +1,69 @@ -Function Remove-GitRepository { +<# +.SYNOPSIS +Removes a Git repository from an Azure DevOps project. + +.DESCRIPTION +The Remove-GitRepository function removes a specified Git repository from a given Azure DevOps project using the provided API URI and version. + +.PARAMETER ApiUri +The base URI of the Azure DevOps API. + +.PARAMETER Project +The project from which the repository will be removed. This should be an object containing the project details. + +.PARAMETER Repository +The repository to be removed. This should be an object containing the repository details. + +.PARAMETER ApiVersion +The version of the Azure DevOps API to use. If not specified, the default version will be used. + +.EXAMPLE +Remove-GitRepository -ApiUri "https://dev.azure.com/organization" -Project $project -Repository $repository + +.NOTES +This function uses the Invoke-AzDevOpsApiRestMethod cmdlet to perform the REST API call to remove the repository. +#> +Function Remove-GitRepository +{ [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( - - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('URI')] [System.String]$ApiUri, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [Object]$Project, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Repo')] [Object]$Repository, [Parameter()] [String] $ApiVersion = $(Get-AzDevOpsApiVersion -Default) - ) Write-Verbose "[Remove-GitRepository] Removing repository '$($Repository.Name)' in project '$($Project.name)'" # Define parameters for creating a new DevOps group $params = @{ - ApiUri = "{0}/{1}/_apis/git/repositories/{2}?api-version={3}" -f $ApiUri, $Project.name, $Repository.id , $ApiVersion + ApiUri = '{0}/{1}/_apis/git/repositories/{2}?api-version={3}' -f $ApiUri, $Project.name, $Repository.id , $ApiVersion Method = 'Delete' } # Try to invoke the REST method to create the group and return the result - try { + try + { $null = Invoke-AzDevOpsApiRestMethod @params return } # Catch any exceptions and write an error message - catch { + catch + { Write-Error "[Remove-GitRepository] Failed to Create Repository: $_" } - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.ps1 index e7b173f55..18152f0a7 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.ps1 @@ -32,21 +32,19 @@ Creates a new group named "MyGroup" in Azure DevOps within the specified project #> # Define a function to create a new Azure DevOps Group -Function New-DevOpsGroup { - # CmdletBinding attribute specifies that this function is written as an advanced function +Function New-DevOpsGroup +{ [CmdletBinding()] - # OutputType attribute defines the output type of the function which is PSObject in this case [OutputType([System.Management.Automation.PSObject])] - # Parameters block defining the parameters accepted by the function param ( # Parameter attribute marks this as a mandatory parameter that the user must supply when calling the function. - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $ApiUri, # The URI for the Azure DevOps API. # Mandatory parameter for the group name - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $GroupName, @@ -68,7 +66,7 @@ Function New-DevOpsGroup { # Hashtable to hold parameters for the API request $params = @{ - Uri = "{0}/_apis/graph/groups?api-version={1}" -f $ApiUri, $ApiVersion + Uri = '{0}/_apis/graph/groups?api-version={1}' -f $ApiUri, $ApiVersion Method = 'Post' ContentType = 'application/json' Body = @{ @@ -78,17 +76,20 @@ Function New-DevOpsGroup { } # If ProjectScopeDescriptor is provided, modify the URI to include it - if ($ProjectScopeDescriptor) { - $params.Uri = "{0}/_apis/graph/groups?scopeDescriptor={1}&api-version={2}" -f $ApiUri, $ProjectScopeDescriptor, $ApiVersion + if ($ProjectScopeDescriptor) + { + $params.Uri = '{0}/_apis/graph/groups?scopeDescriptor={1}&api-version={2}' -f $ApiUri, $ProjectScopeDescriptor, $ApiVersion } # Try to invoke the REST method to create the group and return the result - try { + try + { $group = Invoke-AzDevOpsApiRestMethod @params return $group } # Catch any exceptions and write an error message - catch { + catch + { Write-Error "Failed to create group: $_" } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.ps1 index beb2dcef8..69e21d27f 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.ps1 @@ -23,13 +23,13 @@ Remove-DevOpsGroup -ApiUri "https://dev.azure.com/myorganization" -GroupDescript This example removes the group with the specified group descriptor from Azure DevOps. #> - -Function Remove-DevOpsGroup { +Function Remove-DevOpsGroup +{ [CmdletBinding()] [OutputType([System.Management.Automation.PSObject])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $ApiUri, @@ -43,15 +43,17 @@ Function Remove-DevOpsGroup { ) $params = @{ - Uri = "{0}/_apis/graph/groups/{1}?api-version={2}" -f $ApiUri, $GroupDescriptor, $ApiVersion + Uri = '{0}/_apis/graph/groups/{1}?api-version={2}' -f $ApiUri, $GroupDescriptor, $ApiVersion Method = 'Delete' ContentType = 'application/json' } - try { + try + { return (Invoke-AzDevOpsApiRestMethod @params) } - catch { + catch + { Write-Error "Failed to remove group: $_" } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.ps1 index 433eddcc6..0cbf79626 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.ps1 @@ -31,21 +31,20 @@ This example updates the group named "MyGroup" in the Azure DevOps organization #> # This function is designed to update the description of a group in Azure DevOps. -Function Set-DevOpsGroup { - # CmdletBinding attribute allows the function to use cmdlet parameters and supports advanced functionality like ShouldProcess. +Function Set-DevOpsGroup +{ [CmdletBinding(DefaultParameterSetName = 'Default')] - # OutputType attribute specifies the type of object that the function returns. [OutputType([System.Management.Automation.PSObject])] param ( # Parameter attribute marks this as a mandatory parameter that the user must supply when calling the function. - [Parameter(Mandatory, ParameterSetName = 'ProjectScope')] - [Parameter(Mandatory, ParameterSetName = 'Default')] + [Parameter(Mandatory = $true, ParameterSetName = 'ProjectScope')] + [Parameter(Mandatory = $true, ParameterSetName = 'Default')] [string] $ApiUri, # The URI for the Azure DevOps API. - [Parameter(Mandatory, ParameterSetName = 'ProjectScope')] - [Parameter(Mandatory, ParameterSetName = 'Default')] + [Parameter(Mandatory = $true, ParameterSetName = 'ProjectScope')] + [Parameter(Mandatory = $true, ParameterSetName = 'Default')] [string] $GroupName, # The name of the group to be updated. @@ -62,19 +61,19 @@ Function Set-DevOpsGroup { $ApiVersion = $(Get-AzDevOpsApiVersion -Default), # The API version to use for the request. # Group Descriptor for the project within which the group exists. - [Parameter(Mandatory, ParameterSetName = 'Default')] + [Parameter(Mandatory = $true, ParameterSetName = 'Default')] [String] $GroupDescriptor, # Optional parameter without a default value. - [Parameter(Mandatory, ParameterSetName = 'ProjectScope')] + [Parameter(Mandatory = $true, ParameterSetName = 'ProjectScope')] [String] $ProjectScopeDescriptor # Scope descriptor for the project within which the group exists. ) # A hashtable is created to hold parameters that will be used in the REST method invocation. $params = @{ - Uri = "{0}/_apis/graph/groups/{1}?api-version={2}" -f $ApiUri, $GroupDescriptor, $ApiVersion # The API endpoint, formatted with the base URI and API version. + Uri = '{0}/_apis/graph/groups/{1}?api-version={2}' -f $ApiUri, $GroupDescriptor, $ApiVersion # The API endpoint, formatted with the base URI and API version. Method = 'Patch' # The HTTP method used for the request, indicating an update operation. ContentType = 'application/json-patch+json' # The content type of the request body. Body = @( @@ -92,16 +91,19 @@ Function Set-DevOpsGroup { } # If ProjectScopeDescriptor is provided, modify the URI to include it in the query parameters. - if ($ProjectScopeDescriptor) { - $params.Uri = "{0}/_apis/graph/groups?scopeDescriptor={1}&api-version={2}" -f $ApiUri, $ProjectScopeDescriptor, $ApiVersion + if ($ProjectScopeDescriptor) + { + $params.Uri = '{0}/_apis/graph/groups?scopeDescriptor={1}&api-version={2}' -f $ApiUri, $ProjectScopeDescriptor, $ApiVersion } - try { + try + { # Invoke the REST method with the parameters and store the result in $group. $group = Invoke-AzDevOpsApiRestMethod @params return $group # Return the result of the REST method call. } - catch { + catch + { # Write an error message to the console if the REST method call fails. Write-Error "Failed to create group: $_" } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.ps1 index f784266a2..061bec45d 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.ps1 @@ -1,14 +1,46 @@ -Function New-DevOpsGroupMember { +<# +.SYNOPSIS + Adds a member to an Azure DevOps group using the Azure DevOps REST API. + +.DESCRIPTION + The New-DevOpsGroupMember function adds a specified member to a specified Azure DevOps group by invoking the Azure DevOps REST API. + It constructs the appropriate URI for the API call and uses the 'PUT' method to add the member to the group. + +.PARAMETER GroupIdentity + The identity of the group to which the member will be added. This parameter is mandatory. + +.PARAMETER MemberIdentity + The identity of the member to be added to the group. This parameter is mandatory. + +.PARAMETER ApiVersion + The version of the Azure DevOps API to use. If not specified, the default value is obtained from the Get-AzDevOpsApiVersion function. + +.PARAMETER ApiUri + The URI for the Azure DevOps API. This parameter is mandatory. + +.EXAMPLE + $group = Get-DevOpsGroup -Name "Developers" + $member = Get-DevOpsUser -UserName "jdoe" + New-DevOpsGroupMember -GroupIdentity $group -MemberIdentity $member -ApiUri "https://dev.azure.com/yourorganization" + + This example adds the user "jdoe" to the "Developers" group in the specified Azure DevOps organization. + +.NOTES + The function uses the Invoke-AzDevOpsApiRestMethod function to perform the REST API call. + It handles exceptions by writing an error message to the console if the API call fails. +#> +Function New-DevOpsGroupMember +{ [CmdletBinding()] param ( # The group Identity - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Group')] [Object]$GroupIdentity, # The group member - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Member')] [Object]$MemberIdentity, @@ -18,17 +50,16 @@ Function New-DevOpsGroupMember { $ApiVersion = $(Get-AzDevOpsApiVersion -Default), # The URI for the Azure DevOps API. - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $ApiUri - ) # Define a hashtable to store parameters for the Invoke-AzDevOpsApiRestMethod function. $params = @{ # Construct the Uri using string formatting with the -f operator. # It includes the API endpoint, group identity, member identity, and the API version. - Uri = "{0}/_apis/graph/memberships/{1}/{2}?api-version={3}" -f $ApiUri, + Uri = '{0}/_apis/graph/memberships/{1}/{2}?api-version={3}' -f $ApiUri, $MemberIdentity.descriptor, $GroupIdentity.descriptor, $ApiVersion @@ -40,14 +71,16 @@ Function New-DevOpsGroupMember { # Try to invoke the REST method to create the group and return the result - try { + try + { # Call the Invoke-AzDevOpsApiRestMethod function with the parameters defined above. # The "@" symbol is used to pass the hashtable as splatting parameters. Write-Verbose "[Add-DevOpsGroupMember] Attempting to invoke REST method to add group member." $member = Invoke-AzDevOpsApiRestMethod @params Write-Verbose "[Add-DevOpsGroupMember] Member added successfully." - - } catch { + } + catch + { # If an exception occurs, write an error message to the console with details about the issue. Write-Error "[Add-DevOpsGroupMember] Failed to add member to group: $($_.Exception.Message)" } @@ -58,5 +91,4 @@ Function New-DevOpsGroupMember { Write-Verbose "[Add-DevOpsGroupMember] Returning result from REST method invocation." return $member - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.ps1 index 2d1438a22..52d91fa15 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.ps1 @@ -1,14 +1,42 @@ -Function Remove-DevOpsGroupMember { +<# +.SYNOPSIS +Removes a member from an Azure DevOps group. + +.DESCRIPTION +The Remove-DevOpsGroupMember function removes a specified member from a specified Azure DevOps group using the Azure DevOps REST API. + +.PARAMETER GroupIdentity +The identity of the group from which the member will be removed. This parameter is mandatory. + +.PARAMETER MemberIdentity +The identity of the member to be removed from the group. This parameter is mandatory. + +.PARAMETER ApiVersion +The version of the Azure DevOps API to use. If not specified, the default version is obtained from the Get-AzDevOpsApiVersion function. + +.PARAMETER ApiUri +The base URI for the Azure DevOps API. This parameter is mandatory. + +.EXAMPLE +Remove-DevOpsGroupMember -GroupIdentity $group -MemberIdentity $member -ApiUri "https://dev.azure.com/organization" + +This example removes the specified member from the specified group in the Azure DevOps organization. + +.NOTES +This function constructs the appropriate URI for the Azure DevOps REST API call and uses the Invoke-AzDevOpsApiRestMethod function to perform the removal operation. If the operation fails, an error message is written to the console. +#> +Function Remove-DevOpsGroupMember +{ [CmdletBinding()] param ( # The group Identity - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Group')] [Object]$GroupIdentity, # The group member - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Member')] [Object]$MemberIdentity, @@ -18,17 +46,16 @@ Function Remove-DevOpsGroupMember { $ApiVersion = $(Get-AzDevOpsApiVersion -Default), # The URI for the Azure DevOps API. - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $ApiUri - ) # Define a hashtable to store parameters for the Invoke-AzDevOpsApiRestMethod function. $params = @{ # Construct the Uri using string formatting with the -f operator. # It includes the API endpoint, group identity, member identity, and the API version. - Uri = "{0}/_apis/graph/memberships/{1}/{2}?api-version={3}" -f $ApiUri, + Uri = '{0}/_apis/graph/memberships/{1}/{2}?api-version={3}' -f $ApiUri, $MemberIdentity.descriptor, $GroupIdentity.descriptor, $ApiVersion @@ -40,23 +67,19 @@ Function Remove-DevOpsGroupMember { # Try to invoke the REST method to create the group and return the result - try { + try + { # Call the Invoke-AzDevOpsApiRestMethod function with the parameters defined above. # The "@" symbol is used to pass the hashtable as splatting parameters. Write-Verbose "[Remove-DevOpsGroupMember] Attempting to invoke REST method to remove group member." $member = Invoke-AzDevOpsApiRestMethod @params Write-Verbose "[Remove-DevOpsGroupMember] Member removed successfully." - } catch { + } + catch + { # If an exception occurs, write an error message to the console with details about the issue. Write-Error "[Remove-DevOpsGroupMember] Failed to add member to group: $($_.Exception.Message)" } - #Write-Verbose "[Remove-DevOpsGroupMember] Result $($member | ConvertTo-Json)." - - # Return the result of the REST method invocation, which is stored in $member. - #Write-Verbose "[Remove-DevOpsGroupMember] Returning result from REST method invocation." - #return $member - - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.ps1 index c625eb183..63c0e52c4 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.ps1 @@ -58,12 +58,11 @@ function New-DevOpsProject [Parameter()] [String] $ApiVersion = $(Get-AzDevOpsApiVersion | Select-Object -Last 1) - ) # Validate the parameters $params = @{ - Uri = "https://dev.azure.com/{0}/_apis/projects?api-version={1}" -f $Organization, $ApiVersion + Uri = 'https://dev.azure.com/{0}/_apis/projects?api-version={1}' -f $Organization, $ApiVersion Method = "POST" Body = @{ name = $ProjectName @@ -88,15 +87,16 @@ function New-DevOpsProject # Invoke the Azure DevOps REST API to create the project $response = Invoke-AzDevOpsApiRestMethod @params - if ($null -eq $response) { + if ($null -eq $response) + { Throw "[New-DevOpsProject] Failed to create the Azure DevOps project: No response returned" } # Output the response which contains the created project details return $response - } catch + } + catch { Write-Error "[New-DevOpsProject] Failed to create the Azure DevOps project: $_" } - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.ps1 index 26baffdef..e62d166ca 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.ps1 @@ -40,7 +40,7 @@ function Remove-DevOpsProject # Define the API version to use $params = @{ - Uri = "https://dev.azure.com/{0}/_apis/projects/{1}?api-version={2}" -f $Organization, $ProjectId, $ApiVersion + Uri = 'https://dev.azure.com/{0}/_apis/projects/{1}?api-version={2}' -f $Organization, $ProjectId, $ApiVersion Method = "DELETE" } @@ -52,7 +52,8 @@ function Remove-DevOpsProject $response = Invoke-AzDevOpsApiRestMethod @params # Output the response which contains the created project details return $response - } catch + } + catch { Write-Error "[Remove-DevOpsProject] Failed to create the Azure DevOps project: $_" } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 index 6648ec2c8..e86d666ad 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 @@ -82,7 +82,7 @@ function Update-DevOpsProject # Construct the Paramters for the Invoke-AzDevOpsApiRestMethod function $params = @{ - Uri = "https://dev.azure.com/{0}/_apis/projects/{1}?api-version={2}" -f $Organization, $ProjectId, $ApiVersion + Uri = 'https://dev.azure.com/{0}/_apis/projects/{1}?api-version={2}' -f $Organization, $ProjectId, $ApiVersion Body = $body | ConvertTo-Json Method = 'PATCH' } @@ -91,7 +91,8 @@ function Update-DevOpsProject try { $response = Invoke-AzDevOpsApiRestMethod @params - } catch + } + catch { Write-Error "Failed to update the Azure DevOps project: $_" } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 index be809a163..2cb15cce0 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 @@ -22,7 +22,8 @@ Date: Current Date #> -Function Wait-DevOpsProject { +Function Wait-DevOpsProject +{ [CmdletBinding()] param( [Parameter(Mandatory = $true)] @@ -37,7 +38,7 @@ Function Wait-DevOpsProject { ) $params = @{ - Uri = "{0}" -f $ProjectURL + Uri = '{0}' -f $ProjectURL Method = "GET" } @@ -45,13 +46,15 @@ Function Wait-DevOpsProject { # Loop until the project is created $counter = 0 - do { + do + { Write-Verbose "[Wait-DevOpsProject] Sending request to check project status..." $response = Invoke-AzDevOpsApiRestMethod @params $project = $response # Check the status of the project - switch ($response.status) { + switch ($response.status) + { 'creating' { Write-Verbose "[Wait-DevOpsProject] Project is still being created..." Start-Sleep -Seconds 5 @@ -80,7 +83,9 @@ Function Wait-DevOpsProject { } while ($counter -lt 10) - if ($counter -ge 10) { + if ($counter -ge 10) + { Write-Error "[Wait-DevOpsProject] Timed out waiting for project to be created." } + } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.ps1 index a10a1cf1e..cb4e53d65 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.ps1 @@ -1,3 +1,35 @@ +<# +.SYNOPSIS +Retrieves the status of a specified project service in Azure DevOps. + +.DESCRIPTION +The Get-ProjectServiceStatus function retrieves the status of a specified service within a project in Azure DevOps. +It constructs the appropriate URI and makes a REST API call to fetch the service status. If the service status is +'undefined', it is treated as 'enabled'. + +.PARAMETER Organization +The name of the Azure DevOps organization. + +.PARAMETER ProjectId +The ID of the project in Azure DevOps. + +.PARAMETER ServiceName +The name of the service whose status is to be retrieved. + +.PARAMETER ApiVersion +The API version to use for the request. If not specified, the default API version is used. + +.OUTPUTS +System.Object +Returns the state of the specified service. + +.EXAMPLE +PS> Get-ProjectServiceStatus -Organization "MyOrg" -ProjectId "12345" -ServiceName "MyService" +This command retrieves the status of the service "MyService" in the project with ID "12345" within the organization "MyOrg". + +.NOTES +If the service status is 'undefined', it is treated as 'enabled'. +#> function Get-ProjectServiceStatus { [CmdletBinding()] diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.ps1 index f47b6a32e..691fdb430 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.ps1 @@ -1,3 +1,31 @@ +<# +.SYNOPSIS +Sets the status of a specified project service in Azure DevOps. + +.DESCRIPTION +The Set-ProjectServiceStatus function updates the status of a specified service within a given project in an Azure DevOps organization. It constructs the appropriate URI and sends a PATCH request with the provided body content. + +.PARAMETER Organization +The name of the Azure DevOps organization. + +.PARAMETER ProjectId +The ID of the project within the Azure DevOps organization. + +.PARAMETER ServiceName +The name of the service whose status is to be set. + +.PARAMETER Body +The body content to be sent in the PATCH request. This should be an object that will be converted to JSON. + +.PARAMETER ApiVersion +The API version to use for the request. If not specified, the default API version is retrieved using Get-AzDevOpsApiVersion. + +.EXAMPLE +Set-ProjectServiceStatus -Organization "myOrg" -ProjectId "12345" -ServiceName "myService" -Body $bodyContent + +.NOTES +This function requires the Azure DevOps REST API and the Invoke-AzDevOpsApiRestMethod cmdlet to be available. +#> function Set-ProjectServiceStatus { [CmdletBinding()] diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.ps1 index f6703b926..faea69efa 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.ps1 @@ -25,10 +25,10 @@ function Get-DevOpsSecurityDescriptor { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $ProjectId, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $Organization, [Parameter()] @@ -39,7 +39,7 @@ function Get-DevOpsSecurityDescriptor # Get the project # Construct the URI with optional state filter $params = @{ - Uri = "https://vssps.dev.azure.com/{0}/_apis/graph/descriptors/{1}?api-version={2}" -f $Organization, $ProjectId, $ApiVersion + Uri = 'https://vssps.dev.azure.com/{0}/_apis/graph/descriptors/{1}?api-version={2}' -f $Organization, $ProjectId, $ApiVersion Method = 'Get' } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.ps1 index 2fb3d997e..c740e94f3 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.ps1 @@ -1,29 +1,46 @@ -Function Add-AuthenticationHTTPHeader { +<# +.SYNOPSIS +Adds the appropriate authentication HTTP header based on the type of authentication token. - # Dertimine the type of token. +.DESCRIPTION +The Add-AuthenticationHTTPHeader function determines the type of authentication token and adds the corresponding HTTP header. +It supports Personal Access Tokens and Managed Identity Tokens. If the token is null or the token type is not supported, an error is thrown. - $headerValue = "" +.PARAMETER None +This function does not take any parameters. + +.OUTPUTS +String +Returns the authentication HTTP header as a string. + +.NOTES +The function relies on the global variables $Global:DSCAZDO_AuthenticationToken and $Global:DSCAZDO_OrganizationName. + +.EXAMPLE +$header = Add-AuthenticationHTTPHeader +# Adds the appropriate authentication HTTP header and returns it as a string. +#> - switch ($Global:DSCAZDO_AuthenticationToken.tokenType) { +Function Add-AuthenticationHTTPHeader +{ + # Dertimine the type of token. + $headerValue = "" + switch ($Global:DSCAZDO_AuthenticationToken.tokenType) + { # If the token is null {[String]::IsNullOrEmpty($_)} { throw "[Add-AuthenticationHTTPHeader] Error. The authentication token is null. Please ensure that the authentication token is set." } {$_ -eq 'PersonalAccessToken'} { - - # # Personal Access Token # Add the Personal Access Token to the header - $headerValue = "Authorization: Basic {0}" -f $Global:DSCAZDO_AuthenticationToken.Get() + $headerValue = 'Authorization: Basic {0}' -f $Global:DSCAZDO_AuthenticationToken.Get() break } {$_ -eq 'ManagedIdentity'} { - - # # Managed Identity Token - Write-Verbose "[Add-AuthenticationHTTPHeader] Adding Managed Identity Token to the HTTP Headers." # Test if the Managed Identity Token has expired diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 index f9fd7ae71..1cd5f169a 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 @@ -19,11 +19,12 @@ Obtains the access token for the managed identity associated with the organizati This function does not require the Azure PowerShell module. #> -Function Get-AzManagedIdentityToken { +Function Get-AzManagedIdentityToken +{ [CmdletBinding()] param ( # Organization Name - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String] $OrganizationName, @@ -46,18 +47,36 @@ Function Get-AzManagedIdentityToken { } # Dertimine if the machine is an arc machine - if ($env:IDENTITY_ENDPOINT) { + if ($env:IDENTITY_ENDPOINT) + { + + # Validate what type of machine it is. + if ($null -eq $IsCoreCLR) + { + # If the $IsCoreCLR variable is not set, the script is running on Windows PowerShell. + Write-Verbose "[Get-AzManagedIdentityToken] The machine is a Windows machine Running Windows PowerShell." + $IsWindows = $true + } # Test if console is being run as Administrator - if (-not([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + if ( + ($IsWindows) -and + (-not ( + [Security.Principal.WindowsPrincipal] + [Security.Principal.WindowsIdentity]::GetCurrent() + ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) + ) + { throw "[Get-AzManagedIdentityToken] Error: Authentication to Azure Arc requires Administrator privileges." } Write-Verbose "[Get-AzManagedIdentityToken] The machine is an Azure Arc machine. The Uri needs to be updated to $($env:IDENTITY_ENDPOINT):" - $ManagedIdentityParams.Uri = "{0}?api-version=2020-06-01&resource=499b84ac-1321-427f-aa17-267ca6975798" -f $env:IDENTITY_ENDPOINT + $ManagedIdentityParams.Uri = '{0}?api-version=2020-06-01&resource=499b84ac-1321-427f-aa17-267ca6975798' -f $env:IDENTITY_ENDPOINT $ManagedIdentityParams.AzureArcAuthentication = $true - } else { + } + else + { Write-Verbose "[Get-AzManagedIdentityToken] The machine is not an Azure Arc machine. No changes are required." } @@ -65,15 +84,17 @@ Function Get-AzManagedIdentityToken { Write-Verbose "[Get-AzManagedIdentityToken] Invoking the Azure Instance Metadata Service to get the access token." # Invoke the RestAPI - try { + try + { $response = Invoke-AzDevOpsApiRestMethod @ManagedIdentityParams - } catch { - + } + catch + { # If there is an error it could be because it's an arc machine, and we need to use the secret file: $wwwAuthHeader = $_.Exception.Response.Headers.WwwAuthenticate if ($wwwAuthHeader -notmatch "Basic realm=.+") { - Throw "[Get-AzManagedIdentityToken] {0}" -f $_ + Throw ('[Get-AzManagedIdentityToken] {0}' -f $_) } Write-Verbose "[Get-AzManagedIdentityToken] Managed Identity Token Retrival Failed. Retrying with secret file." @@ -87,10 +108,13 @@ Function Get-AzManagedIdentityToken { # Retry the request. Silently continue to suppress the error message, since we will handle it below. $response = Invoke-AzDevOpsApiRestMethod @ManagedIdentityParams -ErrorAction SilentlyContinue - } + # Test the response - if ($null -eq $response.access_token) { throw "Error. Access token not returned from Azure Instance Metadata Service. Please ensure that the Azure Instance Metadata Service is available." } + if ($null -eq $response.access_token) + { + throw "Error. Access token not returned from Azure Instance Metadata Service. Please ensure that the Azure Instance Metadata Service is available." + } Write-Verbose "[Get-AzManagedIdentityToken] Managed Identity Token Retrival Successful." @@ -100,12 +124,18 @@ Function Get-AzManagedIdentityToken { $null = $response # Return the token if the verify switch is not set - if (-not($verify)) { return $ManagedIdentity } + if (-not($verify)) + { + return $ManagedIdentity + } Write-Verbose "[Get-AzManagedIdentityToken] Verifying the connection to the Azure DevOps API." # Test the Connection - if (-not(Test-AzToken $ManagedIdentity)) { throw "Error. Failed to call the Azure DevOps API." } + if (-not(Test-AzToken $ManagedIdentity)) + { + throw "Error. Failed to call the Azure DevOps API." + } Write-Verbose "[Get-AzManagedIdentityToken] Connection Verified." diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 index fe74db190..12432a42c 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 @@ -15,10 +15,11 @@ This example updates the Azure Managed Identity for the organization named "Cont #> -Function Update-AzManagedIdentity { - +Function Update-AzManagedIdentity +{ # Test if the Global Var's Exist $Global:DSCAZDO_OrganizationName - if ($null -eq $Global:DSCAZDO_OrganizationName) { + if ($null -eq $Global:DSCAZDO_OrganizationName) + { Throw "[Update-AzManagedIdentity] Organization Name is not set. Please run 'New-AzManagedIdentity -OrganizationName '" } @@ -27,5 +28,4 @@ Function Update-AzManagedIdentity { # Refresh the Token. $Global:DSCAZDO_AuthenticationToken = Get-AzManagedIdentityToken -OrganizationName $Global:DSCAZDO_OrganizationName - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.ps1 index 342b82081..ae0c42902 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.ps1 @@ -1,21 +1,60 @@ -Function Set-AzPersonalAccessToken { +<# +.SYNOPSIS +Sets the Personal Access Token (PAT) for an Azure DevOps organization. + +.DESCRIPTION +The Set-AzPersonalAccessToken function sets the Personal Access Token (PAT) for an Azure DevOps organization. +It supports both plain text and secure string PATs. Optionally, it can verify the connection to the Azure DevOps API. + +.PARAMETER OrganizationName +Specifies the name of the Azure DevOps organization. + +.PARAMETER PersonalAccessToken +Specifies the Personal Access Token (PAT) in plain text. + +.PARAMETER SecureStringPersonalAccessToken +Specifies the Personal Access Token (PAT) as a secure string. + +.PARAMETER Verify +Indicates that the connection to the Azure DevOps API should be verified. + +.EXAMPLE +Set-AzPersonalAccessToken -OrganizationName "MyOrg" -PersonalAccessToken "myPAT" + +Sets the PAT for the organization "MyOrg" using the provided plain text PAT. + +.EXAMPLE +Set-AzPersonalAccessToken -OrganizationName "MyOrg" -SecureStringPersonalAccessToken $securePAT + +Sets the PAT for the organization "MyOrg" using the provided secure string PAT. + +.EXAMPLE +Set-AzPersonalAccessToken -OrganizationName "MyOrg" -PersonalAccessToken "myPAT" -Verify + +Sets the PAT for the organization "MyOrg" and verifies the connection to the Azure DevOps API. + +.NOTES +This function requires the New-PersonalAccessToken and Test-AzToken functions to be defined. +#> +Function Set-AzPersonalAccessToken +{ [CmdletBinding(DefaultParameterSetName = 'PersonalAccessToken')] param ( # Organization Name - [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] - [Parameter(Mandatory, ParameterSetName = 'SecureStringPersonalAccessToken')] + [Parameter(Mandatory = $true, ParameterSetName = 'PersonalAccessToken')] + [Parameter(Mandatory = $true, ParameterSetName = 'SecureStringPersonalAccessToken')] [Alias('OrgName')] [String] $OrganizationName, # Personal Access Token - [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] + [Parameter(Mandatory = $true, ParameterSetName = 'PersonalAccessToken')] [Alias("PAT")] [String] $PersonalAccessToken, # Secure String Personal Access Token - [Parameter(Mandatory, ParameterSetName = 'SecureStringPersonalAccessToken')] + [Parameter(Mandatory = $true, ParameterSetName = 'SecureStringPersonalAccessToken')] [Alias("SecureStringPAT")] [SecureString] $SecureStringPersonalAccessToken, @@ -30,23 +69,34 @@ Function Set-AzPersonalAccessToken { Write-Verbose "[Set-PersonalAccessToken] Setting the Personal Access Token for the organization $OrganizationName." # If a SecureString Personal Access Token is provided, parse it and set as the Token - if ($SecureStringPersonalAccessToken) { + if ($SecureStringPersonalAccessToken) + { $Token = New-PersonalAccessToken -SecureStringPersonalAccessToken $SecureStringPersonalAccessToken - } elseif ($PersonalAccessToken) { + } + elseif ($PersonalAccessToken) + { # TypeCast the response to a PersonalAccessToken object $Token = New-PersonalAccessToken -PersonalAccessToken $PersonalAccessToken - } else { + } + else + { throw "Error. A Personal Access Token or SecureString Personal Access Token must be provided." } # # Return the token if the verify switch is not set - if (-not($verify)) { return $Token } + if (-not($verify)) + { + return $Token + } Write-Verbose "[Set-PersonalAccessToken] Verifying the connection to the Azure DevOps API." # Test the Connection - if (-not(Test-AzToken $Token)) { throw "Error. Failed to call the Azure DevOps API." } + if (-not(Test-AzToken $Token)) + { + throw "Error. Failed to call the Azure DevOps API." + } Write-Verbose "[Set-PersonalAccessToken] Connection Verified." diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.ps1 index 7743e6665..aa149ffe8 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.ps1 @@ -13,31 +13,35 @@ #> -Function Test-AzToken { +Function Test-AzToken +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Object] $Token ) # Define the Azure DevOps REST API endpoint to get the list of projects - $AZDOProjectUrl = "https://dev.azure.com/{0}/_apis/projects" -f $GLOBAL:DSCAZDO_OrganizationName - $FormattedUrl = "{0}?api-version=7.2-preview.4" -f $AZDOProjectUrl + $AZDOProjectUrl = 'https://dev.azure.com/{0}/_apis/projects' -f $GLOBAL:DSCAZDO_OrganizationName + $FormattedUrl = '{0}?api-version=7.2-preview.4' -f $AZDOProjectUrl $params = @{ Uri = $FormattedUrl Method = 'Get' Headers = @{ - Authorization ="Bearer {0}" -f $Token.Get() + Authorization = 'Bearer {0}' -f $Token.Get() } NoAuthentication = $true } # Call the Azure DevOps REST API with the Managed Identity Bearer token - try { + try + { $null = Invoke-AzDevOpsApiRestMethod @params - } catch { + } + catch + { return $false } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.ps1 index 01917c09e..dce8641aa 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.ps1 @@ -1,40 +1,41 @@ -Function Add-CacheItem { - <# - .SYNOPSIS - Add a cache item to the cache. +<# +.SYNOPSIS +Add a cache item to the cache. - .DESCRIPTION - Adds a cache item to the cache with a specified key, value, and type. +.DESCRIPTION +Adds a cache item to the cache with a specified key, value, and type. - .PARAMETER Key - The key of the cache item to add. +.PARAMETER Key +The key of the cache item to add. - .PARAMETER Value - The value of the cache item to add. +.PARAMETER Value +The value of the cache item to add. - .PARAMETER Type - The type of the cache item to add. Valid values are 'Project', 'Team', 'Group', 'SecurityDescriptor'. +.PARAMETER Type +The type of the cache item to add. Valid values are 'Project', 'Team', 'Group', 'SecurityDescriptor'. - .EXAMPLE - Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project' +.EXAMPLE +Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project' - .NOTES - This function is private and should not be used directly. - #> +.NOTES +This function is private and should not be used directly. +#> +Function Add-CacheItem +{ [CmdletBinding()] param ( # The key of the cache item to add - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $Key, # The value of the cache item to add - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [object] $Value, # The type of the cache item to add - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] [string] $Type, @@ -46,10 +47,10 @@ Function Add-CacheItem { Write-Verbose "[Add-CacheItem] Retrieving the current cache." [System.Collections.Generic.List[CacheItem]]$cache = Get-CacheObject -CacheType $Type - #Get-AzDevOpsCache -CacheType $Type # If the cache is empty, create a new cache - if ($cache.count -eq 0) { + if ($cache.count -eq 0) + { Write-Verbose "[Add-CacheItem] Cache is empty. Creating new cache." $cache = [System.Collections.Generic.List[CacheItem]]::New() } @@ -60,12 +61,15 @@ Function Add-CacheItem { Write-Verbose "[Add-CacheItem] Checking if the cache already contains the key: '$Key'." $existingItem = $cache | Where-Object { $_.Key -eq $Key } - if ($existingItem) { - + if ($existingItem) + { # If the cache already contains the key, remove the existing item - if ($SuppressWarning.IsPresent) { + if ($SuppressWarning.IsPresent) + { Write-Verbose "[Add-CacheItem] A cache item with the key '$Key' already exists. Flushing key from the cache." - } else { + } + else + { Write-Warning "[Add-CacheItem] A cache item with the key '$Key' already exists. Flushing key from the cache." } @@ -76,7 +80,8 @@ Function Add-CacheItem { [System.Collections.Generic.List[CacheItem]]$cache = Get-CacheObject -CacheType $Type # If the cache is empty, create a new cache - if ($cache.count -eq 0) { + if ($cache.count -eq 0) + { Write-Verbose "[Add-CacheItem] Cache is empty. Creating new cache." $cache = [System.Collections.Generic.List[CacheItem]]::New() } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 index 5ec4ef35e..eb2e787c1 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 @@ -23,16 +23,19 @@ Author: [Author Name] Date: [Date] #> -Function AzDoAPI_0_ProjectCache { +Function AzDoAPI_0_ProjectCache +{ [CmdletBinding()] param( + [Parameter(Mandatory = $false)] [string]$OrganizationName ) # Use a verbose statement to indicate the start of the function. Write-Verbose "Starting 'AzDoAPI_0_ProjectCache' function." - if (-not $OrganizationName) { + if (-not $OrganizationName) + { # If no organization name is provided, use a global variable as fallback Write-Verbose "No organization name provided as parameter; using global variable." $OrganizationName = $Global:DSCAZDO_OrganizationName @@ -43,7 +46,8 @@ Function AzDoAPI_0_ProjectCache { Organization = $OrganizationName } - try { + try + { # Inform about the API call being made with the parameters Write-Verbose "Calling 'List-DevOpsProjects' with parameters: $($params | Out-String)" @@ -52,7 +56,8 @@ Function AzDoAPI_0_ProjectCache { $projectsArr = [System.Collections.ArrayList]::new() # Iterate through each project and get the security descriptors - foreach ($project in $projects) { + foreach ($project in $projects) + { # Add the Project $securityDescriptor = Get-DevOpsSecurityDescriptor -ProjectId $project.Id -Organization $OrganizationName # Add the security descriptor to the project object @@ -63,7 +68,8 @@ Function AzDoAPI_0_ProjectCache { Write-Verbose "'List-DevOpsProjects' returned a total of $($projects.Count) projects." # Iterate through each project in the response and add them to the cache - foreach ($project in $projectsArr) { + foreach ($project in $projectsArr) + { # Log the addition of each project to the cache Write-Verbose "Adding Project '$($project.Name)' to the cache." # Add the project to the cache with its name as the key @@ -76,7 +82,9 @@ Function AzDoAPI_0_ProjectCache { # Indicate completion of adding projects to cache Write-Verbose "Completed adding projects to cache." - } catch { + } + catch + { # Handle any exceptions that occur during the try block Write-Error "An error occurred: $_" } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.ps1 index 13462fbf8..a9ccc99b2 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.ps1 @@ -11,17 +11,19 @@ The name of the organization. If not provided as a parameter, it uses the global .EXAMPLE AzDoAPI_1_GroupCache -OrganizationName "MyOrganization" #> - -Function AzDoAPI_1_GroupCache { +Function AzDoAPI_1_GroupCache +{ [CmdletBinding()] param( + [Parameter(Mandatory = $false)] [string]$OrganizationName ) # Use a verbose statement to indicate the start of the function. Write-Verbose "Starting 'Set-GroupCache' function." - if (-not $OrganizationName) { + if (-not $OrganizationName) + { Write-Verbose "No organization name provided as parameter; using global variable." $OrganizationName = $Global:DSCAZDO_OrganizationName } @@ -30,8 +32,8 @@ Function AzDoAPI_1_GroupCache { Organization = $OrganizationName } - try { - + try + { Write-Verbose "Calling 'List-DevOpsGroups' with parameters: $($params | Out-String)" # Perform an Azure DevOps API request to get the groups @@ -40,7 +42,8 @@ Function AzDoAPI_1_GroupCache { Write-Verbose "'List-DevOpsGroups' returned a total of $($groups.Count) groups." # Iterate through each of the responses and add them to the cache - foreach ($group in $groups) { + foreach ($group in $groups) + { Write-Verbose "Adding group '$($group.PrincipalName)' to cache." # Add the group to the cache Add-CacheItem -Key $group.PrincipalName -Value $group -Type 'LiveGroups' @@ -48,15 +51,13 @@ Function AzDoAPI_1_GroupCache { # Export the cache to a file Export-CacheObject -CacheType 'LiveGroups' -Content $AzDoLiveGroups - Write-Verbose "Completed adding groups to cache." - } catch { - + } + catch + { Write-Error "An error occurred: $_" - } Write-Verbose "Function 'AzDoAPI_1_GroupCache' completed." - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.ps1 index dd729793b..3692471fd 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.ps1 @@ -1,7 +1,31 @@ -Function AzDoAPI_2_UserCache { +<# +.SYNOPSIS +Initializes and populates the user cache for Azure DevOps. + +.DESCRIPTION +The `AzDoAPI_2_UserCache` function initializes and populates the user cache by retrieving user information from Azure DevOps. +It uses the provided organization name or a global variable if no organization name is provided. The function retrieves the +user list, adds each user to the cache, and then exports the cache to a file. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization. If not provided, the function will use the global variable `$Global:DSCAZDO_OrganizationName`. + +.EXAMPLE +PS> AzDoAPI_2_UserCache -OrganizationName "MyOrganization" +This example initializes and populates the user cache for the specified Azure DevOps organization "MyOrganization". + +.NOTES +- This function uses the `List-UserCache` cmdlet to retrieve the list of users. +- Each user is added to the cache using the `Add-CacheItem` cmdlet. +- The cache is exported to a file using the `Export-CacheObject` cmdlet. +- Verbose output is provided to indicate the progress and actions of the function. +#> +Function AzDoAPI_2_UserCache +{ [CmdletBinding()] param( + [Parameter(Mandatory = $false)] [string]$OrganizationName ) @@ -28,7 +52,8 @@ Function AzDoAPI_2_UserCache { Write-Verbose "[AzDoAPI_2_UserCache] 'AzDoAPI_2_UserCache' returned a total of $($users.Count) users." # Iterate through each of the responses and add them to the cache - foreach ($user in $users) { + foreach ($user in $users) + { Write-Verbose "[AzDoAPI_2_UserCache] Adding user '$($user.PrincipalName)' to cache." # Add the group to the cache Add-CacheItem -Key $user.PrincipalName -Value $user -Type 'LiveUsers' diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.ps1 index 77dc34e91..c51ab6280 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.ps1 @@ -1,7 +1,29 @@ +<# +.SYNOPSIS +Initializes and updates the Azure DevOps group member cache. + +.DESCRIPTION +The `AzDoAPI_3_GroupMemberCache` function initializes and updates the cache for Azure DevOps group members. +It retrieves the live group and user caches, iterates through each group, and updates the cache with the members of each group. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization. If not provided, the global variable `$Global:DSCAZDO_OrganizationName` is used. + +.EXAMPLE +AzDoAPI_3_GroupMemberCache -OrganizationName "MyOrganization" +Initializes and updates the group member cache for the specified Azure DevOps organization. + +.NOTES +- This function relies on the `Get-CacheObject`, `List-DevOpsGroupMembers`, and `Add-CacheItem` functions. +- The cache is exported to a file at the end of the function. +- Verbose output is used to indicate the progress of the function. + +#> function AzDoAPI_3_GroupMemberCache { [CmdletBinding()] param( + [Parameter(Mandatory = $false)] [string]$OrganizationName ) @@ -49,11 +71,15 @@ function AzDoAPI_3_GroupMemberCache $azdoUserMembers = $AzDoLiveUsers.value | Where-Object { $_.descriptor -in $groupMembers.memberDescriptor } $azdoGroupMembers = $AzDoLiveGroups.value | Where-Object { $_.descriptor -in $groupMembers.memberDescriptor } - $azdoUserMembers | Select-Object *,@{Name="Type";Exp={"user"}} | Where-Object { $_.descriptor -in $groupMembers.memberDescriptor } | ForEach-Object { + # Add the users to the cache + $azdoUserMembers = $azdoUserMembers | Select-Object *,@{Name="Type";Exp={"user"}} + $azdoUserMembers | Where-Object { $_.descriptor -in $groupMembers.memberDescriptor } | ForEach-Object { $null = $members.Add($_) } - $azdoGroupMembers | Select-Object *,@{Name="Type";Exp={"group"}} | Where-Object { $_.descriptor -in $groupMembers.memberDescriptor } | ForEach-Object { + # Add the groups to the cache + $azdoGroupMembers = $azdoGroupMembers | Select-Object *,@{Name="Type";Exp={"group"}} + $azdoGroupMembers | Where-Object { $_.descriptor -in $groupMembers.memberDescriptor } | ForEach-Object { $null = $members.Add($_) } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.ps1 index ec551da51..5d601ded5 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.ps1 @@ -1,7 +1,30 @@ +<# +.SYNOPSIS +Initializes the Git repository cache for Azure DevOps projects. + +.DESCRIPTION +The AzDoAPI_4_GitRepositoryCache function initializes the Git repository cache by enumerating live projects and retrieving their repositories from Azure DevOps. The repositories are then added to the cache. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization. If not provided, the function uses the global variable $Global:DSCAZDO_OrganizationName. + +.EXAMPLE +AzDoAPI_4_GitRepositoryCache -OrganizationName "MyOrganization" +Initializes the Git repository cache for the specified Azure DevOps organization. + +.EXAMPLE +AzDoAPI_4_GitRepositoryCache +Initializes the Git repository cache using the global organization name variable. + +.NOTES +This function uses verbose logging to indicate the progress and actions taken during the cache initialization process. It also handles errors by logging them as errors. + +#> function AzDoAPI_4_GitRepositoryCache { [CmdletBinding()] param( + [Parameter(Mandatory = $false)] [string]$OrganizationName ) diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.ps1 index 3f459967e..63f2897d6 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.ps1 @@ -1,7 +1,30 @@ +<# +.SYNOPSIS +Initializes and caches the permissions for Azure DevOps security namespaces. + +.DESCRIPTION +The `AzDoAPI_5_PermissionsCache` function retrieves the security namespaces for a specified Azure DevOps organization and caches the permissions. +If no organization name is provided, it uses a global variable for the organization name. The function then exports the cached permissions to a file. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization. This parameter is optional. If not provided, the function uses the global variable `$Global:DSCAZDO_OrganizationName`. + +.EXAMPLE +AzDoAPI_5_PermissionsCache -OrganizationName "MyOrganization" +This example initializes and caches the permissions for the "MyOrganization" Azure DevOps organization. + +.EXAMPLE +AzDoAPI_5_PermissionsCache +This example initializes and caches the permissions using the global organization name variable. + +.NOTES +This function requires the `List-DevOpsSecurityNamespaces`, `Add-CacheItem`, and `Export-CacheObject` cmdlets to be available in the session. +#> function AzDoAPI_5_PermissionsCache { [CmdletBinding()] param( + [Parameter(Mandatory = $false)] [string]$OrganizationName ) @@ -23,7 +46,6 @@ function AzDoAPI_5_PermissionsCache # # Iterate through each security namespace and export the permissions to the cache - foreach ($securityNamespace in $securityNamespaces) { $securityNamespaceName = $securityNamespace.name @@ -31,7 +53,6 @@ function AzDoAPI_5_PermissionsCache # Add the project to the cache with its name as the key Add-CacheItem -Key $securityNamespaceName -Value $value -Type 'SecurityNamespaces' - } # Export the cache to a file diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.ps1 index 1725c2dd6..70241edb4 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.ps1 @@ -1,3 +1,24 @@ +<# +.SYNOPSIS +Initializes the cache with Azure DevOps service principals. + +.DESCRIPTION +The AzDoAPI_6_ServicePrinciple function retrieves service principals from Azure DevOps and adds them to a cache. +If no organization name is provided, it uses a global variable for the organization name. +The function then exports the cache to a file. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization. If not provided, the function uses the global variable $Global:DSCAZDO_OrganizationName. + +.EXAMPLE +AzDoAPI_6_ServicePrinciple -OrganizationName "MyOrganization" +This example initializes the cache with service principals from the "MyOrganization" Azure DevOps organization. + +.NOTES +This function uses the List-DevOpsServicePrinciples cmdlet to retrieve service principals and the Add-CacheItem cmdlet to add them to the cache. +The cache is then exported using the Export-CacheObject cmdlet. + +#> function AzDoAPI_6_ServicePrinciple { [CmdletBinding()] diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.ps1 index 634aebd2f..6bdcccd80 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.ps1 @@ -1,7 +1,31 @@ +<# +.SYNOPSIS + Initializes and updates the identity subject descriptors cache for Azure DevOps groups, users, and service principals. + +.DESCRIPTION + The AzDoAPI_7_IdentitySubjectDescriptors function retrieves and updates the identity subject descriptors for Azure DevOps groups, users, and service principals. + It uses the provided organization name or a global variable if no organization name is provided. The function enumerates the live groups, users, and service principals + from the cache, queries their identities, and updates the cache with the retrieved identity information. + +.PARAMETER OrganizationName + The name of the Azure DevOps organization. If not provided, the function uses the global variable $Global:DSCAZDO_OrganizationName. + +.EXAMPLE + PS> AzDoAPI_7_IdentitySubjectDescriptors -OrganizationName "MyOrganization" + Initializes and updates the identity subject descriptors cache for the specified Azure DevOps organization. + +.EXAMPLE + PS> AzDoAPI_7_IdentitySubjectDescriptors + Initializes and updates the identity subject descriptors cache using the global organization name. + +.NOTES + This function is part of the AzureDevOpsDsc module and is used internally to manage the identity subject descriptors cache. +#> function AzDoAPI_7_IdentitySubjectDescriptors { [CmdletBinding()] param( + [Parameter(Mandatory = $false)] [string]$OrganizationName ) @@ -31,10 +55,9 @@ function AzDoAPI_7_IdentitySubjectDescriptors } # Iterate through each of the groups and query the Identity and add to the cache - ForEach ($AzDoLiveGroup in $AzDoLiveGroups) { - + ForEach ($AzDoLiveGroup in $AzDoLiveGroups) + { $identity = Get-DevOpsDescriptorIdentity @params -SubjectDescriptor $AzDoLiveGroup.value.descriptor - $ACLIdentity = [PSCustomObject]@{ id = $identity.id descriptor = $identity.descriptor @@ -64,8 +87,8 @@ function AzDoAPI_7_IdentitySubjectDescriptors # # Iterate through each of the users and query the Identity and add to the cache - ForEach ($AzDoLiveUser in $AzDoLiveUsers) { - + ForEach ($AzDoLiveUser in $AzDoLiveUsers) + { $identity = Get-DevOpsDescriptorIdentity @params -SubjectDescriptor $AzDoLiveUser.value.descriptor $ACLIdentity = [PSCustomObject]@{ @@ -97,8 +120,8 @@ function AzDoAPI_7_IdentitySubjectDescriptors # # Iterate through each of the service principals and query the Identity and add to the cache - ForEach ($AzDoLiveServicePrinciple in $AzDoLiveServicePrinciples) { - + ForEach ($AzDoLiveServicePrinciple in $AzDoLiveServicePrinciples) + { $identity = Get-DevOpsDescriptorIdentity @params -SubjectDescriptor $AzDoLiveServicePrinciple.value.descriptor $ACLIdentity = [PSCustomObject]@{ @@ -127,5 +150,4 @@ function AzDoAPI_7_IdentitySubjectDescriptors # Update the cache Export-CacheObject -CacheType 'LiveServicePrinciples' -Content $AzDoLiveServicePrinciples - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.ps1 index 8ea37b5bf..e3d88976f 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.ps1 @@ -1,3 +1,26 @@ +<# +.SYNOPSIS +Retrieves and caches Azure DevOps project process templates for a specified organization. + +.DESCRIPTION +The 'AzDoAPI_8_ProjectProcessTemplates' function retrieves the list of project process templates +from Azure DevOps for a specified organization and caches them. If no organization name is provided +as a parameter, it uses a global variable for the organization name. The function then exports the +cached process templates to a file. + +.PARAMETER OrganizationName +The name of the Azure DevOps organization for which to retrieve the project process templates. + +.EXAMPLE +PS> AzDoAPI_8_ProjectProcessTemplates -OrganizationName "MyOrganization" + +This example retrieves and caches the project process templates for the organization named "MyOrganization". + +.NOTES +This function uses the 'List-DevOpsProcess' cmdlet to retrieve the process templates and the 'Add-CacheItem' +cmdlet to add each process template to the cache. The cache is then exported using the 'Export-CacheObject' cmdlet. + +#> function AzDoAPI_8_ProjectProcessTemplates { [CmdletBinding()] diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.ps1 index ec6d933ec..72eda4ce8 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.ps1 @@ -30,10 +30,11 @@ None. .NOTES This function is part of the AzureDevOpsDsc module and is used for caching Azure DevOps API responses. #> -Function Export-CacheObject { +Function Export-CacheObject +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] [string]$CacheType, @@ -49,18 +50,22 @@ Function Export-CacheObject { Write-Verbose "[Export-ObjectCache] Starting export process for cache type: $CacheType" # Use the Enviroment Variables to set the Cache Directory Path - if ($ENV:AZDODSC_CACHE_DIRECTORY) { + if ($ENV:AZDODSC_CACHE_DIRECTORY) + { $CacheDirectoryPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "Cache" - } else { + } + else + { Throw "The environment variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the variable to the path of the cache directory." } - try { - + try + { $cacheFilePath = Join-Path -Path $CacheDirectoryPath -ChildPath "$CacheType.clixml" # Create cache directory if it does not exist - if (-not (Test-Path -Path $CacheDirectoryPath)) { + if (-not (Test-Path -Path $CacheDirectoryPath)) + { Write-Verbose "[Export-ObjectCache] Creating cache directory at path: $CacheDirectoryPath" New-Item -Path $CacheDirectoryPath -ItemType Directory | Out-Null } @@ -72,8 +77,9 @@ Function Export-CacheObject { # Confirm completion of export process Write-Verbose "[Export-ObjectCache] Export process completed successfully for cache type: $CacheType" - } catch { - Write-Error "[Export-ObjectCache] Failed to create cache for Azure DevOps API: $_" - throw + } + catch + { + throw "[Export-ObjectCache] Failed to create cache for Azure DevOps API: $_" } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 index efd791b24..62e27f068 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 @@ -25,26 +25,23 @@ $filteredCacheItem Author: Your Name Date: Today's Date #> - Function Find-CacheItem { [CmdletBinding()] [OutputType([System.Management.Automation.PSObject])] param ( - [Parameter(Mandatory, ValueFromPipeline)] + [Parameter(Mandatory = $true, ValueFromPipeline)] [Alias('Cache')] [Object[]]$CacheList, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ScriptBlock]$Filter ) - # # Logging Write-Verbose "[Find-CacheItem] Searching for the CacheItem with filter '$Filter'." - # # Get the CacheItem $cacheItem = $null $cacheItem = $CacheList | Where-Object -FilterScript $Filter diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.ps1 index 1d7c3f6a1..ca059cffa 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.ps1 @@ -1,31 +1,29 @@ +<# +.SYNOPSIS +Get a cache item from the cache. +.DESCRIPTION +Get a cache item from the cache. -function Get-CacheItem -{ - <# - .SYNOPSIS - Get a cache item from the cache. - - .DESCRIPTION - Get a cache item from the cache. +.PARAMETER Key +The key of the cache item to get. - .PARAMETER Key - The key of the cache item to get. +.EXAMPLE +Get-CacheItem -Key 'MyKey' - .EXAMPLE - Get-CacheItem -Key 'MyKey' - - .NOTES - This function is private and should not be used directly. - #> +.NOTES +This function is private and should not be used directly. +#> +function Get-CacheItem +{ [CmdletBinding()] [OutputType([CacheItem])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $Key, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] [string] $Type, @@ -39,13 +37,17 @@ function Get-CacheItem { [System.Collections.Generic.List[CacheItem]]$cache = Get-CacheObject -CacheType $Type $cacheItem = $cache.Where({$_.Key -eq $Key}) - } catch + } + catch { $cacheItem = $null Write-Verbose $_ } - if ($null -eq $cacheItem) { return $null } + if ($null -eq $cacheItem) + { + return $null + } if ($Filter -ne $null) { diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 index d32dbc286..df2490662 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 @@ -32,10 +32,11 @@ This function is part of the AzureDevOpsDsc module. https://github.com/Azure/AzureDevOpsDsc #> -function Get-CacheObject { +function Get-CacheObject +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] [string]$CacheType ) @@ -44,21 +45,28 @@ function Get-CacheObject { Write-Verbose "[Get-ObjectCache] Attempting to retrieve cache object for type: $CacheType" # Use the Enviroment Variables to set the Cache Directory Path - if ($ENV:AZDODSC_CACHE_DIRECTORY) { + if ($ENV:AZDODSC_CACHE_DIRECTORY) + { $CacheDirectoryPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "Cache" - } else { + } + else + { Throw "The environment variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the variable to the path of the cache directory." } - try { + try + { # Attempt to get the variable from the global scope $var = Get-Variable -Name "AzDo$CacheType" -Scope Global -ErrorAction SilentlyContinue - if ($var) { + if ($var) + { Write-Verbose "[Get-ObjectCache] Cache object found in memory for type: $CacheType" # If the variable is found, return the content of the cache. Dont use $var here, since it will a different object type. $var = Get-Variable -Name "AzDo$CacheType" -ValueOnly -Scope Global - } else { + } + else + { Write-Verbose "[Get-ObjectCache] Cache object not found in memory, attempting to import for type: $CacheType" $var = Import-CacheObject -CacheType $CacheType } @@ -67,8 +75,9 @@ function Get-CacheObject { Write-Verbose "[Get-ObjectCache] Returning imported cache object for type: $CacheDirectoryPath" return $var - } catch { - Write-Error "[Get-ObjectCache] Failed to get cache for Azure DevOps API: $_" - throw + } + catch + { + throw "[Get-ObjectCache] Failed to get cache for Azure DevOps API: $_" } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.ps1 index 2edc9b096..e45e146e4 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.ps1 @@ -29,19 +29,21 @@ function Import-CacheObject { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] [string]$CacheType - ) # Write initial verbose message Write-Verbose "[Import-CacheObject] Starting to import cache object for type: $CacheType" # Use the Enviroment Variables to set the Cache Directory Path - if ($ENV:AZDODSC_CACHE_DIRECTORY) { + if ($ENV:AZDODSC_CACHE_DIRECTORY) + { $CacheDirectoryPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "Cache" - } else { + } + else + { Throw "The environment variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the variable to the path of the cache directory." } @@ -73,7 +75,10 @@ function Import-CacheObject { $Content | ForEach-Object { # If the key is empty, skip the item - if ([string]::IsNullOrEmpty($_.Key)) { return } + if ([string]::IsNullOrEmpty($_.Key)) + { + return + } # Create a new CacheItem object and add it to the list $newCache.Add([CacheItem]::New($_.Key, $_.Value)) @@ -84,9 +89,9 @@ function Import-CacheObject Set-Variable -Name "AzDo$CacheType" -Value $newCache -Scope Global -Force Write-Verbose "[Import-CacheObject] Cache object imported successfully for '$CacheType'." - } catch + } + catch { - Write-Error "[Import-CacheObject] Failed to import cache for Azure DevOps API: $_" - throw $_ + throw "[Import-CacheObject] Failed to import cache for Azure DevOps API: $_" } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.ps1 index c437c470b..42a1a2071 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.ps1 @@ -14,11 +14,12 @@ .NOTES #> -Function Initialize-CacheObject { +Function Initialize-CacheObject +{ [CmdletBinding()] param( # Specifies the type of cache to initialize. Valid values are 'Project', 'Team', 'Group', and 'SecurityDescriptor'. - [Parameter(Mandatory,ValueFromPipeline)] + [Parameter(Mandatory = $true,ValueFromPipeline)] [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] [string]$CacheType, # Used to bypass the file deletion check for live caches. Needed for DSC Resources to import the cache. @@ -26,12 +27,16 @@ Function Initialize-CacheObject { [Switch]$BypassFileCheck ) - try { + try + { # Use the Enviroment Variables to set the Cache Directory Path - if ($ENV:AZDODSC_CACHE_DIRECTORY) { + if ($ENV:AZDODSC_CACHE_DIRECTORY) + { $CacheDirectoryPath = Join-Path -Path $ENV:AZDODSC_CACHE_DIRECTORY -ChildPath "Cache" - } else { + } + else + { Throw "The environment variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the variable to the path of the cache directory." } @@ -39,33 +44,36 @@ Function Initialize-CacheObject { Write-Verbose "[Initialize-CacheObject] Cache file path: $cacheFilePath" # If the cache group is LiveGroups or LiveProjects, set the cache file path to the temporary directory - if (-not($BypassFileCheck.IsPresent) -and ($CacheType -match '^Live')) { - + if (-not($BypassFileCheck.IsPresent) -and ($CacheType -match '^Live')) + { # Flush the cache if it is a live cache - if (Test-Path -LiteralPath $cacheFilePath -ErrorAction SilentlyContinue) { + if (Test-Path -LiteralPath $cacheFilePath -ErrorAction SilentlyContinue) + { Write-Verbose "[Initialize-CacheObject] Cache file found. Removing cache file for '$CacheType'." Remove-Item -LiteralPath $cacheFilePath -Force } - - } else { + } + else + { # Test if the Cache File exists. If it exists, import the cache object Write-Verbose "[Initialize-CacheObject] Cache file path: $cacheFilePath" } # Test if the Cache File exists. If it exists, import the cache object - if (Test-Path -Path $cacheFilePath) { - + if (Test-Path -Path $cacheFilePath) + { # If the cache file exists, import the cache object Write-Verbose "[Initialize-CacheObject] Cache file found. Importing cache object for '$CacheType'." Import-CacheObject -CacheType $CacheType - - } else { - + } + else + { # If the cache file does not exist, create a new cache object Write-Verbose "[Initialize-CacheObject] Cache file not found. Creating new cache object for '$CacheType'." # Create the cache directory if it does not exist - if (-not (Test-Path -Path $CacheDirectoryPath)) { + if (-not (Test-Path -Path $CacheDirectoryPath)) + { Write-Verbose "[Initialize-CacheObject] Cache directory not found. Creating cache directory." New-Item -Path $CacheDirectoryPath -ItemType Directory | Out-Null } @@ -78,8 +86,9 @@ Function Initialize-CacheObject { } - } catch { - Write-Verbose "An error occurred: $_" + } + catch + { throw "[Initialize-CacheObject] Failed to import cache for Azure DevOps API: $_" } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.ps1 index 0919e036d..a3a6b57e0 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.ps1 @@ -1,6 +1,25 @@ -Function Refresh-AzDoCache { +<# +.SYNOPSIS +Refreshes the Azure DevOps cache by clearing existing cache variables and reinitializing them. + +.DESCRIPTION +The Refresh-AzDoCache function clears the current Azure DevOps cache variables and reinitializes them by invoking all caching commands from the AzureDevOpsDsc.Common module. It then reimports the cache objects to ensure the cache is up-to-date. + +.PARAMETER OrganizationName +Specifies the name of the Azure DevOps organization for which the cache should be refreshed. This parameter is mandatory. + +.EXAMPLE +Refresh-AzDoCache -OrganizationName "MyOrganization" +This example refreshes the Azure DevOps cache for the organization named "MyOrganization". + +.NOTES +This function is intended for internal use within the AzureDevOpsDsc.Common module to maintain the integrity of the cache. + +#> +Function Refresh-AzDoCache +{ param( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$OrganizationName ) @@ -17,5 +36,4 @@ Function Refresh-AzDoCache { Import-CacheObject -CacheType $_ } - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.ps1 index 97c7f541e..c0d1a04d1 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.ps1 @@ -1,11 +1,38 @@ -Function Refresh-CacheIdentity { +<# +.SYNOPSIS +Refreshes the cache identity for a given object. + +.DESCRIPTION +The Refresh-CacheIdentity function updates the cache identity for a specified object. It performs a lookup to get the ACL descriptor and adds the ACL identity to the object. The updated object is then added to the cache. + +.PARAMETER Identity +The object whose cache identity needs to be refreshed. This parameter is mandatory. + +.PARAMETER Key +The key associated with the cache item. This parameter is mandatory. + +.PARAMETER CacheType +The type of cache to update. This parameter is mandatory and must be one of the valid cache types returned by Get-AzDoCacheObjects. + +.EXAMPLE +$identity = Get-IdentityObject +$key = "someKey" +$cacheType = "someCacheType" +Refresh-CacheIdentity -Identity $identity -Key $key -CacheType $cacheType + +.NOTES +This function relies on the global variable $Global:DSCAZDO_OrganizationName and the functions Get-DevOpsDescriptorIdentity, Add-CacheItem, Get-CacheObject, and Set-CacheObject. + +#> +Function Refresh-CacheIdentity +{ [CmdletBinding()] param( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Object]$Identity, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String]$Key, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] [String]$CacheType ) diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.ps1 index 5c8d01fb4..f3f8fbe0b 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.ps1 @@ -1,7 +1,28 @@ +<# +.SYNOPSIS + Unloads and reloads the cache object of the specified type. + +.DESCRIPTION + The Refresh-CacheObject function is used to unload and then reload a cache object of a specified type. + This can be useful when you need to refresh the state of a cache object to ensure it is up-to-date. + +.PARAMETER CacheType + The type of the cache object to be refreshed. This parameter is mandatory and must be one of the valid cache object types returned by the Get-AzDoCacheObjects function. + +.EXAMPLE + Refresh-CacheObject -CacheType 'Project' + This example unloads and reloads the cache object of type 'Project'. + +.NOTES + The function uses the Remove-Variable cmdlet to unload the cache object and the Import-CacheObject function to reload it. + Verbose messages are written to provide feedback on the unloading and reloading process. + +#> # Unloads and reloads the cache object of the specified type. -Function Refresh-CacheObject { +Function Refresh-CacheObject +{ param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] [string] $CacheType diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.ps1 index 999588c50..f53b4ef82 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.ps1 @@ -19,14 +19,14 @@ Removes the item with the key "myKey" from the Project cache. Remove-CacheItem -Key "anotherKey" -Type "Group" Removes the item with the key "anotherKey" from the Group cache. #> - -Function Remove-CacheItem { +Function Remove-CacheItem +{ param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $Key, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] [string] $Type @@ -39,7 +39,8 @@ Function Remove-CacheItem { Write-Verbose "[Remove-CacheItem] Removing the cache item with the key: '$Key'." # If the cache has a length of 1, and the key matches, remove the cache - if ($cache.Count -eq 1 -and $cache[0].Key -eq $Key) { + if ($cache.Count -eq 1 -and $cache[0].Key -eq $Key) + { Write-Verbose "[Remove-CacheItem] Cache has a length of 1 and the key matches. Removing the cache." Set-Variable -Name "AzDo$Type" -Value ([System.Collections.Generic.List[CacheItem]]::New()) -Scope Global return diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 index 1dec5370d..8dce154f3 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 @@ -37,7 +37,7 @@ function Set-CacheObject { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateScript({$_ -in (Get-AzDoCacheObjects)})] [string]$CacheType, @@ -67,8 +67,7 @@ function Set-CacheObject } catch { - Write-Error "[Set-ObjectCache] Failed to create cache for Azure DevOps API: $_" - throw + throw "[Set-ObjectCache] Failed to create cache for Azure DevOps API: $_" } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 index 7c0be4dc8..654737a41 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 @@ -27,11 +27,12 @@ Author: Your Name Date: Today's Date #> -Function ConvertTo-ACEList { +Function ConvertTo-ACEList +{ [CmdletBinding()] param ( # Mandatory parameter: the security namespace as a string. - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$SecurityNamespace, # Mandatory parameter: an array of permissions objects. @@ -39,7 +40,7 @@ Function ConvertTo-ACEList { [Object[]]$Permissions = @(), # Mandatory parameter: the organization name as a string. - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$OrganizationName ) @@ -53,7 +54,8 @@ Function ConvertTo-ACEList { $ACEs = [System.Collections.Generic.List[HashTable]]::new() # Iterate through each of the permissions and construct the ACE token. - ForEach ($Permission in $Permissions) { + ForEach ($Permission in $Permissions) + { Write-Verbose "[ConvertTo-ACEList] Constructing ACE for $($Permission.Identity)." @@ -78,12 +80,14 @@ Function ConvertTo-ACEList { } # If the Identity is not found, log a warning. - if (-not $ht.Identity) { + if (-not $ht.Identity) + { Write-Warning "[ConvertTo-ACEList] Identity $($Permission.Identity) was not found. This will not be added to the ACEs list." continue } # If the Permissions are not found, log a warning. - if (-not $ht.Permissions) { + if (-not $ht.Permissions) + { Write-Warning "[ConvertTo-ACEList] Permissions for $($Permission.Identity) were not found. This will not be added to the ACEs list." continue } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.ps1 index 039cc9cba..5a9d899eb 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.ps1 @@ -1,12 +1,13 @@ -Function ConvertTo-ACETokenList { +Function ConvertTo-ACETokenList +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$SecurityNamespace, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Object[]]$ACEPermissions ) @@ -19,7 +20,8 @@ Function ConvertTo-ACETokenList { $SecurityDescriptor = Get-CacheItem -Key $SecurityNamespace -Type 'SecurityNamespaces' # Check if the Security Descriptor was found - if (-not $SecurityDescriptor) { + if (-not $SecurityDescriptor) + { Write-Error "Security Descriptor not found for namespace: $SecurityNamespace" return } @@ -27,8 +29,8 @@ Function ConvertTo-ACETokenList { # Iterate through each of the ACEs and construct the ACE Object Write-Verbose "[ConvertTo-ACETokenList] Iterating through each of the ACE Permissions." - ForEach ($ACEPermission in $ACEPermissions) { - + ForEach ($ACEPermission in $ACEPermissions) + { # Check to see if there are any permissions that are not found in the Security Descriptor $missingPermissions = $ACEPermission.Keys | Where-Object { ($_ -notin $SecurityDescriptor.actions.displayName) -and diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 index 2b3fe3536..1567b962b 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 @@ -41,7 +41,8 @@ A list of Access Control Lists (ACLs) created from the provided permissions. Author: Your Name Date: Today's Date #> -Function ConvertTo-ACL { +Function ConvertTo-ACL +{ [CmdletBinding()] param ( # Mandatory parameter: an array of hash tables containing permissions. @@ -93,7 +94,8 @@ Function ConvertTo-ACL { } # If the ACEs are empty, write a warning and return. - if ($ACL.aces.Count -eq 0) { + if ($ACL.aces.Count -eq 0) + { Write-Warning "[ConvertTo-ACL] No ACEs were created. Returning." return $ACL } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.ps1 index 1c7a30216..a9c7ed078 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.ps1 @@ -76,17 +76,18 @@ System.Collections.Hashtable The constructed ACLs hashtable containing the serialized ACLs. #> -Function ConvertTo-ACLHashtable { +Function ConvertTo-ACLHashtable +{ param( [Parameter()] [Object[]] $ReferenceACLs, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Object[]] $DescriptorACLList, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String] $DescriptorMatchToken ) @@ -113,7 +114,8 @@ Function ConvertTo-ACLHashtable { # Iterate through the filtered descriptor ACLs to construct the ACLs object Write-Verbose "[ConvertTo-ACLHashtable] Iterating through the filtered descriptor ACLs to construct the ACLs object." - ForEach ($DescriptorACL in $FilteredDescriptorACLs) { + ForEach ($DescriptorACL in $FilteredDescriptorACLs) + { Write-Verbose "Adding filtered ACL to the ACLs object." $ACLHashtable.value.Add($DescriptorACL) } @@ -122,7 +124,8 @@ Function ConvertTo-ACLHashtable { Write-Verbose "[ConvertTo-ACLHashtable] Constructing the ACLs object from the reference ACLs." # Iterate through the ACLs in the ReferenceACLs - ForEach ($ReferenceACL in $ReferenceACLs) { + ForEach ($ReferenceACL in $ReferenceACLs) + { Write-Verbose "[ConvertTo-ACLHashtable] Processing reference ACL." Write-Verbose "[ConvertTo-ACLHashtable] Reference ACL: $($ReferenceACL | ConvertTo-Json -Depth 3)" @@ -134,7 +137,8 @@ Function ConvertTo-ACLHashtable { } # Iterate through the ACEs in the current ACL to construct the ACEs Dictionary - ForEach ($ACE in $ReferenceACL.aces) { + ForEach ($ACE in $ReferenceACL.aces) + { Write-Verbose "[ConvertTo-ACLHashtable] Constructing ACE Object." # Construct the ACE Object with properties allow, deny, and descriptor diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 index ba0d274b5..a47c66246 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 @@ -29,7 +29,8 @@ Author: Michael Zanatta Date: 06/26/2024 #> -Function ConvertTo-FormattedACL { +Function ConvertTo-FormattedACL +{ [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] @@ -42,18 +43,21 @@ Function ConvertTo-FormattedACL { [String]$OrganizationName ) - begin { + begin + { Write-Verbose "[ConvertTo-FormattedACL] Started." $ACLList = [System.Collections.Generic.List[HashTable]]::new() } - process { + process + { # Logging Write-Verbose "[ConvertTo-FormattedACL] Processing ACL: $($ACL.token)" Write-Verbose "[ConvertTo-FormattedACL] ACL: $($ACL | ConvertTo-Json)" # If the token is empty, skip it. - if (-not $ACL.token) { + if (-not $ACL.token) + { Write-Verbose "[ConvertTo-FormattedACL] Current token is empty. Skipping." Write-Warning "[ConvertTo-FormattedACL] Current token is empty. Skipping. ACL: $($ACL | ConvertTo-Json)" return @@ -64,7 +68,8 @@ Function ConvertTo-FormattedACL { Write-Verbose "[ConvertTo-FormattedACL] Found ACE entries: $($ACEEntries.Count)" # If the ACE entries are empty, skip it. - if ($ACEEntries.Count -eq 0) { + if ($ACEEntries.Count -eq 0) + { Write-Verbose "[ConvertTo-FormattedACL] Current ACE entries are empty. Skipping." Write-Warning "[ConvertTo-FormattedACL] Current ACE entries are empty. Skipping. ACL: $($ACL | ConvertTo-Json)" return @@ -80,14 +85,16 @@ Function ConvertTo-FormattedACL { Write-Verbose "[ConvertTo-FormattedACL] Found ACEs: $($ACEs.Count)" # If the ACEs are empty, skip it. - if ($ACEs.Count -eq 0) { + if ($ACEs.Count -eq 0) + { Write-Verbose "[ConvertTo-FormattedACL] Current ACEs are empty. Skipping." Write-Warning "[ConvertTo-FormattedACL] Current ACEs are empty. Skipping. ACL: $($ACL | ConvertTo-Json)" return } # Create the Formatted ACL Object - foreach ($ACE in $ACEs) { + foreach ($ACE in $ACEs) + { Write-Verbose "[ConvertTo-FormattedACL] Matching identity for ACE: $($ACE.Name)" $ACE."Identity" = Find-Identity -Name $ACE.Name -OrganizationName $OrganizationName Write-Verbose "[ConvertTo-FormattedACL] Formatting ACE: $($ACE.Name) - Allow $($ACE.value.allow) - Deny $($ACE.value.deny)" @@ -106,9 +113,9 @@ Function ConvertTo-FormattedACL { $ACLList.Add($formattedACL) } - end { + end + { Write-Verbose "[ConvertTo-FormattedACL] Completed." - return $ACLList } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.ps1 index ffec9acee..9a097ac04 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.ps1 @@ -25,7 +25,7 @@ Function ConvertTo-FormattedToken { [CmdletBinding()] param ( # Define a mandatory parameter named 'Token' of type Object array - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Object[]]$Token ) @@ -36,7 +36,8 @@ Function ConvertTo-FormattedToken { $string = "" # Determine the type of the token and format accordingly - switch ($Token) { + switch ($Token) + { # If the token type is 'GitOrganization' {$_.type -eq 'GitOrganization'} { $string = 'repoV2' diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.ps1 index 622359687..f47891793 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.ps1 @@ -35,7 +35,7 @@ Function Format-ACEs [Int]$Allow=0, [Parameter()] [Int]$Deny=0, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$SecurityNamespace ) diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 index 5f9aba1e2..9385cee09 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 @@ -24,7 +24,8 @@ Author: Your Name Date: Current Date #> -Function Get-BitwiseOrResult { +Function Get-BitwiseOrResult +{ [CmdletBinding()] param ( [int[]]$integers @@ -35,19 +36,23 @@ Function Get-BitwiseOrResult { $result = 0 - if ($integers.Count -eq 0) { + if ($integers.Count -eq 0) + { return 0 } - foreach ($integer in $integers) { - if (-not [int]::TryParse($integer.ToString(), [ref]$null)) { + foreach ($integer in $integers) + { + if (-not [int]::TryParse($integer.ToString(), [ref]$null)) + { Write-Error "Invalid integer value: $integer" return 0 } $result = $result -bor $integer } - if ([String]::IsNullOrEmpty($result)) { + if ([String]::IsNullOrEmpty($result)) + { return 0 } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.ps1 index b033f0ed6..7237e2914 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.ps1 @@ -1,4 +1,29 @@ -Function Group-ACEs { +<# +.SYNOPSIS + Groups Access Control Entries (ACEs) by identity and permissions. + +.DESCRIPTION + The Group-ACEs function takes an array of ACE objects and groups them by their identity. + It then further groups the identities by their permissions. The function returns a list + of grouped ACEs. + +.PARAMETER ACEs + An array of ACE objects to be grouped. Each ACE object should have an Identity and Permissions property. + +.OUTPUTS + System.Collections.Generic.List[HashTable] + A list of hash tables where each hash table represents a grouped ACE with its identity and permissions. + +.EXAMPLE + $aces = Get-ACEs + $groupedACEs = Group-ACEs -ACEs $aces + Write-Output $groupedACEs + +.NOTES + The function uses verbose output to provide detailed information about its processing steps. +#> +Function Group-ACEs +{ param( # Mandatory parameter: an array of ACE objects. [Parameter()] @@ -8,7 +33,8 @@ Function Group-ACEs { Write-Verbose "[Group-ACE] Started." # Check if the ACEs are not found. - if (-not $ACEs) { + if (-not $ACEs) + { Write-Verbose "[Group-ACE] ACEs not found." return } @@ -39,8 +65,8 @@ Function Group-ACEs { Write-Verbose "[Group-ACE] Grouping multiple identities by permissions." # Group the multiple identities by the permissions - ForEach ($item in $Multiple) { - + ForEach ($item in $Multiple) + { Write-Verbose "[Group-ACE] Processing multiple identity: $($item.Group[0].Identity)" # Create a new hash table for the group diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.ps1 index 0ec4b09f9..ac59d3d57 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.ps1 @@ -27,14 +27,15 @@ This example converts the security TokenName 'Contoso/Org/Project' to an ACL tok This function is part of the AzureDevOpsDsc.Common module and is used internally by other functions in the module. #> -Function New-ACLToken { +Function New-ACLToken +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$SecurityNamespace, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$TokenName ) @@ -45,32 +46,36 @@ Function New-ACLToken { Write-Verbose "[New-ACLToken] Security Namespace: $SecurityNamespace" Write-Verbose "[New-ACLToken] Token Name: $TokenName" - $result = @{} # Create a new ACL Object - switch ($SecurityNamespace) { + switch ($SecurityNamespace) + { # Git Repositories 'Git Repositories' { # Derive the Token Type GitOrganization, GitProject or GitRepository - if ($TokenName -match $LocalizedDataAzResourceTokenPatten.OrganizationGit) { + if ($TokenName -match $LocalizedDataAzResourceTokenPatten.OrganizationGit) + { # Derive the Token Type GitOrganization $result.type = 'GitOrganization' - - } elseif ($TokenName -match $LocalizedDataAzResourceTokenPatten.GitProject) { + } + elseif ($TokenName -match $LocalizedDataAzResourceTokenPatten.GitProject) + { # Derive the Token Type GitProject $result.type = 'GitProject' $result.projectId = (Get-CacheItem -Key $matches.ProjectName.Trim() -Type 'LiveProjects').id - - } elseif ($TokenName -match $LocalizedDataAzResourceTokenPatten.GitRepository) { + } + elseif ($TokenName -match $LocalizedDataAzResourceTokenPatten.GitRepository) + { # Derive the Token Type GitRepository $result.type = 'GitRepository' $result.projectId = (Get-CacheItem -Key $matches.ProjectName.Trim() -Type 'LiveProjects').id $result.RepoId = (Get-CacheItem -Key $TokenName -Type 'LiveRepositories').id - - } else { + } + else + { # Derive the Token Type GitUnknown $result.type = 'GitUnknown' Write-Warning "[New-ACLToken] TokenName '$TokenName' does not match any known Git ACL Token Patterns." @@ -82,13 +87,15 @@ Function New-ACLToken { 'Identity' { # Derive the Token Type Identity - if ($TokenName -match $LocalizedDataAzResourceTokenPatten.GroupPermission) { + if ($TokenName -match $LocalizedDataAzResourceTokenPatten.GroupPermission) + { # Derive the Token Type Identity $result.type = 'GitGroupPermission' $result.projectId = $matches.ProjectId $result.groupId = $matches.GroupId - - } else { + } + else + { # Derive the Token Type Identity $result.type = 'GroupUnknown' Write-Warning "[New-ACLToken] TokenName '$TokenName' does not match any known Identity ACL Token Patterns." diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.ps1 index 79a461f42..d63f4c8a5 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.ps1 @@ -1,9 +1,10 @@ -Function Parse-ACLToken { +Function Parse-ACLToken +{ param( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String]$Token, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateSet('Identity', 'Git Repositories')] [String]$SecurityNamespace ) @@ -16,9 +17,11 @@ Function Parse-ACLToken { # # Git Repositories - if ($SecurityNamespace -eq 'Git Repositories') { + if ($SecurityNamespace -eq 'Git Repositories') + { # Match the Token with the Regex Patterns - switch -regex ($Token.Trim()) { + switch -regex ($Token.Trim()) + { $LocalizedDataAzACLTokenPatten.OrganizationGit { $result.type = 'OrganizationGit' break; @@ -46,10 +49,13 @@ Function Parse-ACLToken { # # Identity - } elseif ($SecurityNamespace -eq 'Identity') { + } + elseif ($SecurityNamespace -eq 'Identity') + { # Match the Token with the Regex Patterns - switch -regex ($Token.Trim()) { + switch -regex ($Token.Trim()) + { $LocalizedDataAzACLTokenPatten.ResourcePermission { $result.type = 'ResourcePermission' diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.ps1 index df4a23050..14534e055 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.ps1 @@ -1,3 +1,27 @@ +<# +.SYNOPSIS + Resolves the ACL token from the provided reference or difference ACL objects. + +.DESCRIPTION + The Resolve-ACLToken function returns the token from the provided ACL objects. + It prefers the token from the DifferenceObject if it is not null, as it contains + the most recent information. If the DifferenceObject is null, it returns the token + from the ReferenceObject. + +.PARAMETER ReferenceObject + The reference ACL object(s) from which the token can be resolved if the DifferenceObject is null. + +.PARAMETER DifferenceObject + The difference ACL object(s) from which the token is preferred if it is not null. + +.EXAMPLE + $referenceACL = @{} + $differenceACL = @{} + $token = Resolve-ACLToken -ReferenceObject $referenceACL -DifferenceObject $differenceACL + +.NOTES + This function is part of the AzureDevOpsDsc module. +#> function Resolve-ACLToken { param ( diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.ps1 index e28c464de..a3f734be1 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.ps1 @@ -105,12 +105,14 @@ Function Test-ACLListforChanges # # Test each of the Reference ACLs - ForEach ($ReferenceACL in $ReferenceACLs) { + ForEach ($ReferenceACL in $ReferenceACLs) + { $acl = $DifferenceACLs | Where-Object { $_.Identity.value.originId -eq $ReferenceACL.Identity.value.originId } # Test if the ACL is not found in the Difference ACL. - if ($null -eq $acl) { + if ($null -eq $acl) + { $result.status = "Changed" $result.propertiesChanged = $ReferenceACLs $result.reason += @{ @@ -121,7 +123,8 @@ Function Test-ACLListforChanges } # Test the inherited flag. - if ($ReferenceACL.isInherited -ne $acl.isInherited) { + if ($ReferenceACL.isInherited -ne $acl.isInherited) + { $result.status = "Changed" $result.propertiesChanged = $ReferenceACLs $result.reason += @{ @@ -133,13 +136,15 @@ Function Test-ACLListforChanges # Iterate through the ACEs and compare them. - ForEach ($ReferenceACE in $ReferenceACL.ACEs) { + ForEach ($ReferenceACE in $ReferenceACL.ACEs) + { # Check if the ACE is found in the Difference ACL. $ace = $DifferenceACLs.ACEs | Where-Object { $_.Identity.value.originId -eq $ReferenceACE.Identity.value.originId } # Check if the ACE is not found in the Difference ACL. - if ($null -eq $ace) { + if ($null -eq $ace) + { $result.status = "Changed" $result.propertiesChanged = $ReferenceACLs $result.reason += @{ @@ -201,12 +206,14 @@ Function Test-ACLListforChanges # # Test each of the Difference ACLs - foreach ($DifferenceACL in $DifferenceACLs) { + foreach ($DifferenceACL in $DifferenceACLs) + { $acl = $ReferenceACLs | Where-Object { $_.Identity.value.originId -eq $DifferenceACL.Identity.value.originId } # Test if the ACL is not found in the Reference ACL. - if ($null -eq $acl) { + if ($null -eq $acl) + { $result.status = "Changed" $result.reason += @{ Value = $DifferenceACL diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.ps1 index c3ce7a41c..3d8d4fe24 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.ps1 @@ -41,10 +41,16 @@ function Test-AzDevOpsApiHttpRequestHeader # if Metadata is specifed within the header then it is a Managed Identity Token request # and is valid. - if ($HttpRequestHeader.Metadata) { return $true } + if ($HttpRequestHeader.Metadata) + { + return $true + } # Otherwise, if the header is not valid, retrun false - if ($null -eq $HttpRequestHeader) { return $false } + if ($null -eq $HttpRequestHeader) + { + return $false + } # If the header is not null, but the Authorization is null, return false if ($HttpRequestHeader.Authorization) diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.ps1 index 04463d9e8..f4ea494e1 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.ps1 @@ -36,6 +36,6 @@ function Test-AzDevOpsApiResourceId $IsValid ) - return !(![guid]::TryParse($ResourceId, $([ref][guid]::Empty))) + } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.ps1 index 998dcfeaf..c435b16b5 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.ps1 @@ -48,4 +48,5 @@ function Test-AzDevOpsApiTimeoutExceeded ) return $($(New-TimeSpan -Start $StartTime -End $EndTime).TotalMilliseconds -gt $TimeoutMs) + } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.ps1 index 7f75451a2..8912e8247 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.ps1 @@ -37,7 +37,10 @@ function Test-AzDevOpsApiUri ) # The APIUri is not mandatory. If it is not provided, the function will return $true. - if ([String]::IsNullOrEmpty($ApiUri)) { return $true } + if ([String]::IsNullOrEmpty($ApiUri)) + { + return $true + } return !(($ApiUri -inotlike 'http://*' -and $ApiUri -inotlike 'https://*') -or diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.ps1 index eb5de524f..97033e047 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.ps1 @@ -119,8 +119,10 @@ function Wait-AzDevOpsApiResource # Wait/Sleep while... # 1) Resource is currently absent but waiting for presence or; # 2) Resource is currently present but waiting for absence - while (($IsPresent -and -not $testAzDevOpsApiResource) -or - ($IsAbsent -and $testAzDevOpsApiResource)) + while ( + ($IsPresent -and -not $testAzDevOpsApiResource) -or + ($IsAbsent -and $testAzDevOpsApiResource) + ) { Start-Sleep -Milliseconds $WaitIntervalMilliseconds @@ -130,8 +132,14 @@ function Wait-AzDevOpsApiResource New-InvalidOperationException -Message $errorMessage } - [bool]$testAzDevOpsApiResource = Test-AzDevOpsApiResource -ApiUri $ApiUri -Pat $Pat ` - -ResourceName $ResourceName ` - -ResourceId $ResourceId + $params = @{ + ApiUri = $ApiUri + Pat = $Pat + ResourceName = $ResourceName + ResourceId = $ResourceId + } + + [bool]$testAzDevOpsApiResource = Test-AzDevOpsApiResource @params + } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 index 0db85f42d..4ec6bbbd3 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 @@ -22,13 +22,14 @@ function ConvertTo-Base64String { [CmdletBinding()] param ( - [Parameter(Mandatory, ValueFromPipeline)] + [Parameter(Mandatory = $true, ValueFromPipeline)] [ValidateNotNullOrEmpty()] [String] $InputObject ) - process { + process + { [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($InputObject)) } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.ps1 index 030044040..cfa3ca5d1 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.ps1 @@ -1,8 +1,40 @@ -Function Find-AzDoIdentity { +<# +.SYNOPSIS + Finds an Azure DevOps identity (user or group) based on the provided identity string. + +.DESCRIPTION + The Find-AzDoIdentity function searches for an Azure DevOps identity (user or group) using the provided identity string. + The identity string can be an email address, a group name, or a display name. The function first checks if the identity + is an email address or a group name and performs the lookup accordingly. If the identity is neither, it performs a lookup + using the display name. + +.PARAMETER Identity + The identity string to search for. This can be an email address, a group name, or a display name. + +.EXAMPLE + PS> Find-AzDoIdentity -Identity "user@domain.com" + Finds the user with the specified email address. + +.EXAMPLE + PS> Find-AzDoIdentity -Identity "Project\GroupName" + Finds the group with the specified name. + +.EXAMPLE + PS> Find-AzDoIdentity -Identity "Display Name" + Finds the user or group with the specified display name. + +.NOTES + The function uses cached user and group data stored in the global variables $Global:AZDOLiveUsers and $Global:AZDOLiveGroups. + If multiple users or groups are found with the same display name, a warning is issued and no result is returned. + If both a user and a group are found with the same display name, a warning is issued and no result is returned. + +#> +Function Find-AzDoIdentity +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$Identity ) @@ -17,7 +49,8 @@ Function Find-AzDoIdentity { $CachedGroups = $Global:AZDOLiveGroups Write-Verbose "[Find-AzDoIdentity] Retrieved cached groups." - switch ($Identity) { + switch ($Identity) + { # Test if the Username contains an '@' symbol. If it does, it is an email address and should be converted to a UPN { $Identity -like '*@*' } { @@ -28,7 +61,8 @@ Function Find-AzDoIdentity { #$cachedItem = $cachedItem | Select-Object *, @{Name = 'Type'; Exp={'Users'}} # Test if the user is found - if ($null -eq $cachedItem) { + if ($null -eq $cachedItem) + { Write-Warning "[Find-AzDoIdentity] No user found with the UPN '$Identity'." return } @@ -42,9 +76,10 @@ Function Find-AzDoIdentity { Write-Verbose "[Find-AzDoIdentity] Identity contains a '\' or '/'; treated as a group." # If the Identity 'Project\GroupName' does not contain square brackets, add them around the Project. - if ($Identity -notmatch '\[.*\]') { + if ($Identity -notmatch '\[.*\]') + { $split = $Identity -split ('\\|\/') - $Identity = "[{0}]\{1}" -f $split[0], $split[1] + $Identity = '[{0}]\{1}' -f $split[0], $split[1] } # Perform a lookup using the existing username @@ -52,7 +87,8 @@ Function Find-AzDoIdentity { $cachedItem = Get-CacheItem -Key $Identity -Type 'LiveGroups' # Test if the group is found - if ($null -eq $cachedItem) { + if ($null -eq $cachedItem) + { Write-Warning "[Find-AzDoIdentity] No group found with the name '$Identity'." return } @@ -80,28 +116,34 @@ Function Find-AzDoIdentity { Write-Verbose "[Find-AzDoIdentity] Found $($User.Count) users and $($Group.Count) groups with the display name '$Identity'." # Test if the user is found - if ($User.Count -gt 1) { + if ($User.Count -gt 1) + { Write-Warning "[Find-AzDoIdentity] Multiple users found with the display name '$Identity'. Please use the UPN (user@domain.com)" return } # Test if a group is found - if ($Group.Count -gt 1) { + if ($Group.Count -gt 1) + { Write-Warning "[Find-AzDoIdentity] Multiple groups found with the display name '$Identity'. Please use the UPN ([project]\[groupname])" return } # Test if both a user and a group are found - if ($User.Count -eq 1 -and $Group.Count -eq 1) { + if ($User.Count -eq 1 -and $Group.Count -eq 1) + { Write-Warning "[Find-AzDoIdentity] Both a user and a group found with the display name '$Identity'. Please use the UPN (user@domain.com or [project]\[groupname])" return } - if ($User.Count -eq 1) { + if ($User.Count -eq 1) + { # If the user is found, add the type and return the user Write-Verbose "[Find-AzDoIdentity] Single user found with the display name '$Identity'." return $User.value - } elseif ($Group.Count -eq 1) { + } + elseif ($Group.Count -eq 1) + { # If the group is found, add the type and return the group Write-Verbose "[Find-AzDoIdentity] Single group found with the display name '$Identity'." return $Group.value diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 index 8cf6a6b3f..a994eca8d 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 @@ -28,16 +28,17 @@ Returns the ACLIdentity object of the identity with the name "JohnDoe" if found. Otherwise, returns null. #> -Function Find-Identity { +Function Find-Identity +{ [CmdletBinding()] param( # The name of the identity to search for. - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, # The name of the organization. - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$OrganizationName, @@ -53,19 +54,22 @@ Function Find-Identity { Write-Verbose "[Find-Identity] Organization Name: $OrganizationName" Write-Verbose "[Find-Identity] Search Type: $SearchType" - try { + try + { $CachedGroups = Get-CacheObject -CacheType 'LiveGroups' $CachedUsers = Get-CacheObject -CacheType 'LiveUsers' $CachedServicePrincipals = Get-CacheObject -CacheType 'LiveServicePrinciples' - } catch { + } + catch + { Write-Error "Failed to retrieve cache objects: $_" return $null } # # Define the lookup table based on the search type - - switch ($SearchType) { + switch ($SearchType) + { 'descriptor' { $lookup = @{ groupIdentitySB = { $_.value.ACLIdentity.descriptor -eq $Name } @@ -115,24 +119,40 @@ Function Find-Identity { $servicePrincipalIdentity = $CachedServicePrincipals | Where-Object $lookup.servicePrincipalIdentitySB # Check if multiple identities were found. - if ($groupIdentity -or $userIdentity -or $servicePrincipalIdentity) { - if (($groupIdentity -and $userIdentity) -or ($groupIdentity -and $servicePrincipalIdentity) -or ($userIdentity -and $servicePrincipalIdentity)) { + if ($groupIdentity -or $userIdentity -or $servicePrincipalIdentity) + { + + if ( + ($groupIdentity -and $userIdentity) -or + ($groupIdentity -and $servicePrincipalIdentity) -or + ($userIdentity -and $servicePrincipalIdentity) + ) + { Write-Warning "[Find-Identity] Found multiple identities with the name '$Name'. Returning null." return $null } - if ($groupIdentity) { + if ($groupIdentity) + { Write-Verbose "[Find-Identity] Found group identity for '$Name'." Write-Verbose "[Find-Identity] $SearchType" return $groupIdentity - } elseif ($userIdentity) { + } + elseif ($userIdentity) + { Write-Verbose "[Find-Identity] Found user identity for '$Name'." return $userIdentity - } elseif ($servicePrincipalIdentity) { + } + elseif ($servicePrincipalIdentity) + { Write-Verbose "[Find-Identity] Found service principal identity for '$Name'." return $servicePrincipalIdentity } - } else { + + } + else + { + Write-Warning "[Find-Identity] No identity found for '$Name'. Performing a lookup via the API." # Perform a lookup via the API @@ -144,10 +164,13 @@ Function Find-Identity { Write-Verbose "[Find-Identity] Performing a lookup via the API." Write-Verbose "[Find-Identity] $SearchType" - try { + try + { # Get the identity $identity = Get-DevOpsDescriptorIdentity @params - } catch { + } + catch + { Write-Error "Failed to retrieve identity via API: $_" return $null } @@ -158,21 +181,31 @@ Function Find-Identity { $servicePrincipalIdentity = $CachedServicePrincipals | Where-Object { $_.value.ACLIdentity.id -eq $identity.id } # Test if the identity was found - if ($groupIdentity -or $userIdentity -or $servicePrincipalIdentity) { - + if ($groupIdentity -or $userIdentity -or $servicePrincipalIdentity) + { # Check if multiple identities were found. - if (($groupIdentity -and $userIdentity) -or ($groupIdentity -and $servicePrincipalIdentity) -or ($userIdentity -and $servicePrincipalIdentity)) { + if ( + ($groupIdentity -and $userIdentity) -or + ($groupIdentity -and $servicePrincipalIdentity) -or + ($userIdentity -and $servicePrincipalIdentity) + ) + { Write-Warning "[Find-Identity] Found multiple identities with the ID '$($identity.id)'. Returning null." return $null } - if ($groupIdentity) { + if ($groupIdentity) + { Write-Verbose "[Find-Identity] Found group identity for '$Name'." return $groupIdentity - } elseif ($userIdentity) { + } + elseif ($userIdentity) + { Write-Verbose "[Find-Identity] Found user identity for '$Name'." return $userIdentity - } elseif ($servicePrincipalIdentity) { + } + elseif ($servicePrincipalIdentity) + { Write-Verbose "[Find-Identity] Found service principal identity for '$Name'." return $servicePrincipalIdentity } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.ps1 index d15898f7e..a1a37aefa 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.ps1 @@ -17,15 +17,16 @@ Returns: "[Contoso]\Developers" #> -Function Format-AzDoGroup { +Function Format-AzDoGroup +{ [CmdletBinding()] param( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('ProjectName', 'Organization')] [string] $Prefix, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String] $GroupName ) @@ -41,4 +42,5 @@ Function Format-AzDoGroup { Write-Verbose "[Format-AzDoGroup] Resulting User Principal Name: '$userPrincipalName'." return $userPrincipalName + } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroupMember.ps1 index 236d7e061..8df52222f 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroupMember.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroupMember.ps1 @@ -1,7 +1,23 @@ +<# +.SYNOPSIS +Formats an Azure DevOps group member name by removing square brackets and reformatting the string. + +.PARAMETER GroupName +The name of the group to be formatted. This parameter is mandatory. + +.RETURNS +System.String +A formatted string representing the group name in the format '[prefix]\group'. + +.EXAMPLE +$formattedName = Format-AzDoGroupMember -GroupName '[prefix]\group' +# This will return 'prefix\group'. + +#> Function Format-AzDoGroupMember { param( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [System.String]$GroupName ) @@ -13,6 +29,6 @@ Function Format-AzDoGroupMember # Split the GroupName into the prefix and the group name. $prefix, $group = $GroupName -split '\\' - return "[{0}]\{1}" -f $prefix, $group + return '[{0}]\{1}' -f $prefix, $group } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.ps1 index 55248da33..5bcb86d5e 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.ps1 @@ -1,12 +1,43 @@ -Function Format-AzDoProjectName { +<# +.SYNOPSIS +Formats the Azure DevOps project name. + +.DESCRIPTION +This function formats the Azure DevOps project name by ensuring it follows the correct format. +If the group name is already in the format '[Project|Organization]\GroupName', it returns the group name as is. +Otherwise, it splits the group name and ensures it has the correct format. + +.PARAMETER GroupName +The name of the group. This parameter is mandatory. + +.PARAMETER OrganizationName +The name of the organization. This parameter is mandatory. + +.EXAMPLE +PS> Format-AzDoProjectName -GroupName 'Project\Group' -OrganizationName 'MyOrg' +[Project]\Group + +.EXAMPLE +PS> Format-AzDoProjectName -GroupName '%ORG%\Group' -OrganizationName 'MyOrg' +[MyOrg]\Group + +.EXAMPLE +PS> Format-AzDoProjectName -GroupName '%TFS%\Group' -OrganizationName 'MyOrg' +[TEAM FOUNDATION]\Group + +.NOTES +If the group name is not in the correct format, an error is thrown. +#> +Function Format-AzDoProjectName +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$GroupName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Organization')] [System.String]$OrganizationName ) @@ -15,7 +46,8 @@ Function Format-AzDoProjectName { Write-Verbose "[Format-AzDoProjectName] Formatting GroupName." # If the GroupName contains [Project|Organization]\GroupName, it's in the correct format. - if ($GroupName -match '^\[.*\]\\.*$') { + if ($GroupName -match '^\[.*\]\\.*$') + { return $GroupName } @@ -23,20 +55,25 @@ Function Format-AzDoProjectName { $splitGroupName = $GroupName -split '\\|\/' # There must be at least 2 elements in the array. The first element is the project name and the second element is the group name. - if ($splitGroupName.Length -lt 2) { + if ($splitGroupName.Length -lt 2) + { Throw "The GroupName '$GroupName' is not in the correct format. The GroupName must be in the format 'ProjectName\GroupName' or 'Project/GroupName." } # If the first element contains '%ORG%' or is empty, replace it with the organization name. - if ($splitGroupName[0] -eq '%ORG%' -or [String]::IsNullOrEmpty($splitGroupName[0].Trim())) { + if ($splitGroupName[0] -eq '%ORG%' -or [String]::IsNullOrEmpty($splitGroupName[0].Trim())) + { $splitGroupName[0] = $OrganizationName - # If the first element contains '%TFS%', replace it with 'TEAM FOUNDATION'. - } elseif ($splitGroupName[0].Trim() -eq '%TFS%') { + } + elseif ($splitGroupName[0].Trim() -eq '%TFS%') + { + # If the first element contains '%TFS%', replace it with 'TEAM FOUNDATION'. $splitGroupName[0] = 'TEAM FOUNDATION' } # The group name cannot be null. - if ([String]::IsNullOrEmpty($splitGroupName[1].Trim())) { + if ([String]::IsNullOrEmpty($splitGroupName[1].Trim())) + { Throw "The GroupName '$GroupName' is not in the correct format. The GroupName must be in the format 'ProjectName\GroupName'." } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.ps1 index 0909fcb55..d3ab71669 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.ps1 @@ -1,14 +1,39 @@ -Function Format-DescriptorType { +<# +.SYNOPSIS +Formats the descriptor type to match the expected naming conventions. +.DESCRIPTION +This function takes a descriptor type as input and returns a formatted string. +If the descriptor type is "GitRepositories", it returns "Git Repositories". +For all other descriptor types, it returns the input descriptor type unchanged. + +.PARAMETER DescriptorType +The descriptor type to be formatted. This parameter is mandatory. + +.OUTPUTS +System.String +The formatted descriptor type. + +.EXAMPLE +PS C:\> Format-DescriptorType -DescriptorType "GitRepositories" +Git Repositories + +.EXAMPLE +PS C:\> Format-DescriptorType -DescriptorType "OtherType" +OtherType +#> +Function Format-DescriptorType +{ [CmdletBinding()] [OutputType([String])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [System.String]$DescriptorType ) # Switch on the DescriptorType - switch ($DescriptorType) { + switch ($DescriptorType) + { # The Descriptor Name in the API is different to the Descriptor Name in the DSC Resource. "GitRepositories" { @@ -20,5 +45,4 @@ Function Format-DescriptorType { return $DescriptorType } } - } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceUri.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceUri.ps1 index afd7515ed..9025ebdcc 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceUri.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceUri.ps1 @@ -59,7 +59,6 @@ function Get-AzDevOpsApiResourceUri [string]$apiResourceUri = $ApiUri - # Obtain URI-specific names relating to $ResourceName [string]$apiUriResourceAreaName = Get-AzDevOpsApiUriAreaName -ResourceName $ResourceName [string]$apiUriResourceName = Get-AzDevOpsApiUriResourceName -ResourceName $ResourceName @@ -71,18 +70,15 @@ function Get-AzDevOpsApiResourceUri $apiResourceUri = $apiResourceUri + "$apiUriResourceAreaName/" } - # Append the URI-specific, 'ResourceName' of the 'Resource' onto the URI $apiResourceUri = $apiResourceUri + "$apiUriResourceName/" - # Append the identifier of the resource, if provided if (![System.String]::IsNullOrWhiteSpace($ResourceId)) { $apiResourceUri = $apiResourceUri + "$ResourceId/" } - # Append any parameters to the URI $apiResourceUriParameters = @{ "api-version" = $ApiVersion # Taken from input parameter @@ -93,11 +89,9 @@ function Get-AzDevOpsApiResourceUri $apiResourceUri = $apiResourceUri + '?' $apiResourceUriParameters.Keys | ForEach-Object { - $apiResourceUri = $apiResourceUri + '&' + $_ + '=' + $apiResourceUriParameters[$_] } $apiResourceUri = $apiResourceUri.Replace('/?&','?') # Tidy up the end of base URI where initial parameter begins - return $apiResourceUri } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.ps1 index ff3398e3b..c50469b2b 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.ps1 @@ -1,3 +1,19 @@ +<# +.SYNOPSIS +Retrieves a list of Azure DevOps cache object types. + +.DESCRIPTION +The Get-AzDoCacheObjects function returns an array of strings representing different types of cache objects used in Azure DevOps. + +.OUTPUTS +String[] +An array of strings representing the cache object types. + +.EXAMPLE +PS> Get-AzDoCacheObjects +This command retrieves the list of Azure DevOps cache object types. + +#> function Get-AzDoCacheObjects { return @( diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 index a30fb82f1..0d2ebf848 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 @@ -110,8 +110,8 @@ function Invoke-AzDevOpsApiRestMethod ResponseHeadersVariable = 'responseHeaders' } - Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] Invoking the Azure DevOps API REST method '{0}'." -f $HttpMethod) - Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] API URI: {0}" -f $ApiUri) + Write-Verbose -Message ('[Invoke-AzDevOpsApiRestMethod] Invoking the Azure DevOps API REST method {0}' -f $HttpMethod) + Write-Verbose -Message ('[Invoke-AzDevOpsApiRestMethod] API URI: {0}' -f $ApiUri) # Remove the 'Body' and 'ContentType' if not relevant to request if ($HttpMethod -in $('Get','Delete')) @@ -128,14 +128,14 @@ function Invoke-AzDevOpsApiRestMethod while ($CurrentNoOfRetryAttempts -lt $RetryAttempts) { + <# + Slow down the retry attempts if the API resource is close to being overwelmed + If there are any retry attempts, wait for the specified number of seconds before retrying + #> - # - # Slow down the retry attempts if the API resource is close to being overwelmed - - # If there are any retry attempts, wait for the specified number of seconds before retrying if (($null -ne $Global:DSCAZDO_APIRateLimit.xRateLimitRemaining) -and ($Global:DSCAZDO_APIRateLimit.retryAfter -ge 0)) { - Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] Waiting for {0} seconds before retrying." -f $Global:DSCAZDO_APIRateLimit.retryAfter) + Write-Verbose -Message ('[Invoke-AzDevOpsApiRestMethod] Waiting for {0} seconds before retrying.' -f $Global:DSCAZDO_APIRateLimit.retryAfter) Start-Sleep -Seconds $Global:DSCAZDO_APIRateLimit.retryAfter } @@ -148,15 +148,15 @@ function Invoke-AzDevOpsApiRestMethod # If the API resouce is overwelmed, wait for the specified number of seconds before sending the request elseif (($null -ne $Global:DSCAZDO_APIRateLimit.xRateLimitRemaining) -and ($Global:DSCAZDO_APIRateLimit.xRateLimitRemaining -lt 5)) { - Write-Verbose -Message ("[Invoke-AzDevOpsApiRestMethod] Resource is overwhelmed. Waiting for {0} seconds to reset the TSTUs." -f $Global:DSCAZDO_APIRateLimit.xRateLimitReset) + Write-Verbose -Message ('[Invoke-AzDevOpsApiRestMethod] Resource is overwhelmed. Waiting for {0} seconds to reset the TSTUs.' -f $Global:DSCAZDO_APIRateLimit.xRateLimitReset) Start-Sleep -Milliseconds $RetryIntervalMs } # # Invoke the REST method. Loop until the Continuation Token is False. - Do { - + Do + { # # Add the Authentication Header @@ -230,7 +230,9 @@ function Invoke-AzDevOpsApiRestMethod $retryAfter = [int]$retryAfter Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $retryAfter seconds before retrying." $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($retryAfter) - } else { + } + else + { # If the Retry-After header is not present, wait for the specified number of milliseconds before retrying Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $RetryIntervalMs milliseconds before retrying." $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($RetryIntervalMs) @@ -247,7 +249,6 @@ function Invoke-AzDevOpsApiRestMethod # Break the continuation token loop so that the next attempt can be made break; - } } Until (-not $isContinuationToken) diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt index 7b14711a7..a907109d2 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt @@ -21,7 +21,8 @@ Function Initialize-Log { } # Ensure the log directory exists - if (-not (Test-Path -Path $LogDirectory)) { + if (-not (Test-Path -Path $LogDirectory)) + { Write-Verbose "[Initialize-Log] Log directory does not exist. Creating directory." New-Item -ItemType Directory -Path $LogDirectory -Force | Out-Null } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 index 7778dc39a..e80f9072c 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 @@ -1,4 +1,32 @@ -Function Write-Error { +<# +.SYNOPSIS +Logs an error message to a specified log file and displays the error message. + +.DESCRIPTION +The Write-Error function logs an error message to a specified log file and displays the error message using the original Write-Error cmdlet. +It appends the error message with a timestamp to the log file. + +.PARAMETER Message +The error message to be logged and displayed. This parameter is mandatory. + +.PARAMETER LogFilePath +The path to the log file where the error message will be appended. The default path is "C:\Temp\error_log.txt". + +.EXAMPLE +Write-Error -Message "An unexpected error occurred." + +This example logs the error message "An unexpected error occurred." to the default log file and displays the error message. + +.EXAMPLE +Write-Error -Message "An unexpected error occurred." -LogFilePath "C:\Logs\custom_error_log.txt" + +This example logs the error message "An unexpected error occurred." to the specified log file "C:\Logs\custom_error_log.txt" and displays the error message. + +.NOTES +The function uses the original Write-Error cmdlet from the Microsoft.PowerShell.Utility module to display the error message. +#> +Function Write-Error +{ [CmdletBinding()] param( [Parameter(Mandatory = $true)] diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 index 399fa23ae..265740b52 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 @@ -1,4 +1,27 @@ -Function Write-Verbose { +<# +.SYNOPSIS +Writes a verbose message to the console and optionally logs it to a file. + +.DESCRIPTION +The Write-Verbose function writes a verbose message to the console using the built-in Write-Verbose cmdlet. +specified by the LogFilePath parameter. + +.PARAMETER Message +The verbose message to be written to the console and optionally logged to a file. + +.PARAMETER LogFilePath +The path to the log file where the verbose message will be appended. If not specified, the value of the + +.EXAMPLE +Write-Verbose -Message "This is a verbose message." + +This command writes the message "This is a verbose message." to the console and, if the environment variable + +.NOTES +The function uses the built-in Write-Verbose cmdlet to write messages to the console. It also checks if the +#> +Function Write-Verbose +{ [CmdletBinding()] param( [Parameter(Mandatory = $true)] @@ -12,7 +35,8 @@ Function Write-Verbose { Microsoft.PowerShell.Utility\Write-Verbose $Message # Test if the env:enableVerboseLogging variable is set to true - if ($null -ne $env:AZDO_VERBOSELOGGING_FILEPATH) { + if ($null -ne $env:AZDO_VERBOSELOGGING_FILEPATH) + { # Append the message to the log file $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" Add-Content -Path $LogFilePath -Value "[$timestamp] $Message" diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 index d5e37b13e..9d53b5e21 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 @@ -1,4 +1,31 @@ -Function Write-Warning { +<# +.SYNOPSIS +Writes a warning message to the console and appends it to a log file. + +.DESCRIPTION +The Write-Warning function writes a warning message to the console using the original Write-Verbose cmdlet if the verbose preference is enabled. It also appends the warning message to a specified log file with a timestamp. + +.PARAMETER Message +The warning message to be written to the console and log file. This parameter is mandatory. + +.PARAMETER LogFilePath +The path to the log file where the warning message will be appended. The default path is "C:\Temp\warning_log.txt". + +.EXAMPLE +Write-Warning -Message "This is a warning message." + +This example writes the warning message "This is a warning message." to the console and appends it to the default log file. + +.EXAMPLE +Write-Warning -Message "This is a warning message." -LogFilePath "C:\Logs\custom_warning_log.txt" + +This example writes the warning message "This is a warning message." to the console and appends it to the specified log file "C:\Logs\custom_warning_log.txt". + +.NOTES +The function temporarily sets the $VerbosePreference to 'Continue' to ensure the warning message is displayed if verbose preference is enabled, and then restores the original preference. +#> +Function Write-Warning +{ [CmdletBinding()] param( [Parameter(Mandatory = $true)] diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt index 1918a6a3b..23295de1a 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt @@ -4,11 +4,11 @@ Function Write-Log param ( # The message to be logged. - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$Message, # The type of log message. - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateSet('Verbose', 'Warning')] [string]$Type ) diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.ps1 index fd53be055..0c77d46de 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.ps1 @@ -24,29 +24,35 @@ New-AzDevOpsACLToken -OrganizationName "Contoso" -ProjectId "MyProject" Creates a token for project-level access to the specified Azure DevOps project. #> -function New-AzDevOpsACLToken { +function New-AzDevOpsACLToken +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$OrganizationName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$ProjectId, [Parameter()] [string]$TeamId ) - process { - if ($TeamId) { + process + { + if ($TeamId) + { # Construct a token for team-level access $token = "vstfs:///Classification/TeamProject/$ProjectId/$TeamId" - } else { + } + else + { # Construct a token for project-level access $token = "vstfs:///Classification/TeamProject/$ProjectId" } return $token + } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.ps1 index 810b3691d..5aba84d0e 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-InvalidOperationException.ps1 @@ -1,11 +1,36 @@ +<# +.SYNOPSIS +Creates a new InvalidOperationException error record. + +.DESCRIPTION +The New-InvalidOperationException function generates a new ErrorRecord object for an InvalidOperationException with a specified message. Optionally, it can throw the error. + +.PARAMETER Message +The message that describes the error. This parameter is mandatory and cannot be null or empty. + +.PARAMETER Throw +A switch parameter that, if specified, will throw the generated ErrorRecord instead of returning it. + +.OUTPUTS +System.Management.Automation.ErrorRecord + +.EXAMPLE +PS> New-InvalidOperationException -Message "An invalid operation occurred." + +Creates and returns an ErrorRecord for an InvalidOperationException with the specified message. + +.EXAMPLE +PS> New-InvalidOperationException -Message "An invalid operation occurred." -Throw + +Creates and throws an ErrorRecord for an InvalidOperationException with the specified message. +#> using namespace System.Management.Automation -function New-InvalidOperationException { +function New-InvalidOperationException +{ [CmdletBinding()] [OutputType([System.Management.Automation.ErrorRecord])] - - ## PARAMETERS ############################################################# - param( + param ( [Parameter( Position = 0, Mandatory @@ -18,9 +43,8 @@ function New-InvalidOperationException { [switch] $Throw ) - - ## PROCESS ################################################################ - process { + process + { $ErrorRecord = [ErrorRecord]::new( [InvalidOperationException]::new($Message), "System.InvalidOperationException", @@ -28,10 +52,12 @@ function New-InvalidOperationException { $null ) - if ($Throw) { + if ($Throw) + { throw $ErrorRecord } Write-Output $ErrorRecord + } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 index 184b8c455..ff7fd85cd 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 @@ -1,7 +1,29 @@ -Function New-Thread { +<# +.SYNOPSIS +Creates and starts a new thread to run the specified script block. + +.DESCRIPTION +The New-Thread function creates a new thread using the provided script block and starts it. +This can be useful for running tasks concurrently. + +.PARAMETER ScriptBlock +The script block to be executed in the new thread. This parameter is mandatory. + +.EXAMPLE +$scriptBlock = { + # Your code here +} +$thread = New-Thread -ScriptBlock $scriptBlock + +.NOTES +Author: Your Name +Date: YYYY-MM-DD +#> +Function New-Thread +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ScriptBlock]$ScriptBlock ) diff --git a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 index 3720b5b27..cf8dfccdd 100644 --- a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 +++ b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psd1 @@ -40,48 +40,48 @@ # # DSC Class Based Resources - 'Get-xAzDoProject', - 'New-xAzDoProject', - 'Set-xAzDoProject', - 'Remove-xAzDoProject', - 'Test-xAzDoProject', - - 'Get-xAzDoProjectGroup', - 'New-xAzDoProjectGroup', - 'Set-xAzDoProjectGroup', - 'Remove-xAzDoProjectGroup', - 'Test-xAzDoProjectGroup', - - 'Get-xAzDoOrganizationGroup', - 'New-xAzDoOrganizationGroup', - 'Set-xAzDoOrganizationGroup', - 'Remove-xAzDoOrganizationGroup', - 'Test-xAzDoOrganizationGroup', - - 'Get-xAzDoGroupMember', - 'New-xAzDoGroupMember', - 'Set-xAzDoGroupMember', - 'Remove-xAzDoGroupMember', - 'Test-xAzDoGroupMember' - - 'Get-xAzDoGitRepository', - 'New-xAzDoGitRepository', - 'Remove-xAzDoGitRepository', - - 'Get-xAzDoGitPermission', - 'New-xAzDoGitPermission', - 'Remove-xAzDoGitPermission', - 'Set-xAzDoGitPermission', - - 'Get-xAzDoGroupPermission', - 'New-xAzDoGroupPermission', - 'Remove-xAzDoGroupPermission', - 'Set-xAzDoGroupPermission', - - 'Get-xAzDoProjectServices', - 'Set-xAzDoProjectServices', - 'Test-xAzDoProjectServices', - 'Remove-xAzDoProjectServices' + 'Get-AzDoProject', + 'New-AzDoProject', + 'Set-AzDoProject', + 'Remove-AzDoProject', + 'Test-AzDoProject', + + 'Get-AzDoProjectGroup', + 'New-AzDoProjectGroup', + 'Set-AzDoProjectGroup', + 'Remove-AzDoProjectGroup', + 'Test-AzDoProjectGroup', + + 'Get-AzDoOrganizationGroup', + 'New-AzDoOrganizationGroup', + 'Set-AzDoOrganizationGroup', + 'Remove-AzDoOrganizationGroup', + 'Test-AzDoOrganizationGroup', + + 'Get-AzDoGroupMember', + 'New-AzDoGroupMember', + 'Set-AzDoGroupMember', + 'Remove-AzDoGroupMember', + 'Test-AzDoGroupMember' + + 'Get-AzDoGitRepository', + 'New-AzDoGitRepository', + 'Remove-AzDoGitRepository', + + 'Get-AzDoGitPermission', + 'New-AzDoGitPermission', + 'Remove-AzDoGitPermission', + 'Set-AzDoGitPermission', + + 'Get-AzDoGroupPermission', + 'New-AzDoGroupPermission', + 'Remove-AzDoGroupPermission', + 'Set-AzDoGroupPermission', + + 'Get-AzDoProjectServices', + 'Set-AzDoProjectServices', + 'Test-AzDoProjectServices', + 'Remove-AzDoProjectServices' ) diff --git a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 index 153f2d98d..91d71242c 100644 --- a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 +++ b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 @@ -77,7 +77,10 @@ Export-ModuleMember -Function 'Get-AzDoAPIGroupCache' Export-ModuleMember -Function 'Get-AzDoAPIProjectCache' Export-ModuleMember -Function 'Initialize-CacheObject' Export-ModuleMember -Function 'Get-AzDoCacheObjects' -Export-ModuleMember -Function '*-xAzDoProjectGroup' +Export-ModuleMember -Function '*-AzDoProjectGroup' # Stop processing -if ($isClass) { return } +if ($isClass) +{ + return +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzDevOpsPatCredential.ps1 b/source/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzDevOpsPatCredential.ps1 index 293087ae8..d5a111482 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzDevOpsPatCredential.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Test-AzDevOpsPatCredential.ps1 @@ -37,6 +37,6 @@ function Test-AzDevOpsPatCredential $IsValid ) - return !($null -eq $PatCredential -or - 'PAT' -ne $PatCredential.UserName) + return (-not($null -eq $PatCredential -or + 'PAT' -ne $PatCredential.UserName)) } diff --git a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 index 176776083..75bcbca21 100644 --- a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 @@ -1,28 +1,35 @@ -data LocalizedDataAzACLTokenPatten -{ -@' - -# -# Git ACL Token Patterns -# +<# +.SYNOPSIS + Contains localized data for Azure DevOps ACL token patterns. -# Organizational Level Token -OrganizationGit = ^repoV2$ -# Project-Level Git Repository Token -GitProject = \^\(repoV2\)\\/\(\?\[A-Za-z0-9-]\+\)\$ -# Git Repository Token -GitRepository = \^\(repoV2\)\\/\(\?\[A-Za-z0-9-]\+\)\\/\(\?\[A-Za-z0-9-]\+\)\$ -# Git Branch Token -GitBranch = \^\(repoV2\)\\/\(\?\[A-Za-z0-9-]\+\)\\/\(\?\[A-Za-z0-9-]\+\)\\/refs\\/heads\\/\(\?\[A-Za-z0-9]\+\)\$ +.DESCRIPTION + This data section defines various regular expression patterns used for matching Azure DevOps ACL tokens. + These patterns are used to identify and extract information from different components such as organizations, + projects, repositories, branches, groups, and resources within Azure DevOps. -# -# Identity ACL Token Patterns -# +.KEYWORDS + Azure DevOps, ACL, Token Patterns, Regular Expressions -# Group Permission Token -GroupPermission = ^(?[A-Za-z0-9-_]+)\\\\(?[A-Za-z0-9-_]+)$ -# Resource (Project or Identity) Permission -ResourcePermission = \^\(\?\[A-Za-z0-9-_]\+\)\$ +.EXAMPLES + The patterns can be used to match and extract information from ACL tokens in Azure DevOps: -'@ | ConvertFrom-StringData + - OrganizationGit: Matches the organization token. + - GitProject: Matches the project token and extracts the ProjectId. + - GitRepository: Matches the repository token and extracts the ProjectId and RepoId. + - GitBranch: Matches the branch token and extracts the ProjectId, RepoId, and BranchName. + - GroupPermission: Matches the group permission token and extracts the ProjectId and GroupId. + - ResourcePermission: Matches the resource permission token and extracts the ProjectId. +#> +data LocalizedDataAzACLTokenPatten +{ + @{ + # Git ACL Token Patterns + OrganizationGit = '^repoV2$' + GitProject = '^\(repoV2\)\\/\(\?[A-Za-z0-9-]+\)$' + GitRepository = '^\(repoV2\)\\/\(\?[A-Za-z0-9-]+\)\\/\(\?[A-Za-z0-9-]+\)$' + GitBranch = '^\(repoV2\)\\/\(\?[A-Za-z0-9-]+\)\\/\(\?[A-Za-z0-9-]+\)\\/refs/heads/\(\?[A-Za-z0-9]+\)$' + # Identity ACL Token Patterns + GroupPermission = '^(?[A-Za-z0-9-_]+)\\(?[A-Za-z0-9-_]+)$' + ResourcePermission = '^\(\?[A-Za-z0-9-_]+\)$' + } } diff --git a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/001.LocalizedDataAzResourceTokenPatten.ps1 b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/001.LocalizedDataAzResourceTokenPatten.ps1 index 4b448dfb5..8c3788687 100644 --- a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/001.LocalizedDataAzResourceTokenPatten.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/001.LocalizedDataAzResourceTokenPatten.ps1 @@ -1,26 +1,39 @@ -data LocalizedDataAzResourceTokenPatten -{ -@' +<# +.SYNOPSIS + Contains localized data for Azure DevOps resource token patterns. -# -# Git ACL Token Patterns -# +.DESCRIPTION + This data section defines various regular expression patterns used for matching Azure DevOps resource tokens. + These patterns are used to identify and extract information from different Azure DevOps resources such as organizations, projects, repositories, and permissions. -# Organizational Level Token -OrganizationGit = \^azdoorg\$ -# Project-Level Git Repository Token -GitProject = \^\(\?\[A-Za-z0-9-]\+\)\$ -# Git Repository Token -GitRepository = \(\?\[A-Za-z0-9-_]\+\)\(\\/\|\\\\\)\(\?\[A-Za-z0-9-_]\+\) +.KEYWORDS + Azure DevOps, Regular Expressions, Token Patterns, Localization -# -# Identity Permissions +.NOTES + Filepath: /c:/Git/AzureDevOpsDsc/source/Modules/AzureDevOpsDsc.Common/LocalizedData/001.LocalizedDataAzResourceTokenPatten.ps1 -# Group Permission -GroupPermission = \^\(\?\[A-Za-z0-9-_]\+\)\\\\(\?\[A-Za-z0-9-_]\+\)\$ +.EXAMPLES + # Example usage of the data section + $localizedData = LocalizedDataAzResourceTokenPatten + $orgPattern = $localizedData.OrganizationGit + $projectPattern = $localizedData.GitProject + $repoPattern = $localizedData.GitRepository + $groupPermissionPattern = $localizedData.GroupPermission + $resourcePermissionPattern = $localizedData.ResourcePermission + $projectPermissionPattern = $localizedData.ProjectPermission +#> -# Project Permissions -ProjectPermission = \^\\\$PROJECT:vstfs:\\/\{3}Classification\\/TeamProject\\/\(\?\[A-Za-z0-9-_]\+\)\$ +data LocalizedDataAzResourceTokenPatten +{ + @{ + # Git ACL Token Patterns + OrganizationGit = '^azdoorg$' + GitProject = '^\(repoV2\)\\/\(\?[A-Za-z0-9-]+\)$' + GitRepository = '(?[A-Za-z0-9-_]+)(\/|\\)(?[A-Za-z0-9-_]+)' + # Identity ACL Token Patterns + GroupPermission = '^(?[A-Za-z0-9-_]+)\\(?[A-Za-z0-9-_]+)$' + ResourcePermission = '^\(\?[A-Za-z0-9-_]+\)$' + ProjectPermission = '^\$PROJECT:vstfs:\/{3}Classification\/TeamProject\/(?[A-Za-z0-9-_]+)$' + } -'@ | ConvertFrom-StringData } diff --git a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/002.LocalizedDataAzSerializationPatten.ps1 b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/002.LocalizedDataAzSerializationPatten.ps1 index ca029f736..295445377 100644 --- a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/002.LocalizedDataAzSerializationPatten.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/002.LocalizedDataAzSerializationPatten.ps1 @@ -1,19 +1,39 @@ -data LocalizedDataAzSerializationPatten -{ -@' +<# +.SYNOPSIS + Contains localized data patterns for Azure DevOps serialization. + +.DESCRIPTION + This data section defines regular expression patterns used for matching various Azure DevOps entities such as Git repositories, group permissions, and project permissions. + +.NOTES -# Git Repository ACL Token Patterns. Exclude the refs token since these are branch level ACLs. -# Example: repoV2/ProjectId/RepoId -# Not: repoV2/ProjectId/RepoId/refs/heads/BranchName -GitRepository = \^repoV2\\/\{0}\\/\(\?!\.\*\\/refs\)\.\* +.DATA + GitRepository + Pattern to match Git repository ACL tokens, excluding branch level ACLs. + Example: repoV2/ProjectId/RepoId + Excludes: repoV2/ProjectId/RepoId/refs/heads/BranchName -# Group Permissions -# Example: 78a5065f-3043-426f-9cc5-785748b18f9d\\242ea4ca-e150-4499-a491-00f4ce1f480e -GroupPermission = \^\{0}\\\\\\\\\{1}\$ -# -# Project Permissions -# Example: $PROJECT:vstfs:///Classification/TeamProject/78a5065f-3043-426f-9cc5-785748b18f9d -ProjectPermission = \^\\\$PROJECT:vstfs:\\/\{3}Classification\\/TeamProject\\/\{0}\$ + GroupPermission + Pattern to match group permissions. + Example: 78a5065f-3043-426f-9cc5-785748b18f9d\\242ea4ca-e150-4499-a491-00f4ce1f480e + + ProjectPermission + Pattern to match project permissions. + Example: $PROJECT:vstfs:///Classification/TeamProject/78a5065f-3043-426f-9cc5-785748b18f9d +#> +data LocalizedDataAzSerializationPatten +{ + @{ + # Git Repository ACL Token Patterns. Exclude the refs token since these are branch level ACLs. + # Example: repoV2/ProjectId/RepoId + # Not: repoV2/ProjectId/RepoId/refs/heads/BranchName + GitRepository = '^repoV2\/{0}\/(?!.*\/refs).*' + # Group Permissions + # Example: 78a5065f-3043-426f-9cc5-785748b18f9d\\242ea4ca-e150-4499-a491-00f4ce1f480e + GroupPermission = '^{0}\\\\{1}$' + # Project Permissions + # Example: $PROJECT:vstfs:///Classification/TeamProject/78a5065f-3043-426f-9cc5-785748b18f9d + ProjectPermission = '^\$PROJECT:vstfs:\/{3}Classification\/TeamProject\/{0}$' + } -'@ | ConvertFrom-StringData } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOrganizationName.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOrganizationName.ps1 index fb8aa0e17..ebb86965c 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOrganizationName.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsOrganizationName.ps1 @@ -36,10 +36,13 @@ function Test-AzDevOpsOrganizationName $IsValid ) - return !([System.String]::IsNullOrWhiteSpace($OrganizationName) -or - ($OrganizationName.Contains(' ') -or - $OrganizationName.Contains('%') -or - $OrganizationName.Contains('*') -or - $OrganizationName.StartsWith(' ') -or - $OrganizationName.EndsWith(' '))) + return (-not([System.String]::IsNullOrWhiteSpace($OrganizationName)) -or + ( + $OrganizationName.Contains(' ') -or + $OrganizationName.Contains('%') -or + $OrganizationName.Contains('*') -or + $OrganizationName.StartsWith(' ') -or + $OrganizationName.EndsWith(' ') + ) + ) } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.ps1 index bf5b7a785..74f6d6be8 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsPat.ps1 @@ -38,8 +38,10 @@ function Test-AzDevOpsPat # If the Pat token is blank it means that managed identity is being used. # In this case, the function will return $true. - if ([System.String]::IsNullOrWhiteSpace($Pat)) { return $true } + if ([System.String]::IsNullOrWhiteSpace($Pat)) + { + return $true + } - return !([System.String]::IsNullOrWhiteSpace($Pat) -or - $Pat.Length -ne 52) # Note: 52 is the current/expected length of PAT + return (-not([System.String]::IsNullOrWhiteSpace($Pat)) -or $Pat.Length -ne 52) # Note: 52 is the current/expected length of PAT } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectDescription.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectDescription.ps1 index d393daff2..70e13d22d 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectDescription.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectDescription.ps1 @@ -37,9 +37,13 @@ function Test-AzDevOpsProjectDescription $IsValid ) - return !($ProjectDescription -eq $null -or - ($ProjectDescription.Contains('%') -or - $ProjectDescription.Contains('*') -or - $ProjectDescription.StartsWith(' ') -or - $ProjectDescription.EndsWith(' '))) + return ( + [String]::IsNullOrWhiteSpace($ProjectDescription) -or + ( + $ProjectDescription.Contains('%') -or + $ProjectDescription.Contains('*') -or + $ProjectDescription.StartsWith(' ') -or + $ProjectDescription.EndsWith(' ') + ) + ) } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectName.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectName.ps1 index b9991873c..feb429f89 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectName.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-AzDevOpsProjectName.ps1 @@ -40,9 +40,9 @@ function Test-AzDevOpsProjectName $AllowWildcard ) - return !([System.String]::IsNullOrWhiteSpace($ProjectName) -or + return -not([System.String]::IsNullOrWhiteSpace($ProjectName) -or ($ProjectName.Contains('%') -or - (!$AllowWildcard -and $ProjectName.Contains('*')) -or + (-not($AllowWildcard) -and $ProjectName.Contains('*')) -or $ProjectName.StartsWith(' ') -or $ProjectName.EndsWith(' '))) } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-ObjectProperty.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-ObjectProperty.ps1 index 8f8966625..2808f2c35 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-ObjectProperty.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Test-ObjectProperty.ps1 @@ -28,30 +28,34 @@ Test-ObjectProperty -Object $object -PropertyName "Email" # Returns $false #> -Function Test-ObjectProperty { +Function Test-ObjectProperty +{ [CmdletBinding()] [OutputType([System.Boolean])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [System.Object] $Object, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [System.String] $PropertyName ) # If the object is a hashtable, check if the key exists - if ($Object -is [System.Collections.Hashtable]) { + if ($Object -is [System.Collections.Hashtable]) + { return $Object.ContainsKey($PropertyName) } # If the object is a PSCustomObject, check if the property exists - elseif ($Object -is [PSCustomObject]) { + elseif ($Object -is [PSCustomObject]) + { return $Object.PSObject.Properties.Name -contains $PropertyName } # If the object is a PSObject, check if the property exists - elseif ($Object -is [PSObject]) { + elseif ($Object -is [PSObject]) + { return $Object.PSObject.Properties.Name -contains $PropertyName } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Wait-AzDevOpsOperation.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Wait-AzDevOpsOperation.ps1 index 1fa4916bf..35c978c02 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Wait-AzDevOpsOperation.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Private/Wait-AzDevOpsOperation.ps1 @@ -109,7 +109,7 @@ function Wait-AzDevOpsOperation $testOperationParameters.IsSuccessful = $IsSuccessful } - while (!(Test-AzDevOpsOperation @testOperationParameters)) + while (-not(Test-AzDevOpsOperation @testOperationParameters)) { Start-Sleep -Milliseconds $WaitIntervalMilliseconds diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Get-AzDoGitPermission.ps1 similarity index 50% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Get-AzDoGitPermission.ps1 index 270690555..8f7f86310 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Get-AzDoGitPermission.ps1 @@ -1,31 +1,57 @@ <# - [DscProperty(Key, Mandatory)] - [Alias('Name')] - [System.String]$ProjectName +.SYNOPSIS +Retrieves the Git repository permissions for a specified Azure DevOps project and repository. - [DscProperty(Mandatory)] - [Alias('Repository')] - [System.String]$RepositoryName +.DESCRIPTION +The Get-AzDoGitPermission function retrieves the Git repository permissions for a specified Azure DevOps project and repository. +It performs a lookup within the cache for the repository and retrieves the Access Control List (ACL) for the repository. +The function then compares the retrieved ACLs with the provided permissions and returns the result. - [DscProperty()] - [Alias('Inherited')] - [System.Boolean]$isInherited=$true +.PARAMETER ProjectName +The name of the Azure DevOps project. - [DscProperty()] - [Alias('Permissions')] - [Permission[]]$PermissionsList +.PARAMETER RepositoryName +The name of the Git repository within the Azure DevOps project. + +.PARAMETER isInherited +A boolean value indicating whether the permissions are inherited. + +.PARAMETER Permissions +An optional hashtable array of permissions to compare against the retrieved ACLs. + +.PARAMETER LookupResult +An optional hashtable to store the lookup result. + +.PARAMETER Ensure +An optional parameter to specify the desired state of the permissions. + +.PARAMETER Force +A switch parameter to force the operation. + +.EXAMPLE +Get-AzDoGitPermission -ProjectName "MyProject" -RepositoryName "MyRepo" -isInherited $true + +This example retrieves the Git repository permissions for the "MyRepo" repository in the "MyProject" Azure DevOps project, +considering inherited permissions. + +.NOTES +The function relies on cached items for the repository and security namespace. +It uses helper functions like Get-CacheItem, Get-DevOpsACL, ConvertTo-FormattedACL, ConvertTo-ACL, and Test-ACLListforChanges. #> -Function Get-xAzDoGitPermission { + +Function Get-AzDoGitPermission +{ [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$ProjectName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$RepositoryName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [bool]$isInherited, [Parameter()] @@ -42,14 +68,14 @@ Function Get-xAzDoGitPermission { $Force ) - Write-Verbose "[Get-xAzDoGitPermission] Started." + Write-Verbose "[Get-AzDoGitPermission] Started." # Define the Descriptor Type and Organization Name $SecurityNamespace = 'Git Repositories' $OrganizationName = $Global:DSCAZDO_OrganizationName - Write-Verbose "[Get-xAzDoGitPermission] Security Namespace: $SecurityNamespace" - Write-Verbose "[Get-xAzDoGitPermission] Organization Name: $OrganizationName" + Write-Verbose "[Get-AzDoGitPermission] Security Namespace: $SecurityNamespace" + Write-Verbose "[Get-AzDoGitPermission] Organization Name: $OrganizationName" # # Construct a hashtable detailing the group @@ -63,19 +89,20 @@ Function Get-xAzDoGitPermission { reason = $null } - Write-Verbose "[Get-xAzDoGitPermission] Group result hashtable constructed." - Write-Verbose "[Get-xAzDoGitPermission] Performing lookup of permissions for the repository." + Write-Verbose "[Get-AzDoGitPermission] Group result hashtable constructed." + Write-Verbose "[Get-AzDoGitPermission] Performing lookup of permissions for the repository." # Define the ACL List $ACLList = [System.Collections.Generic.List[Hashtable]]::new() # # Perform a Lookup within the Cache for the Repository - $repository = Get-CacheItem -Key $("{0}\{1}" -f $ProjectName, $RepositoryName) -Type 'LiveRepositories' + $repository = Get-CacheItem -Key $('{0}\{1}' -f $ProjectName, $RepositoryName) -Type 'LiveRepositories' # Test if the Repository was found - if (-not $repository) { - Write-Warning "[Get-xAzDoGitPermission] Repository not found: $RepositoryName" + if (-not $repository) + { + Write-Warning "[Get-AzDoGitPermission] Repository not found: $RepositoryName" $getGroupResult.status = [DSCGetSummaryState]::NotFound return $getGroupResult } @@ -84,7 +111,7 @@ Function Get-xAzDoGitPermission { # Perform Lookup of the Permissions for the Repository $namespace = Get-CacheItem -Key $SecurityNamespace -Type 'SecurityNamespaces' - Write-Verbose "[Get-xAzDoGitPermission] Retrieved namespace: $($namespace.namespaceId)" + Write-Verbose "[Get-AzDoGitPermission] Retrieved namespace: $($namespace.namespaceId)" # Add to the ACL Lookup Params $getGroupResult.namespace = $namespace @@ -95,14 +122,15 @@ Function Get-xAzDoGitPermission { } # Get the ACL List and format the ACLS - Write-Verbose "[Get-xAzDoGitPermission] ACL Lookup Params: $($ACLLookupParams | Out-String)" + Write-Verbose "[Get-AzDoGitPermission] ACL Lookup Params: $($ACLLookupParams | Out-String)" # Get the ACLs for the Repository $DevOpsACLs = Get-DevOpsACL @ACLLookupParams # Test if the ACLs were found - if ($DevOpsACLs -eq $null) { - Write-Warning "[Get-xAzDoGitPermission] No ACLs found for the repository." + if ($DevOpsACLs -eq $null) + { + Write-Warning "[Get-AzDoGitPermission] No ACLs found for the repository." $getGroupResult.status = [DSCGetSummaryState]::NotFound return $getGroupResult } @@ -111,8 +139,9 @@ Function Get-xAzDoGitPermission { $DifferenceACLs = $DevOpsACLs | ConvertTo-FormattedACL -SecurityNamespace $SecurityNamespace -OrganizationName $OrganizationName # Test if the ACLs were found - if ($DifferenceACLs -eq $null) { - Write-Warning "[Get-xAzDoGitPermission] No ACLs found for the repository." + if ($DifferenceACLs -eq $null) + { + Write-Warning "[Get-AzDoGitPermission] No ACLs found for the repository." $getGroupResult.status = [DSCGetSummaryState]::NotFound return $getGroupResult } @@ -121,7 +150,7 @@ Function Get-xAzDoGitPermission { ($_.Token.Type -eq 'GitRepository') -and ($_.Token.RepoId -eq $repository.id) } - Write-Verbose "[Get-xAzDoGitPermission] ACL List retrieved and formatted." + Write-Verbose "[Get-AzDoGitPermission] ACL List retrieved and formatted." # # Convert the Permissions into an ACL Token @@ -131,7 +160,7 @@ Function Get-xAzDoGitPermission { SecurityNamespace = $SecurityNamespace isInherited = $isInherited OrganizationName = $OrganizationName - TokenName = "[{0}]\{1}" -f $ProjectName, $RepositoryName + TokenName = '[{0}]\{1}' -f $ProjectName, $RepositoryName } # Convert the Permissions to an ACL Token @@ -143,16 +172,16 @@ Function Get-xAzDoGitPermission { $getGroupResult.status = [DSCGetSummaryState]::"$($compareResult.status)" $getGroupResult.reason = $compareResult.reason - Write-Verbose "[Get-xAzDoGitPermission] ACL Token converted." - Write-Verbose "[Get-xAzDoGitPermission] ACL Token Comparison Result: $($getGroupResult.status)" + Write-Verbose "[Get-AzDoGitPermission] ACL Token converted." + Write-Verbose "[Get-AzDoGitPermission] ACL Token Comparison Result: $($getGroupResult.status)" # Export the ACL List to a file $getGroupResult.ReferenceACLs = $ReferenceACLs $getGroupResult.DifferenceACLs = $DifferenceACLs # Write - Write-Verbose "[Get-xAzDoGitPermission] Result Status: $($getGroupResult.status)" - Write-Verbose "[Get-xAzDoGitPermission] Returning Group Result." + Write-Verbose "[Get-AzDoGitPermission] Result Status: $($getGroupResult.status)" + Write-Verbose "[Get-AzDoGitPermission] Returning Group Result." # Return the Group Result return $getGroupResult diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/New-AzDoGitPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/New-AzDoGitPermission.ps1 new file mode 100644 index 000000000..88eee322d --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/New-AzDoGitPermission.ps1 @@ -0,0 +1,96 @@ +<# +.SYNOPSIS +Creates new Git repository permissions in Azure DevOps. + +.DESCRIPTION +The New-AzDoGitPermission function sets up new permissions for a specified Git repository within a given project in Azure DevOps. It uses cached security namespace and project information to serialize ACLs and apply the permissions. + +.PARAMETER ProjectName +The name of the Azure DevOps project. + +.PARAMETER RepositoryName +The name of the Git repository within the Azure DevOps project. + +.PARAMETER isInherited +Indicates whether the permissions are inherited. + +.PARAMETER Permissions +A hashtable array of permissions to be applied. + +.PARAMETER LookupResult +A hashtable containing the lookup result properties. + +.PARAMETER Ensure +Specifies whether to ensure the permissions are set. + +.PARAMETER Force +A switch parameter to force the operation. + +.EXAMPLE +New-AzDoGitPermission -ProjectName "MyProject" -RepositoryName "MyRepo" -isInherited $true -Permissions $permissions -LookupResult $lookupResult -Ensure "Present" -Force + +.NOTES +This function relies on cached items for security namespace and project information. Ensure that the cache is populated before calling this function. +#> +Function New-AzDoGitPermission +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$ProjectName, + + [Parameter(Mandatory = $true)] + [string]$RepositoryName, + + [Parameter(Mandatory = $true)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[New-AzDoGitPermission] Started." + + # + # Security Namespace ID + + $SecurityNamespace = Get-CacheItem -Key 'Git Repositories' -Type 'SecurityNamespaces' + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + if (($null -eq $SecurityNamespace) -or ($null -eq $Project)) + { + Write-Warning "[New-AzDoGitPermission] Security Namespace or Project not found." + return + } + + # + # Serialize the ACLs + + $serializeACLParams = @{ + ReferenceACLs = $LookupResult.propertiesChanged + DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' + DescriptorMatchToken = ($LocalizedDataAzSerializationPatten.GitRepository -f $Project.id) + } + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SecurityNamespaceID = $SecurityNamespace.namespaceId + SerializedACLs = ConvertTo-ACLHashtable @serializeACLParams + } + + # + # Set the Git Repository Permissions + + Set-AzDoPermission @params + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Remove-AzDoGitPermission.ps1 similarity index 61% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Remove-AzDoGitPermission.ps1 index 0a1b9af34..3683b6233 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Remove-AzDoGitPermission.ps1 @@ -1,13 +1,14 @@ -Function Remove-xAzDoGitPermission { +Function Remove-AzDoGitPermission +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$ProjectName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$RepositoryName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [bool]$isInherited, [Parameter()] @@ -24,7 +25,7 @@ Function Remove-xAzDoGitPermission { $Force ) - Write-Verbose "[New-xAzDoGitPermission] Started." + Write-Verbose "[New-AzDoGitPermission] Started." # # Security Namespace ID @@ -33,26 +34,29 @@ Function Remove-xAzDoGitPermission { $SecurityNamespace = Get-CacheItem -Key 'Git Repositories' -Type 'SecurityNamespaces' # If the Security Namespace is null, return - if (-not $SecurityNamespace) { - Write-Error "[New-xAzDoGitPermission] Security Namespace not found." + if (-not $SecurityNamespace) + { + Write-Error "[New-AzDoGitPermission] Security Namespace not found." return } # Get the Project - $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' # If the Project is null, return - if (-not $Project) { - Write-Error "[New-xAzDoGitPermission] Project not found." + if (-not $Project) + { + Write-Error "[New-AzDoGitPermission] Project not found." return } # Get the Repository - $Repository = Get-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' + $Repository = Get-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' # If the Repository is null, return - if (-not $Repository) { - Write-Error "[New-xAzDoGitPermission] Repository not found." + if (-not $Repository) + { + Write-Error "[New-AzDoGitPermission] Repository not found." return } @@ -60,22 +64,23 @@ Function Remove-xAzDoGitPermission { $DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' # If the ACLs are null, return - if (-not $DescriptorACLList) { - Write-Error "[New-xAzDoGitPermission] ACLs not found." + if (-not $DescriptorACLList) + { + Write-Error "[New-AzDoGitPermission] ACLs not found." return } # # Filter the ACLs that pertain to the Git Repository - $searchString = "repoV2/{0}/{1}" -f $Project.id, $Repository.id + $searchString = 'repoV2/{0}/{1}' -f $Project.id, $Repository.id # Test if the Token exists $Filtered = $DescriptorACLList | Where-Object { $_.token -eq $searchString } # If the ACLs are not null, remove them - if ($Filtered) { - + if ($Filtered) + { $params = @{ OrganizationName = $Global:DSCAZDO_OrganizationName SecurityNamespaceID = $SecurityNamespace.namespaceId @@ -83,7 +88,7 @@ Function Remove-xAzDoGitPermission { } # Remove the ACLs - Remove-xAzDoPermission @params + Remove-AzDoPermission @params } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Set-AzDoGitPermission.ps1 similarity index 73% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Set-AzDoGitPermission.ps1 index 1c301a99f..947b024ad 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Set-AzDoGitPermission.ps1 @@ -1,13 +1,14 @@ -Function Set-xAzDoGitPermission { +Function Set-AzDoGitPermission +{ [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$ProjectName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$RepositoryName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [bool]$isInherited, [Parameter()] @@ -24,7 +25,7 @@ Function Set-xAzDoGitPermission { $Force ) - Write-Verbose "[Set-xAzDoPermission] Started." + Write-Verbose "[Set-AzDoPermission] Started." # # Security Namespace ID @@ -32,13 +33,15 @@ Function Set-xAzDoGitPermission { $SecurityNamespace = Get-CacheItem -Key 'Git Repositories' -Type 'SecurityNamespaces' $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' - if ($SecurityNamespace -eq $null) { - Write-Error "[Set-xAzDoPermission] Security Namespace not found." + if ($SecurityNamespace -eq $null) + { + Write-Error "[Set-AzDoPermission] Security Namespace not found." return } - if ($Project -eq $null) { - Write-Error "[Set-xAzDoPermission] Project not found." + if ($Project -eq $null) + { + Write-Error "[Set-AzDoPermission] Project not found." return } @@ -60,6 +63,6 @@ Function Set-xAzDoGitPermission { # # Set the Git Repository Permissions - Set-xAzDoPermission @params + Set-AzDoPermission @params } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Get-AzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Get-AzDoGitRepository.ps1 new file mode 100644 index 000000000..de5a2c091 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Get-AzDoGitRepository.ps1 @@ -0,0 +1,103 @@ +<# +.SYNOPSIS +Retrieves an Azure DevOps Git repository from the live and local cache. + +.DESCRIPTION +The Get-AzDoGitRepository function attempts to retrieve an Azure DevOps Git repository based on the provided project and repository names. It first checks the live cache for the repository and returns the repository object if found. If the repository is not found in the live cache, it returns a status indicating that the repository was not found. + +.PARAMETER ProjectName +The name of the Azure DevOps project. + +.PARAMETER RepositoryName +The name of the Azure DevOps Git repository. + +.PARAMETER SourceRepository +(Optional) The source repository name. + +.PARAMETER LookupResult +(Optional) A hashtable to store lookup results. + +.PARAMETER Ensure +(Optional) Specifies the desired state of the repository. + +.PARAMETER Force +(Optional) A switch parameter to force the operation. + +.OUTPUTS +System.Management.Automation.PSObject[] +Returns a hashtable detailing the repository status and properties. + +.EXAMPLE +PS C:\> Get-AzDoGitRepository -ProjectName "MyProject" -RepositoryName "MyRepo" + +This command retrieves the "MyRepo" repository from the "MyProject" project. + +#> +Function Get-AzDoGitRepository +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter(Mandatory = $true)] + [Alias('Repository')] + [System.String]$RepositoryName, + + [Parameter()] + [Alias('Source')] + [System.String]$SourceRepository, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # + # Construct a hashtable detailing the group + + $getRepositoryResult = @{ + #Reasons = $() + Ensure = [Ensure]::Absent + liveCache = $livegroup + propertiesChanged = @() + status = $null + } + + # + # Attempt to retrive the Project Group from the Live and Local Cache. + Write-Verbose "[Get-AzDoGitRepository] Retriving the Project Group from the Live and Local Cache." + + # Format the Key for the Project Group. + $projectGroupKey = "$ProjectName\$RepositoryName" + + # Retrive the Repositories from the Live Cache. + $repository = Get-CacheItem -Key $projectGroupKey -Type 'LiveRepositories' + + # If the Repository exists in the Live Cache, return the Repository object. + if ($repository) + { + Write-Verbose "[Get-AzDoGitRepository] The Repository '$RepositoryName' was found in the Live Cache." + $getRepositoryResult.status = [DSCGetSummaryState]::Unchanged + return $getRepositoryResult + + } + else + { + Write-Verbose "[Get-AzDoGitRepository] The Repository '$RepositoryName' was not found in the Live Cache." + $getRepositoryResult.status = [DSCGetSummaryState]::NotFound + } + + # Return the Repository object. + return $getRepositoryResult + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/New-AzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/New-AzDoGitRepository.ps1 new file mode 100644 index 000000000..0dffb89af --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/New-AzDoGitRepository.ps1 @@ -0,0 +1,97 @@ +<# +.SYNOPSIS +Creates a new Azure DevOps Git repository within a specified project. + +.DESCRIPTION +The New-AzDoGitRepository function creates a new Git repository in an Azure DevOps project. +It uses the provided project name and repository name to create the repository. +Optionally, a source repository can be specified to initialize the new repository. + +.PARAMETER ProjectName +The name of the Azure DevOps project where the new repository will be created. + +.PARAMETER RepositoryName +The name of the new Git repository to be created. + +.PARAMETER SourceRepository +(Optional) The name of the source repository to initialize the new repository. + +.PARAMETER LookupResult +(Optional) A hashtable to store lookup results. + +.PARAMETER Ensure +(Optional) Specifies whether to ensure the repository exists or does not exist. + +.PARAMETER Force +(Optional) Forces the creation of the repository even if it already exists. + +.EXAMPLE +PS> New-AzDoGitRepository -ProjectName "MyProject" -RepositoryName "MyRepo" + +Creates a new Git repository named "MyRepo" in the "MyProject" Azure DevOps project. + +.EXAMPLE +PS> New-AzDoGitRepository -ProjectName "MyProject" -RepositoryName "MyRepo" -SourceRepository "TemplateRepo" + +Creates a new Git repository named "MyRepo" in the "MyProject" Azure DevOps project, initialized with the contents of "TemplateRepo". + +.NOTES +This function requires the Azure DevOps organization name to be set in the global variable $Global:DSCAZDO_OrganizationName. +#> + +Function New-AzDoGitRepository +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter(Mandatory = $true)] + [Alias('Repository')] + [System.String]$RepositoryName, + + [Parameter()] + [Alias('Source')] + [System.String]$SourceRepository, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[New-AzDoGitRepository] Creating new repository '$($RepositoryName)' in project '$($ProjectName)'" + + # Define parameters for creating a new DevOps group + $params = @{ + ApiUri = 'https://dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + RepositoryName = $RepositoryName + SourceRepository = $SourceRepository + } + + if ($null -eq $params.Project) + { + Write-Error "[New-AzDoGitRepository] Project '$($ProjectName)' does not exist in the LiveProjects cache. Skipping change." + return + } + + + # Create a new repository + $value = New-GitRepository @params + + # Add the repository to the LiveRepositories cache and write to verbose log + Add-CacheItem -Key "$ProjectName\$RepositoryName" -Value $value -Type 'LiveRepositories' + Export-CacheObject -CacheType 'LiveRepositories' -Content $AzDoLiveRepositories + Refresh-CacheObject -CacheType 'LiveRepositories' + Write-Verbose "[New-AzDoGitRepository] Added new group to LiveGroups cache with key: '$($value.Name)'" + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Remove-AzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Remove-AzDoGitRepository.ps1 new file mode 100644 index 000000000..bb150d04f --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Remove-AzDoGitRepository.ps1 @@ -0,0 +1,87 @@ +<# +.SYNOPSIS +Removes a Git repository from an Azure DevOps project. + +.DESCRIPTION +The Remove-AzDoGitRepository function removes a specified Git repository from a given Azure DevOps project. +It checks the existence of the project and repository in the LiveProjects and LiveRepositories cache before attempting the removal. + +.PARAMETER ProjectName +The name of the Azure DevOps project containing the repository to be removed. + +.PARAMETER RepositoryName +The name of the repository to be removed. + +.PARAMETER SourceRepository +An optional parameter specifying the source repository. + +.PARAMETER LookupResult +An optional hashtable parameter for lookup results. + +.PARAMETER Ensure +An optional parameter to ensure the state of the repository. + +.PARAMETER Force +A switch parameter to force the removal of the repository. + +.EXAMPLE +Remove-AzDoGitRepository -ProjectName "MyProject" -RepositoryName "MyRepo" -Force + +.NOTES +This function relies on the existence of certain global variables and cache items. +Ensure that the necessary cache items and global variables are properly set before invoking this function. +#> +Function Remove-AzDoGitRepository +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter(Mandatory = $true)] + [Alias('Repository')] + [System.String]$RepositoryName, + + [Parameter()] + [Alias('Source')] + [System.String]$SourceRepository, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[Remove-AzDoGitRepository] Removing repository '$($RepositoryName)' in project '$($ProjectName)'" + + # Define parameters for creating a new DevOps group + $params = @{ + ApiUri = 'https://dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + Repository = Get-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' + } + + # Check if the project exists in the LiveProjects cache + if (($null -eq $params.Project) -or ($null -eq $params.Repository)) + { + Write-Error "[Remove-AzDoGitRepository] Project '$($ProjectName)' or Repository '$($RepositoryName)' does not exist in the LiveProjects or LiveRepositories cache. Skipping change." + return + } + + # Create a new repository + $value = Remove-GitRepository @params + + # Add the repository to the LiveRepositories cache and write to verbose log + Remove-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' + Export-CacheObject -CacheType 'LiveRepositories' -Content $AzDoLiveRepositories + Write-Verbose "[Remove-AzDoGitRepository] Added new group to LiveGroups cache with key: '$($value.Name)'" + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Set-AzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Set-AzDoGitRepository.ps1 new file mode 100644 index 000000000..068ce60e9 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Set-AzDoGitRepository.ps1 @@ -0,0 +1,67 @@ +<# +.SYNOPSIS +Sets the configuration for an Azure DevOps Git repository. + +.DESCRIPTION +The Set-AzDoGitRepository function configures an Azure DevOps Git repository based on the provided parameters. It allows specifying the project name, repository name, source repository, and other optional parameters. + +.PARAMETER ProjectName +The name of the Azure DevOps project. This parameter is mandatory. + +.PARAMETER RepositoryName +The name of the Azure DevOps Git repository. This parameter is mandatory. + +.PARAMETER SourceRepository +The name of the source repository to use for configuration. This parameter is optional. + +.PARAMETER LookupResult +A hashtable containing lookup results. This parameter is optional. + +.PARAMETER Ensure +Specifies whether the repository should be present or absent. This parameter is optional. + +.PARAMETER Force +A switch parameter to force the operation. This parameter is optional. + +.OUTPUTS +[System.Management.Automation.PSObject[]] +Returns an array of PSObject representing the result of the operation. + +.EXAMPLE +Set-AzDoGitRepository -ProjectName "MyProject" -RepositoryName "MyRepo" -SourceRepository "SourceRepo" + +.EXAMPLE +Set-AzDoGitRepository -ProjectName "MyProject" -RepositoryName "MyRepo" -Force +#> +Function Set-AzDoGitRepository +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter(Mandatory = $true)] + [Alias('Repository')] + [System.String]$RepositoryName, + + [Parameter()] + [Alias('Source')] + [System.String]$SourceRepository, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Skipped + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Get-AzDoGroupMember.ps1 similarity index 62% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Get-AzDoGroupMember.ps1 index ce11a7d54..5b78239b0 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Get-AzDoGroupMember.ps1 @@ -1,12 +1,45 @@ +<# +.SYNOPSIS +Retrieves the members of an Azure DevOps group and compares them with the provided parameters. +.DESCRIPTION +The Get-AzDoGroupMember function retrieves the members of an Azure DevOps group from the live cache and compares them with the provided parameters. It returns a hashtable detailing the group status, including any differences between the live group members and the provided parameters. -Function Get-xAzDoGroupMember { +.PARAMETER GroupName +The name of the Azure DevOps group to retrieve. +.PARAMETER GroupMembers +An array of group members to compare against the live group members. Default is an empty array. + +.PARAMETER LookupResult +A hashtable to store lookup results. + +.PARAMETER Ensure +Specifies whether the group should be present or absent. + +.PARAMETER Force +A switch parameter to force the operation. + +.OUTPUTS +System.Management.Automation.PSObject[] +A hashtable detailing the group status, including any differences between the live group members and the provided parameters. + +.EXAMPLE +PS C:\> Get-AzDoGroupMember -GroupName "Developers" -GroupMembers @("user1", "user2") + +This command retrieves the members of the "Developers" group and compares them with the provided members "user1" and "user2". + +.NOTES +This function uses verbose logging to provide detailed information about its operations. +#> + +Function Get-AzDoGroupMember +{ [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$GroupName, @@ -23,12 +56,10 @@ Function Get-xAzDoGroupMember { [Parameter()] [System.Management.Automation.SwitchParameter] $Force - ) # Logging - Write-Verbose "[Get-xAzDoGroupMember] Retriving the GroupName from the Live and Local Cache." - + Write-Verbose "[Get-AzDoGroupMember] Retriving the GroupName from the Live and Local Cache." # Format the According to the Group Name $Key = Format-AzDoGroupMember -GroupName $GroupName @@ -36,7 +67,7 @@ Function Get-xAzDoGroupMember { # Check the cache for the group $livegroupMembers = Get-CacheItem -Key $Key -Type 'LiveGroupMembers' - Write-Verbose "[Get-xAzDoGroupMember] GroupName: '$GroupName'" + Write-Verbose "[Get-AzDoGroupMember] GroupName: '$GroupName'" # # Construct a hashtable detailing the group @@ -50,18 +81,20 @@ Function Get-xAzDoGroupMember { status = $null } - Write-Verbose "[Get-xAzDoGroupMember] Testing LocalCache, LiveCache and Parameters." + Write-Verbose "[Get-AzDoGroupMember] Testing LocalCache, LiveCache and Parameters." # # Test if the group is present in the live cache - if ($null -eq $livegroupMembers) { - - Write-Verbose "[Get-xAzDoGroupMember] Group '$GroupName' not found in the live cache." - + if ($null -eq $livegroupMembers) + { + Write-Verbose "[Get-AzDoGroupMember] Group '$GroupName' not found in the live cache." # If there are no group members, test to see if there are group members defined in the parameters - if ($GroupMembers.Count -eq 0) { + if ($GroupMembers.Count -eq 0) + { $getGroupResult.status = [DSCGetSummaryState]::Unchanged - } else { + } + else + { # If there are group members defined in the parameters, but no live group members, the group is new. $getGroupResult.status = [DSCGetSummaryState]::NotFound } @@ -72,14 +105,17 @@ Function Get-xAzDoGroupMember { # # Test if there are no group members in parameters - if ($GroupMembers.Count -eq 0) { - - Write-Verbose "[Get-xAzDoGroupMember] Group '$GroupName' not found in the parameters." + if ($GroupMembers.Count -eq 0) + { + Write-Verbose "[Get-AzDoGroupMember] Group '$GroupName' not found in the parameters." # If there are no live group members, the group is unchanged. - if ($livegroupMembers.Count -eq 0) { + if ($livegroupMembers.Count -eq 0) + { $getGroupResult.status = [DSCGetSummaryState]::Unchanged - } else { + } + else + { # If there are live group members, the groups members are to be removed. $getGroupResult.status = [DSCGetSummaryState]::Missing } @@ -98,7 +134,8 @@ Function Get-xAzDoGroupMember { $FormattedParametersGroups = $GroupMembers | ForEach-Object { Find-AzDoIdentity $_ } # If the formatted live groups is empty. Modify the formatted live groups to be an empty array. - if ($null -eq $FormattedLiveGroups) { + if ($null -eq $FormattedLiveGroups) + { $FormattedLiveGroups = @() } @@ -118,13 +155,13 @@ Function Get-xAzDoGroupMember { # # If there are no differences, the group is unchanged. - if ($members.Count -eq 0) { - + if ($members.Count -eq 0) + { # The group is unchanged. $getGroupResult.status = [DSCGetSummaryState]::Unchanged - - } else { - + } + else + { # Users on the left side are in the comparison object but not in the reference object are to be added. # Users on the right side are in the reference object but not in the comparison object are to be removed. $getGroupResult.propertiesChanged += $members | ForEach-Object { @@ -136,10 +173,8 @@ Function Get-xAzDoGroupMember { } } - # The group has changed. $getGroupResult.status = [DSCGetSummaryState]::Changed - } return $getGroupResult diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.ps1 new file mode 100644 index 000000000..1970ce6be --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.ps1 @@ -0,0 +1,143 @@ +<# +.SYNOPSIS + Adds members to an Azure DevOps group. + +.DESCRIPTION + The New-AzDoGroupMember function adds specified members to an Azure DevOps group. + It fetches the group identity, retrieves cached group members, and performs a lookup + for each member to add them to the group. The function also handles circular references + and updates the cache with the new group members. + +.PARAMETER GroupName + The name of the Azure DevOps group to which members will be added. + +.PARAMETER GroupMembers + An array of member identities to be added to the group. Default is an empty array. + +.PARAMETER LookupResult + A hashtable containing lookup results. + +.PARAMETER Ensure + Specifies whether to ensure the presence or absence of the group members. + +.PARAMETER Force + A switch parameter to force the addition of members even if they are already cached. + +.EXAMPLE + PS> New-AzDoGroupMember -GroupName "Developers" -GroupMembers "user1", "user2" + + This example adds "user1" and "user2" to the "Developers" group. + +.NOTES + The function writes verbose messages to indicate the progress of the group member addition process. + It also handles errors and warnings for cases such as circular references and missing identities. +#> + +Function New-AzDoGroupMember +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Members')] + [System.String[]]$GroupMembers=@(), + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Write a verbose log message indicating that the function has started executing. + Write-Verbose "[New-AzDoGroupMember] Starting group member addition process for group '$GroupName'." + + # Fetch the Group Identity + $GroupIdentity = Find-AzDoIdentity $GroupName + Write-Verbose "[New-AzDoGroupMember] Fetched group identity for '$GroupName'." + + # Retrieve the group members from the cache + $CachedGroupMembers = Get-CacheObject -CacheType 'LiveGroupMembers' + Write-Verbose "[New-AzDoGroupMember] Retrieved cached group members." + + # Check if the group members are already cached + if ( + ($null -ne $CachedGroupMembers) -and + ($CachedGroupMembers.ContainsKey($GroupIdentity.principalName)) + ) + { + Write-Error "[New-AzDoGroupMember] Group members are already cached for group '$GroupName'." + return + } + + $params = @{ + GroupIdentity = $GroupIdentity + ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + } + + Write-Verbose "[New-AzDoGroupMember] Starting group member addition process for group '$GroupName'." + Write-Verbose "[New-AzDoGroupMember] Group members: $($GroupMembers -join ',')." + + # Define the members + $members = [System.Collections.Generic.List[object]]::new() + + # Fetch the group members and perform a lookup of the members + ForEach ($MemberIdentity in $GroupMembers) + { + # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. + Write-Verbose "[New-AzDoGroupMember] Looking up identity for member '$MemberIdentity'." + $identity = Find-AzDoIdentity -Identity $MemberIdentity + + # If the identity is not found, write a warning message to the console and continue to the next member. + if ($null -eq $identity) + { + Write-Warning "[New-AzDoGroupMember] Unable to find identity for member '$MemberIdentity'." + continue + } + + # Check for circular reference + if ($GroupIdentity.originId -eq $identity.originId) + { + Write-Warning "[New-AzDoGroupMember] Circular reference detected for member '$MemberIdentity'." + continue + } + + Write-Verbose "[New-AzDoGroupMember] Found identity for member '$MemberIdentity'." + + # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. + Write-Verbose "[New-AzDoGroupMember] Adding member '$MemberIdentity' to group '$($params.GroupIdentity.displayName)'." + + $result = New-DevOpsGroupMember @params -MemberIdentity $identity + + # Add the member to the list + $members.Add($identity) + Write-Verbose "[New-AzDoGroupMember] Member '$MemberIdentity' added to the internal list." + } + + # If the group members are not found, write a warning message to the console and return. + if ($members.Count -eq 0) + { + Write-Warning "[New-AzDoGroupMember] No group members found: $($GroupMembers -join ',')." + return + } + + # Add the group to the cache + Write-Verbose "[New-AzDoGroupMember] Added group '$GroupName' with members to the cache." + Add-CacheItem -Key $GroupIdentity.principalName -Value $members -Type 'LiveGroupMembers' + + Write-Verbose "[New-AzDoGroupMember] Updated global cache with live group information." + Set-CacheObject -Content $Global:AzDoLiveGroupMembers -CacheType 'LiveGroupMembers' + + # Write a verbose log message indicating that the function has completed the group member addition process. + Write-Verbose "[New-AzDoGroupMember] Completed group member addition process for group '$GroupName'." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Remove-AzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Remove-AzDoGroupMember.ps1 new file mode 100644 index 000000000..52cf5eff5 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Remove-AzDoGroupMember.ps1 @@ -0,0 +1,119 @@ +<# +.SYNOPSIS +Removes members from an Azure DevOps group. + +.DESCRIPTION +The Remove-AzDoGroupMember function removes specified members from an Azure DevOps group. +It looks up the group identity, checks the cache for existing group members, and removes the specified members. + +.PARAMETER GroupName +The name of the Azure DevOps group from which members will be removed. + +.PARAMETER GroupMembers +An array of group members to be removed. This parameter is optional. + +.PARAMETER LookupResult +A hashtable containing lookup results. This parameter is optional. + +.PARAMETER Ensure +Specifies whether to ensure the removal of the group members. This parameter is optional. + +.PARAMETER Force +A switch parameter to force the removal of group members without confirmation. + +.EXAMPLE +Remove-AzDoGroupMember -GroupName "Developers" -GroupMembers "user1@example.com", "user2@example.com" + +This command removes the specified members from the "Developers" group. + +.EXAMPLE +Remove-AzDoGroupMember -GroupName "Developers" -Force + +This command forces the removal of all members from the "Developers" group without confirmation. + +.NOTES +This function requires the Azure DevOps module and appropriate permissions to manage group memberships. + +#> +Function Remove-AzDoGroupMember +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Members')] + [System.String[]]$GroupMembers=@(), + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Group Identity + $GroupIdentity = Find-AzDoIdentity $GroupName + + # Format the According to the Group Name + $Key = Format-AzDoProjectName -GroupName $GroupName -OrganizationName $Global:DSCAZDO_OrganizationName + + # Check the cache for the group + $LiveGroupMembers = @(Get-CacheItem -Key $Key -Type 'LiveGroupMembers') + + # If the group identity or key is not found, write a warning message to the console and return. + if ([String]::IsNullOrEmpty($GroupIdentity) -or [String]::IsNullOrWhiteSpace($GroupIdentity)) + { + Write-Warning "[Remove-AzDoGroupMember] Unable to find identity for group '$GroupName'." + return + } + + $params = @{ + GroupIdentity = $GroupIdentity + ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + } + + Write-Verbose "[Remove-AzDoGroupMember] Starting group member removal process for group '$GroupName'." + Write-Verbose "[Remove-AzDoGroupMember] Group members: $($LiveGroupMembers.principalName -join ',')." + + # Fetch the group members and perform a lookup of the members + ForEach ($MemberIdentity in $LiveGroupMembers) + { + + # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. + Write-Verbose "[Remove-AzDoGroupMember] Looking up identity for member '$($MemberIdentity.principalName)'." + $identity = Find-AzDoIdentity -Identity $MemberIdentity.principalName + + # If the identity is not found, write a warning message to the console and continue to the next member. + if ([String]::IsNullOrEmpty($identity) -or [String]::IsNullOrWhiteSpace($identity)) + { + Write-Warning "[Remove-AzDoGroupMember] Unable to find identity for member '$($MemberIdentity.principalName)'." + continue + } + + # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. + Write-Verbose "[Remove-AzDoGroupMember] Removing member '$($MemberIdentity.principalName)' from group '$($params.GroupIdentity.displayName)'." + + $result = Remove-DevOpsGroupMember @params -MemberIdentity $identity + + } + + # Add the group to the cache + Write-Verbose "[Remove-AzDoGroupMember] Removed group '$GroupName' with members to the cache." + Remove-CacheItem -Key $GroupIdentity.principalName -Type 'LiveGroupMembers' + + Write-Verbose "[Remove-AzDoGroupMember] Updated global cache with live group information." + Set-CacheObject -Content $Global:AzDoLiveGroupMembers -CacheType 'LiveGroupMembers' + + # Write a verbose log message indicating that the function has completed the group member removal process. + Write-Verbose "[Remove-AzDoGroupMember] Completed group member removal process for group '$GroupName'." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Set-AzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Set-AzDoGroupMember.ps1 new file mode 100644 index 000000000..2af6ac5c7 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Set-AzDoGroupMember.ps1 @@ -0,0 +1,161 @@ +<# +.SYNOPSIS + Manages Azure DevOps group members by adding or removing members based on the provided lookup result. + +.PARAMETER GroupName + The name of the Azure DevOps group to manage. + +.PARAMETER GroupMembers + An array of group members to be managed. Defaults to an empty array. + +.PARAMETER LookupResult + A hashtable containing the propertiesChanged key which indicates the action to be performed (Add or Remove). + +.PARAMETER Ensure + Specifies whether the group members should be present or absent. + +.PARAMETER Force + A switch parameter to force the operation. + +.DESCRIPTION + The Set-AzDoGroupMember function manages Azure DevOps group members by adding or removing members based on the provided lookup result. + It checks for circular references and updates the internal cache with the new group member information. + +.EXAMPLE + Set-AzDoGroupMember -GroupName "Developers" -GroupMembers @("user1", "user2") -LookupResult $lookupResult -Ensure "Present" + + This example adds the specified members to the "Developers" group based on the lookup result. + +.NOTES + The function relies on several helper functions such as Find-AzDoIdentity, Format-AzDoProjectName, Get-CacheItem, New-DevOpsGroupMember, Remove-DevOpsGroupMember, Add-CacheItem, and Set-CacheObject. + Ensure that these functions are defined and available in the scope where Set-AzDoGroupMember is called. + +#> + +Function Set-AzDoGroupMember +{ + param( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Members')] + [System.String[]]$GroupMembers=@(), + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Group Identity + $GroupIdentity = Find-AzDoIdentity $GroupName + + # Format the According to the Group Name + $Key = Format-AzDoProjectName -GroupName $GroupName -OrganizationName $Global:DSCAZDO_OrganizationName + # Check the cache for the group + $members = [System.Collections.ArrayList]::New() + Get-CacheItem -Key $Key -Type 'LiveGroupMembers' | ForEach-Object { $members.Add($_) } + + # If the members are null or empty, stop. + if (($null -eq $GroupMembers) -or ($members.Count -eq 0)) + { + Write-Error "[Set-AzDoGroupMember] No members found in the LiveGroupMembers cache for group '$Key'." + return + } + + # If the lookup result is not provided, we need to look it up. + if ($null -eq $LookupResult.propertiesChanged) + { + Throw "[Set-AzDoGroupMember] - LookupResult.propertiesChanged is required." + } + + # Fetch the Group Identity + $params = @{ + GroupIdentity = $GroupIdentity + ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName + } + + Write-Verbose "[Set-AzDoGroupMember] Starting group member addition process for group '$GroupName'." + + # If the lookup result is not provided, we need to look it up. + switch ($LookupResult.propertiesChanged) + { + + # Add members + { $_.action -eq "Add" } { + + # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. + Write-Verbose "[Set-AzDoGroupMember][ADD] Adding Identity for Principal Name '$($_.value.principalName)'." + $identity = $_.value + + # Check for circular reference + if ($GroupIdentity.originId -eq $identity.originId) + { + Write-Warning "[Set-AzDoGroupMember][ADD] Circular reference detected for member '$($GroupIdentity.principalName)'." + continue + } + + # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. + Write-Verbose "[Set-AzDoGroupMember][ADD] Adding member '$($identity.displayName)' to group '$($params.GroupIdentity.displayName)'." + + $result = New-DevOpsGroupMember @params -MemberIdentity $identity + + # Add the member to the list + $members.Add($identity) + Write-Verbose "[Set-AzDoGroupMember][ADD] Member '$($identity.displayName)' added to the internal list." + + } + + # Remove + { $_.action -eq "Remove" } { + + # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. + Write-Verbose "[Set-AzDoGroupMember][REMOVE] Removing Identity for Principal Name '$($_.value.principalName)'." + $identity = $_.value + + # Check for circular reference + if ($GroupIdentity.originId -eq $identity.originId) + { + Write-Warning "[Set-AzDoGroupMember][REMOVE] Circular reference detected for member '$($GroupIdentity.principalName)'." + continue + } + + # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. + Write-Verbose "[Set-AzDoGroupMember][REMOVE] Removing member '$($identity.displayName)' to group '$($params.GroupIdentity.displayName)'." + + $result = Remove-DevOpsGroupMember @params -MemberIdentity $identity + + # Remove the member from the list + + Write-Verbose "[Set-AzDoGroupMember][REMOVE] Removing member '$($identity.displayName)' from the internal list." + Write-Verbose "[Set-AzDoGroupMember][REMOVE] members count: $($members.count)" + + $id = 0 .. $members.count | Where-Object { $members[$_].originId -eq $identity.originId } + $members.RemoveAt($id) + Write-Verbose "[Set-AzDoGroupMember][REMOVE] Member '$($identity.displayName)' removed from the internal list." + + } + + # Default + Default { + Write-Warning "[Set-AzDoGroupMember] Invalid action '$($_.action)' provided." + } + + } + + # Add the group to the cache + Write-Verbose "[Set-AzDoGroupMember] Added group '$GroupName' with the updated member list to the cache." + Add-CacheItem -Key $GroupIdentity.principalName -Value $members -Type 'LiveGroupMembers' + + Write-Verbose "[Set-AzDoGroupMember] Updated global cache with live group information." + Set-CacheObject -Content $Global:AzDoLiveGroupMembers -CacheType 'LiveGroupMembers' + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Test-AzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Test-AzDoGroupMember.ps1 new file mode 100644 index 000000000..3d186415a --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Test-AzDoGroupMember.ps1 @@ -0,0 +1,48 @@ +<# +.SYNOPSIS + Tests the membership of a specified Azure DevOps group. + +.PARAMETER GroupName + The name of the Azure DevOps group to test. + +.PARAMETER GroupMembers + An array of members to check within the Azure DevOps group. Default is an empty array. + +.PARAMETER LookupResult + A hashtable containing lookup results for the group members. + +.PARAMETER Ensure + Specifies whether the group members should be present or absent. + +.PARAMETER Force + Forces the operation to proceed without prompting for confirmation. + +.NOTES +#> + +Function Test-AzDoGroupMember +{ + param( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Members')] + [System.String[]]$GroupMembers=@(), + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + $return + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Get-AzDoGroupPermission.ps1 similarity index 53% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Get-AzDoGroupPermission.ps1 index db0beaae6..5c6054739 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Get-AzDoGroupPermission.ps1 @@ -1,11 +1,53 @@ +<# +.SYNOPSIS +Retrieves the permissions for a specified Azure DevOps group. -Function Get-xAzDoGroupPermission { +.DESCRIPTION +The Get-AzDoGroupPermission function retrieves the permissions for a specified Azure DevOps group. +It performs a lookup within the cache for the group and its associated project, retrieves the +security namespace, and constructs a hashtable detailing the group. It then performs a lookup +of the permissions for the group, formats the ACLs, and compares the reference ACLs to the +difference ACLs to determine any changes. + +.PARAMETER GroupName +The name of the Azure DevOps group. This parameter is mandatory. + +.PARAMETER isInherited +A boolean value indicating whether the permissions are inherited. This parameter is mandatory. + +.PARAMETER Permissions +An array of hashtables representing the permissions to be checked. This parameter is optional. + +.PARAMETER LookupResult +A hashtable representing the lookup result. This parameter is optional. + +.PARAMETER Ensure +Specifies the desired state of the group permissions. This parameter is optional. + +.PARAMETER Force +A switch parameter to force the operation. This parameter is optional. + +.OUTPUTS +System.Management.Automation.PSObject[] +Returns a hashtable detailing the group permissions, including the reference ACLs, difference ACLs, +properties changed, status, and reason. + +.EXAMPLE +PS C:\> Get-AzDoGroupPermission -GroupName "ProjectName\GroupName" -isInherited $true + +Retrieves the permissions for the specified Azure DevOps group with inheritance. + +#> + +Function Get-AzDoGroupPermission +{ [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$GroupName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [bool]$isInherited, [Parameter()] @@ -22,7 +64,7 @@ Function Get-xAzDoGroupPermission { $Force ) - Write-Verbose "[Get-xAzDoGroupPermission] Started." + Write-Verbose "[Get-AzDoGroupPermission] Started." # Define the Descriptor Type and Organization Name $SecurityNamespace = 'Identity' @@ -31,8 +73,9 @@ Function Get-xAzDoGroupPermission { $split = $GroupName.Split('\').Split('/') # Test if the Group Name is valid - if ($split.Count -ne 2) { - Write-Warning "[Get-xAzDoGroupPermission] Invalid Group Name: $GroupName" + if ($split.Count -ne 2) + { + Write-Warning "[Get-AzDoGroupPermission] Invalid Group Name: $GroupName" return } @@ -42,9 +85,8 @@ Function Get-xAzDoGroupPermission { # If the Project Name contains 'organization'. Update the Project Name - - Write-Verbose "[Get-xAzDoGroupPermission] Security Namespace: $SecurityNamespace" - Write-Verbose "[Get-xAzDoGroupPermission] Organization Name: $OrganizationName" + Write-Verbose "[Get-AzDoGroupPermission] Security Namespace: $SecurityNamespace" + Write-Verbose "[Get-AzDoGroupPermission] Organization Name: $OrganizationName" # # Construct a hashtable detailing the group @@ -58,20 +100,20 @@ Function Get-xAzDoGroupPermission { reason = $null } - Write-Verbose "[Get-xAzDoGroupPermission] Group result hashtable constructed." - Write-Verbose "[Get-xAzDoGroupPermission] Performing lookup of permissions for the group." + Write-Verbose "[Get-AzDoGroupPermission] Group result hashtable constructed." + Write-Verbose "[Get-AzDoGroupPermission] Performing lookup of permissions for the group." # Define the ACL List $ACLList = [System.Collections.Generic.List[Hashtable]]::new() - # # Perform a Lookup within the Cache for the Group $group = Get-CacheItem -Key $('[{0}]\{1}' -f $ProjectName, $GroupName) -Type 'LiveGroups' $project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' # Test if the Group was found - if (-not $group) { - Throw "[Get-xAzDoGroupPermission] Group not found: $('[{0}]\{1}' -f $ProjectName, $GroupName)" + if (-not $group) + { + Throw "[Get-AzDoGroupPermission] Group not found: $('[{0}]\{1}' -f $ProjectName, $GroupName)" return } @@ -79,7 +121,7 @@ Function Get-xAzDoGroupPermission { # Perform Lookup of the Permissions for the Group $namespace = Get-CacheItem -Key $SecurityNamespace -Type 'SecurityNamespaces' - Write-Verbose "[Get-xAzDoGroupPermission] Retrieved namespace: $($namespace.namespaceId)" + Write-Verbose "[Get-AzDoGroupPermission] Retrieved namespace: $($namespace.namespaceId)" # Add to the ACL Lookup Params $getGroupResult.namespace = $namespace @@ -90,7 +132,7 @@ Function Get-xAzDoGroupPermission { } # Get the ACL List and format the ACLS - Write-Verbose "[Get-xAzDoGroupPermission] ACL Lookup Params: $($ACLLookupParams | Out-String)" + Write-Verbose "[Get-AzDoGroupPermission] ACL Lookup Params: $($ACLLookupParams | Out-String)" $DifferenceACLs = Get-DevOpsACL @ACLLookupParams | ConvertTo-FormattedACL -SecurityNamespace $SecurityNamespace -OrganizationName $OrganizationName $DifferenceACLs = $DifferenceACLs | Where-Object { @@ -101,13 +143,15 @@ Function Get-xAzDoGroupPermission { # # Iterate through each of the Permissions and append the permission identity if it contains 'Self' or 'This' - forEach ($Permission in $Permissions) { - if ($Permission.Identity -in 'self', 'this') { + forEach ($Permission in $Permissions) + { + if ($Permission.Identity -in 'self', 'this') + { $Permission.Identity = '[{0}]\{1}' -f $ProjectName, $GroupName } } - Write-Verbose "[Get-xAzDoGroupPermission] ACL List retrieved and formatted." + Write-Verbose "[Get-AzDoGroupPermission] ACL List retrieved and formatted." # # Convert the Permissions into an ACL Token @@ -117,15 +161,16 @@ Function Get-xAzDoGroupPermission { SecurityNamespace = $SecurityNamespace isInherited = $isInherited OrganizationName = $OrganizationName - TokenName = "{0}\\{1}" -f $project.id, $group.id + TokenName = '{0}\\{1}' -f $project.id, $group.id } # Convert the Permissions to an ACL Token $ReferenceACLs = ConvertTo-ACL @params | Where-Object { $_.token.Type -ne 'GroupUnknown' } # if the ACEs are empty, skip - if ($ReferenceACLs.aces.Count -eq 0) { - Write-Verbose "[Get-xAzDoGroupPermission] No ACEs found for the group." + if ($ReferenceACLs.aces.Count -eq 0) + { + Write-Verbose "[Get-AzDoGroupPermission] No ACEs found for the group." return } @@ -140,8 +185,8 @@ Function Get-xAzDoGroupPermission { $getGroupResult.DifferenceACLs = $DifferenceACLs # Write - Write-Verbose "[Get-xAzDoGroupPermission] Result Status: $($getGroupResult.status)" - Write-Verbose "[Get-xAzDoGroupPermission] Returning Group Result." + Write-Verbose "[Get-AzDoGroupPermission] Result Status: $($getGroupResult.status)" + Write-Verbose "[Get-AzDoGroupPermission] Returning Group Result." # Return the Group Result return $getGroupResult diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/New-AzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/New-AzDoGroupPermission.ps1 new file mode 100644 index 000000000..faab7cc8a --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/New-AzDoGroupPermission.ps1 @@ -0,0 +1,104 @@ +<# +.SYNOPSIS +Creates a new Azure DevOps group permission. + +.DESCRIPTION +The New-AzDoGroupPermission function creates a new permission set for a specified Azure DevOps group. +It formats the group name, retrieves necessary security namespace and project information, +serializes the ACLs, and sets the permissions accordingly. + +.PARAMETER GroupName +Specifies the name of the group for which the permissions are being set. This parameter is mandatory. + +.PARAMETER isInherited +Indicates whether the permissions are inherited. This parameter is mandatory. + +.PARAMETER Permissions +Specifies a hashtable array of permissions to be applied. This parameter is optional. + +.PARAMETER LookupResult +Specifies a hashtable containing lookup results. This parameter is optional. + +.PARAMETER Ensure +Specifies the desired state of the permissions. This parameter is optional. + +.PARAMETER Force +Forces the command to run without asking for user confirmation. This parameter is optional. + +.EXAMPLE +New-AzDoGroupPermission -GroupName "ProjectName\GroupName" -isInherited $true -Permissions $permissions -LookupResult $lookupResult -Ensure Present -Force + +.NOTES +This function requires the Azure DevOps PowerShell module and appropriate permissions to set group permissions. +#> +Function New-AzDoGroupPermission +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$GroupName, + + [Parameter(Mandatory = $true)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[New-AzDoProjectGroupPermission] Started." + + # + # Format the Group Name + + # Split the Group Name + $split = $GroupName.Split('\').Split('/') + + # Test if the Group Name is valid + if ($split.Count -ne 2) + { + Write-Warning "[Get-AzDoProjectGroupPermission] Invalid Group Name: $GroupName" + return + } + + # Define the Project and Group Name + $ProjectName = $split[0] + $GroupName = $split[1] + + # + # Security Namespace ID + + $SecurityNamespace = Get-CacheItem -Key 'Identity' -Type 'SecurityNamespaces' + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + $Group = Get-CacheItem -Key $('[{0}]\{1}' -f $ProjectName, $GroupName) -Type 'LiveGroups' + + # + # Serialize the ACLs + + $serializeACLParams = @{ + ReferenceACLs = $LookupResult.propertiesChanged + DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' + DescriptorMatchToken = ($LocalizedDataAzSerializationPatten.GroupPermission -f $Project.id, $Group.id) + } + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SecurityNamespaceID = $SecurityNamespace.namespaceId + SerializedACLs = ConvertTo-ACLHashtable @serializeACLParams + } + + # + # Set the Git Repository Permissions + + Set-AzDoPermission @params + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Remove-AzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Remove-AzDoGroupPermission.ps1 new file mode 100644 index 000000000..ada4eba6f --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Remove-AzDoGroupPermission.ps1 @@ -0,0 +1,112 @@ +<# +.SYNOPSIS +Removes Azure DevOps group permissions for a specified group. + +.DESCRIPTION +The Remove-AzDoGroupPermission function removes permissions for a specified group in Azure DevOps. +It validates the group name, retrieves the necessary security namespace and project information, +and removes the Access Control Lists (ACLs) associated with the group if they exist. + +.PARAMETER GroupName +Specifies the name of the group whose permissions are to be removed. This parameter is mandatory. + +.PARAMETER isInherited +Indicates whether the permissions are inherited. This parameter is mandatory. + +.PARAMETER Permissions +Specifies a hashtable array of permissions to be removed. This parameter is optional. + +.PARAMETER LookupResult +Specifies a hashtable for lookup results. This parameter is optional. + +.PARAMETER Ensure +Specifies the desired state of the permissions. This parameter is optional. + +.PARAMETER Force +Forces the removal of permissions without prompting for confirmation. This parameter is optional. + +.EXAMPLE +Remove-AzDoGroupPermission -GroupName "ProjectName\GroupName" -isInherited $true + +This example removes the permissions for the specified group in the given project. + +.NOTES +The function uses cached items to retrieve security namespace, project, and repository information. +It filters the ACLs related to the Git repository and removes them if they exist. +#> +Function Remove-AzDoGroupPermission +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$GroupName, + + [Parameter(Mandatory = $true)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + + Write-Verbose "[Remove-AzDoGroupPermission] Started." + + # + # Format the Group Name + + # Split the Group Name + $split = $GroupName.Split('\').Split('/') + + # Test if the Group Name is valid + if ($split.Count -ne 2) + { + Write-Warning "[Get-AzDoProjectGroupPermission] Invalid Group Name: $GroupName" + return + } + + # Define the Project and Group Name + $ProjectName = $split[0] + $GroupName = $split[1] + + # + # Security Namespace ID + + $SecurityNamespace = Get-CacheItem -Key 'Identity' -Type 'SecurityNamespaces' + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + $Repository = Get-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' + $DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' + + # + # Filter the ACLs that pertain to the Git Repository + + $searchString = 'repoV2/{0}/{1}' -f $Project.id, $Repository.id + + # Test if the Token exists + $Filtered = $DescriptorACLList | Where-Object { $_.token -eq $searchString } + + # If the ACLs are not null, remove them + if ($Filtered) + { + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SecurityNamespaceID = $SecurityNamespace.namespaceId + TokenName = $searchString + } + + # Remove the ACLs + Remove-AzDoPermission @params + + } + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Set-AzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Set-AzDoGroupPermission.ps1 new file mode 100644 index 000000000..8b4c779c4 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Set-AzDoGroupPermission.ps1 @@ -0,0 +1,104 @@ +<# +.SYNOPSIS +Sets Azure DevOps group permissions. + +.DESCRIPTION +The Set-AzDoGroupPermission function sets permissions for a specified Azure DevOps group. +It formats the group name, retrieves necessary security namespace and project information, +serializes ACLs, and applies the permissions. + +.PARAMETER GroupName +The name of the group for which permissions are being set. This parameter is mandatory. + +.PARAMETER isInherited +A boolean value indicating whether the permissions are inherited. This parameter is mandatory. + +.PARAMETER Permissions +A hashtable array containing the permissions to be set. This parameter is optional. + +.PARAMETER LookupResult +A hashtable containing the lookup results. This parameter is optional. + +.PARAMETER Ensure +Specifies whether the permissions should be ensured. This parameter is optional. + +.PARAMETER Force +A switch parameter to force the operation. This parameter is optional. + +.EXAMPLE +Set-AzDoGroupPermission -GroupName "ProjectName\GroupName" -isInherited $true -Permissions $permissions -LookupResult $lookupResult -Ensure Present -Force + +.NOTES +This function relies on cached items for security namespace and project information. +#> + +Function Set-AzDoGroupPermission +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$GroupName, + + [Parameter(Mandatory = $true)] + [bool]$isInherited, + + [Parameter()] + [HashTable[]]$Permissions, + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + Write-Verbose "[Set-AzDoGroupPermission] Started." + + # + # Format the Group Name + + # Split the Group Name + $split = $GroupName.Split('\').Split('/') + + # Test if the Group Name is valid + if ($split.Count -ne 2) + { + Write-Warning "[Get-AzDoProjectGroupPermission] Invalid Group Name: $GroupName" + return + } + + # Define the Project and Group Name + $ProjectName = $split[0] + $GroupName = $split[1] + + # + # Security Namespace ID + + $SecurityNamespace = Get-CacheItem -Key 'Identity' -Type 'SecurityNamespaces' + $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + # + # Serialize the ACLs + + $serializeACLParams = @{ + ReferenceACLs = $LookupResult.propertiesChanged + DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' + DescriptorMatchToken = ($LocalizedDataAzSerializationPatten.GitRepository -f $Project.id) + } + + $params = @{ + OrganizationName = $Global:DSCAZDO_OrganizationName + SecurityNamespaceID = $SecurityNamespace.namespaceId + SerializedACLs = ConvertTo-ACLHashtable @serializeACLParams + } + + # + # Set the Git Repository Permissions + + Set-AzDoPermission @params + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Get-AzDoOrganizationGroup.ps1 similarity index 61% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Get-AzDoOrganizationGroup.ps1 index 3608ad729..de551dde8 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Get-AzDoOrganizationGroup.ps1 @@ -3,7 +3,7 @@ Retrieves an organization group from Azure DevOps. .DESCRIPTION -The Get-xAzDoOrganizationGroup function retrieves an organization group from Azure DevOps based on the provided parameters. +The Get-AzDoOrganizationGroup function retrieves an organization group from Azure DevOps based on the provided parameters. .PARAMETER ApiUri The URI of the Azure DevOps API. This parameter is validated using the Test-AzDevOpsApiUri function. @@ -19,18 +19,18 @@ The name of the organization group to retrieve. The retrieved organization group. .EXAMPLE -Get-xAzDoOrganizationGroup -ApiUri 'https://dev.azure.com/contoso' -Pat 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx' -GroupName 'Developers' +Get-AzDoOrganizationGroup -ApiUri 'https://dev.azure.com/contoso' -Pat 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx' -GroupName 'Developers' Retrieves the organization group named 'Developers' from the Azure DevOps instance at 'https://dev.azure.com/contoso' using the provided PAT. #> -Function Get-xAzDoOrganizationGroup { - +Function Get-AzDoOrganizationGroup +{ [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$GroupName, @@ -43,17 +43,14 @@ Function Get-xAzDoOrganizationGroup { [Parameter()] [Ensure]$Ensure - ) # Logging - Write-Verbose "[Get-xAzDoOrganizationGroup] Retriving the GroupName from the Live and Local Cache." + Write-Verbose "[Get-AzDoOrganizationGroup] Retriving the GroupName from the Live and Local Cache." - # # Format the Key According to the Principal Name $Key = Format-AzDoGroup -Prefix "[$Global:DSCAZDO_OrganizationName]" -GroupName $GroupName - # # Check the cache for the group $livegroup = Get-CacheItem -Key $Key -Type 'LiveGroups' @@ -61,10 +58,8 @@ Function Get-xAzDoOrganizationGroup { # Check if the group is in the cache $localgroup = Get-CacheItem -Key $Key -Type 'Group' + Write-Verbose "[Get-AzDoOrganizationGroup] GroupName: '$GroupName'" - Write-Verbose "[Get-xAzDoOrganizationGroup] GroupName: '$GroupName'" - - # # Construct a hashtable detailing the group $getGroupResult = @{ #Reasons = $() @@ -75,34 +70,34 @@ Function Get-xAzDoOrganizationGroup { status = $null } - Write-Verbose "[Get-xAzDoOrganizationGroup] Testing LocalCache, LiveCache and Parameters." + Write-Verbose "[Get-AzDoOrganizationGroup] Testing LocalCache, LiveCache and Parameters." # # If the localgroup and lifegroup are present, compare the properties as well as the originId - if (($null -ne $livegroup.originId) -and ($null -ne $localgroup.originId)) { - - Write-Verbose "[Get-xAzDoOrganizationGroup] Testing LocalCache, LiveCache and Parameters." + if (($null -ne $livegroup.originId) -and ($null -ne $localgroup.originId)) + { + Write-Verbose "[Get-AzDoOrganizationGroup] Testing LocalCache, LiveCache and Parameters." # # Check if the originId is the same. If so, the group is unchanged. If not, the group has been renamed. - if ($livegroup.originId -ne $localgroup.originId) { + if ($livegroup.originId -ne $localgroup.originId) + { # The group has been renamed or deleted and recreated. # Perform a lookup in the live cache to see if the group has been deleted and recreated. $renamedGroup = $livegroup | Find-CacheItem { $_.originId -eq $livegroup.originId } # If renamed group is not null, the group has been renamed. - if ($null -ne $renamedGroup) { + if ($null -ne $renamedGroup) + { # Add the renamed group to result $getGroupResult.renamedGroup = $renamedGroup # The group has been renamed. $getGroupResult.status = [DSCGetSummaryState]::Renamed - - # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:RenamedGroup', 'The group was renamed') - - } else { + } + else + { # The group has been deleted and recreated. Treat the new group as the live group. # Remove the old group from the local cache @@ -111,38 +106,61 @@ Function Get-xAzDoOrganizationGroup { Add-CacheItem -Key $Key -Value $livegroup -Type 'Group' # Compare the properties of the live group with the parameters - if ($livegroup.description -ne $groupDescription) { $getGroupResult.propertiesChanged += 'description' } - if ($livegroup.name -ne $localgroup.name) { $getGroupResult.propertiesChanged += 'displayName' } + if ($livegroup.description -ne $groupDescription) + { + $getGroupResult.propertiesChanged += 'description' + } + + if ($livegroup.name -ne $localgroup.name){ + $getGroupResult.propertiesChanged += 'displayName' + } # If the properties are the same, the group is unchanged. If not, the group has been changed. - if ($getGroupResult.propertiesChanged.count -ne 0) { + if ($getGroupResult.propertiesChanged.count -ne 0) + { # Update the Result $getGroupResult.status = [DSCGetSummaryState]::Changed # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Deleted&ReCreate', 'The group was deleted and recreated with another group. The properties have changed') - } else { + #$getGroupResult.Reasons += [DscResourceReason]::New('AzDoOrganizationGroup:AzDoOrganizationGroup:Deleted&ReCreate', 'The group was deleted and recreated with another group. The properties have changed') + } + else + { # Update the Result $getGroupResult.status = [DSCGetSummaryState]::Unchanged } } - return $getGroupResult - } - # # The Group hasn't been renamed. Test the properties to make sure they are the same as the parameters. # Compare the properties of the live group with the parameters - if ($livegroup.description -ne $groupDescription) { $getGroupResult.propertiesChanged += 'Description' } - if ($livegroup.name -ne $localgroup.name) { $getGroupResult.propertiesChanged += 'Name' } + if ($livegroup.description -ne $groupDescription) + { + $getGroupResult.propertiesChanged += 'Description' + } + if ($livegroup.name -ne $localgroup.name) + { + $getGroupResult.propertiesChanged += 'Name' + } # If the properties are the same, the group is unchanged. If not, the group has been changed. - $getGroupResult.status = ($getGroupResult.propertiesChanged.count -ne 0) ? [DSCGetSummaryState]::Changed : [DSCGetSummaryState]::Unchanged - if ($getGroupResult.status -eq [DSCGetSummaryState]::Changed) { + $getGroupResult.status = $( + if ($getGroupResult.propertiesChanged.count -ne 0) + { + [DSCGetSummaryState]::Changed + } + else + { + [DSCGetSummaryState]::Unchanged + } + ) + + if ($getGroupResult.status -eq [DSCGetSummaryState]::Changed) + { # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Changed', 'The group has changed') + #$getGroupResult.Reasons += [DscResourceReason]::New('AzDoOrganizationGroup:AzDoOrganizationGroup:Changed', 'The group has changed') } else { $getGroupResult.Ensure = [Ensure]::Present } @@ -152,33 +170,52 @@ Function Get-xAzDoOrganizationGroup { } - # # If the livegroup is not present and the localgroup is present, the group is missing and recreate it. - if (($null -eq $livegroup) -and ($null -ne $localgroup)) { + if (($null -eq $livegroup) -and ($null -ne $localgroup)) + { $getGroupResult.status = [DSCGetSummaryState]::NotFound $getGroupResult.propertiesChanged = @('description', 'displayName') # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Removed', 'The group is missing') return $getGroupResult } - # - # If the localgroup is not present and the livegroup is present, the group is not found. Check the properties are the same as the parameters. - # If the properties are the same, the group is unchanged. If not, the group has been deleted and then recreated and the new group will become authoritative. - # - if (($null -eq $localgroup) -and ($null -ne $livegroup)) { + <# + If the localgroup is not present and the livegroup is present, the group is not found. Check the properties are the same as the parameters. + If the properties are the same, the group is unchanged. If not, the group has been deleted and then recreated and the new group will become authoritative. + #> + + if (($null -eq $localgroup) -and ($null -ne $livegroup)) + { # Validate that the live properties are the same as the parameters - if ($livegroup.description -ne $GroupDescription ) { $getGroupResult.propertiesChanged += 'description' } - if ($livegroup.displayName -ne $GroupName ) { $getGroupResult.propertiesChanged += 'displayName' } + if ($livegroup.description -ne $GroupDescription ) + { + $getGroupResult.propertiesChanged += 'description' + } + if ($livegroup.displayName -ne $GroupName ) + { + $getGroupResult.propertiesChanged += 'displayName' + } + # If the properties are the same, the group is unchanged. If not, the group has been changed. - $getGroupResult.status = ($getGroupResult.propertiesChanged.count -ne 0) ? [DSCGetSummaryState]::Changed : [DSCGetSummaryState]::Unchanged - if ($getGroupResult.status -ne [DSCGetSummaryState]::Unchanged) { - # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Missing', 'The group is missing') + $getGroupResult.status = $( + if ($getGroupResult.propertiesChanged.count -ne 0) + { + [DSCGetSummaryState]::Changed + } + else + { + [DSCGetSummaryState]::Unchanged + } + ) + + if ($getGroupResult.status -ne [DSCGetSummaryState]::Unchanged) + { # Set the Ensure to Present $getGroupResult.Ensure = [Ensure]::Present - } else { + } + else + { # Add the unchanged group to the local cache Add-CacheItem -Key $Key -Value $livegroup -Type 'Group' } @@ -188,13 +225,13 @@ Function Get-xAzDoOrganizationGroup { } - # # If the livegroup and localgroup are not present, the group is missing and recreate it. - if (($null -eq $livegroup) -and ($null -eq $localgroup)) { + if (($null -eq $livegroup) -and ($null -eq $localgroup)) + { $getGroupResult.status = [DSCGetSummaryState]::NotFound $getGroupResult.propertiesChanged = @('description', 'displayName') # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:NotFound', 'The group is not found') + #$getGroupResult.Reasons += [DscResourceReason]::New('AzDoOrganizationGroup:AzDoOrganizationGroup:NotFound', 'The group is not found') return $getGroupResult } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/New-AzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/New-AzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..f65ca9389 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/New-AzDoOrganizationGroup.ps1 @@ -0,0 +1,81 @@ +<# +.SYNOPSIS +Creates a new Azure DevOps organization group. + +.DESCRIPTION +The New-AzDoOrganizationGroup function creates a new group in an Azure DevOps organization. +It accepts parameters for the group name, description, lookup result, ensure, and force. +The function logs verbose messages during the creation process and updates the cache with the new group information. + +.PARAMETER GroupName +Specifies the name of the group to be created. This parameter is mandatory. + +.PARAMETER GroupDescription +Specifies the description of the group to be created. This parameter is optional. + +.PARAMETER LookupResult +Specifies a hashtable for lookup results. This parameter is optional. + +.PARAMETER Ensure +Specifies the desired state of the group. This parameter is optional. + +.PARAMETER Force +Forces the creation of the group without confirmation. This parameter is optional. + +.EXAMPLE +PS C:\> New-AzDoOrganizationGroup -GroupName "Developers" -GroupDescription "Development Team" + +This command creates a new Azure DevOps group named "Developers" with the description "Development Team". + +#> +Function New-AzDoOrganizationGroup +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Define parameters for creating a new DevOps group + $params = @{ + GroupName = $GroupName + GroupDescription = $GroupDescription + ApiUri = 'https://vssps.dev.azure.com/{0}' -f $Global:DSCAZDO_OrganizationName + } + + # Write verbose log with the parameters used for creating the group + Write-Verbose "[New-AzDoOrganizationGroup] Creating a new DevOps group with GroupName: '$($params.GroupName)', GroupDescription: '$($params.GroupDescription)' and ApiUri: '$($params.ApiUri)'" + + # Create a new group + $group = New-DevOpsGroup @params + + # Update the cache with the new group + Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' + + # Add the group to the Group cache and write to verbose log + Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' + Write-Verbose "[New-AzDoOrganizationGroup] Added new group to Group cache with key: '$($group.principalName)'" + + # Update the global AzDoGroup object and write to verbose log + Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' + Write-Verbose "[New-AzDoOrganizationGroup] Updated global AzDoGroup cache object." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Remove-AzDoOrganizationGroup.ps1 similarity index 50% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Remove-AzDoOrganizationGroup.ps1 index 2f48323a2..19ef7b6a0 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Remove-AzDoOrganizationGroup.ps1 @@ -1,11 +1,43 @@ -Function Remove-xAzDoOrganizationGroup { +<# +.SYNOPSIS +Removes an Azure DevOps organization group. +.DESCRIPTION +The Remove-AzDoOrganizationGroup function removes a specified Azure DevOps organization group. +It uses the provided group name, description, and lookup result to identify and remove the group +from the Azure DevOps API and local cache. + +.PARAMETER GroupName +The name of the group to be removed. This parameter is mandatory. + +.PARAMETER GroupDescription +The description of the group to be removed. This parameter is optional. + +.PARAMETER LookupResult +A hashtable containing the lookup result for the group. This parameter is optional. + +.PARAMETER Ensure +Specifies whether the group should be present or absent. This parameter is optional. + +.PARAMETER Force +A switch parameter to force the removal of the group without confirmation. This parameter is optional. + +.EXAMPLE +Remove-AzDoOrganizationGroup -GroupName "Developers" -Force + +This example removes the "Developers" group from the Azure DevOps organization without confirmation. + +.NOTES +This function relies on the global variables $Global:DSCAZDO_OrganizationName, $Global:AZDOLiveGroups, +and $Global:AzDoGroup to interact with the Azure DevOps API and manage cache objects. +#> +Function Remove-AzDoOrganizationGroup +{ [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( - - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$GroupName, @@ -26,13 +58,14 @@ Function Remove-xAzDoOrganizationGroup { ) # If no cache items exist, return. - if (($null -eq $LookupResult.liveCache) -and ($null -eq $LookupResult.localCache)) { + if (($null -eq $LookupResult.liveCache) -and ($null -eq $LookupResult.localCache)) + { return } $params = @{ GroupDescriptor = $LookupResult.liveCache.Descriptor - ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName + ApiUri = 'https://vssps.dev.azure.com/{0}' -f $Global:DSCAZDO_OrganizationName } $cacheItem = @{ @@ -40,7 +73,8 @@ Function Remove-xAzDoOrganizationGroup { } # If the group is not found, return - if (($null -ne $LookupResult.localCache) -and ($null -eq $LookupResult.liveCache)) { + if (($null -ne $LookupResult.localCache) -and ($null -eq $LookupResult.liveCache)) + { $cacheItem.Key = $LookupResult.localCache.principalName $params.GroupDescriptor = $LookupResult.localCache.Descriptor } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Set-AzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Set-AzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..5e8f0dfa5 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Set-AzDoOrganizationGroup.ps1 @@ -0,0 +1,104 @@ +<# +.SYNOPSIS +Sets or updates an Azure DevOps organization group. + +.DESCRIPTION +The Set-AzDoOrganizationGroup function sets or updates an Azure DevOps organization group based on the provided parameters. +It handles renaming, updating group details, and managing cache updates. + +.PARAMETER GroupName +Specifies the name of the group to be set or updated. This parameter is mandatory. + +.PARAMETER GroupDescription +Specifies the description of the group to be set or updated. This parameter is optional. + +.PARAMETER LookupResult +A hashtable containing the lookup result, which includes the status and cache information of the group. This parameter is optional. + +.PARAMETER Ensure +Specifies the desired state of the group. This parameter is optional. + +.PARAMETER Force +A switch parameter that forces the operation to proceed without confirmation. This parameter is optional. + +.EXAMPLE +Set-AzDoOrganizationGroup -GroupName "Developers" -GroupDescription "Development Team" -LookupResult $lookupResult + +This example sets or updates the "Developers" group with the description "Development Team" using the provided lookup result. + +.NOTES +If the group has been renamed, a warning is written and the function returns without making any changes. +The function updates the group using the Azure DevOps API and refreshes the cache with the new group information. +#> + +Function Set-AzDoOrganizationGroup +{ + param( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # + # Depending on the type of lookup status, the group has been renamed the group has been deleted and recreated. + if ($LookupResult.Status -eq [DSCGetSummaryState]::Renamed) + { + # For the time being write a warning and return + Write-Warning "[Set-AzDoOrganizationGroup] The group has been renamed. The group will not be set." + return + } + + # + # Update the group + $params = @{ + ApiUri = 'https://vssps.dev.azure.com/{0}' -f $Global:DSCAZDO_OrganizationName + GroupName = $GroupName + GroupDescription = $GroupDescription + GroupDescriptor = $LookupResult.liveCache.descriptor + } + + try + { + # Set the group from the API + $group = Set-DevOpsGroup @params + } + catch + { + throw $_ + } + + # + # Update the cache with the new group + Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' + + # + # Secondarily Replace the local cache with the new group + + if ($null -ne $LookupResult.localCache) + { + Remove-CacheItem -Key $LookupResult.localCache.principalName -Type 'Group' + } + + Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' + Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' + + # + # Return the group from the cache + return $group + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Test-AzDoOrganizationGroup.ps1 similarity index 78% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Test-AzDoOrganizationGroup.ps1 index b846fe0ef..ed7c35cd4 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Test-AzDoOrganizationGroup.ps1 @@ -22,7 +22,7 @@ Returns $true if the organization group exists, otherwise returns $false. .EXAMPLE - Test-xAzDoOrganizationGroup -GroupName 'MyGroup' -Pat '********' -ApiUri 'https://dev.azure.com/myorg' + Test-AzDoOrganizationGroup -GroupName 'MyGroup' -Pat '********' -ApiUri 'https://dev.azure.com/myorg' Description ----------- @@ -30,10 +30,10 @@ using the specified personal access token and API URI. #> -Function Test-xAzDoOrganizationGroup { - +Function Test-AzDoOrganizationGroup +{ param( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $GroupName, @@ -45,62 +45,64 @@ Function Test-xAzDoOrganizationGroup { [Parameter()] [Alias('Name')] [hashtable]$GetResult - ) - # # Firstly we need to compare to see if the group names are the same. If so we can return $false. - - if ($GetResult.Status -eq [DSCGetSummaryState]::Unchanged ) { + if ($GetReslut.Status -eq [DSCGetSummaryState]::Unchanged ) + { $result = $true if ($GroupDescription -eq $GetResult.Current.description) { - $GetResult. $result = $false } - return $true } + return $true + } - # # If the status has been flagged as 'Renamed', returned $true. This means that the originId has changed. - if ($GetResult.Status -eq [DSCGetSummaryState]::Renamed) { return $false } + if ($GetResult.Status -eq [DSCGetSummaryState]::Renamed) + { + return $false + } - # # If the status has been flagged as 'Missing', returned $true. This means that the group is missing from the live cache. - - - - if ($GetResult.Status -eq [DSCGetSummaryState]::Changed) { - - # + if ($GetResult.Status -eq [DSCGetSummaryState]::Changed) + { # If the group is present in the live cache and the local cache. This means that the originId has changed. This needs to be updated. - if (($null -ne $GetResult.Current) -and ($null -ne $GetResult.Cache)) { + if (($null -ne $GetResult.Current) -and ($null -ne $GetResult.Cache)) + { return $true } - # # If the group is present in the live cache but not in the local cache. Flag as Changed. - if ($GetResult.Current -and -not($GetResult.Cache)) { + if ($GetResult.Current -and -not($GetResult.Cache)) + { return $true } - # # If the group is not present in the live cache but is in the local cache. Flag as Changed. - if (-not($GetResult.Current) -and $GetResult.Cache) { + if (-not($GetResult.Current) -and $GetResult.Cache) + { return $true } } - # Format the Key According to the Principal Name $Key = Format-AzDoGroup -Prefix "[$Global:DSCAZDO_OrganizationName]" -GroupName $GroupName # # Check the cache for the group $group = Get-CacheItem -Key $Key -Type 'LiveGroups' - if (-not($group)) { $false } else { $true } + if (-not($group)) + { + $false + } + else + { + $true + } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.ps1 new file mode 100644 index 000000000..ffcfd0fca --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.ps1 @@ -0,0 +1,152 @@ +<# +.SYNOPSIS + Retrieves information about an Azure DevOps project. + +.DESCRIPTION + The Get-AzDoProject function retrieves details about an Azure DevOps project, including its name, description, source control type, process template, and visibility. It performs lookups to check if the project and process template exist and returns the project's status and any properties that have changed. + +.PARAMETER ProjectName + The name of the Azure DevOps project. This parameter is validated using the Test-AzDevOpsProjectName function. + +.PARAMETER ProjectDescription + The description of the Azure DevOps project. Defaults to an empty string if not specified. + +.PARAMETER SourceControlType + The source control type of the Azure DevOps project. Valid values are 'Git' and 'Tfvc'. Defaults to 'Git'. + +.PARAMETER ProcessTemplate + The process template used by the Azure DevOps project. Valid values are 'Agile', 'Scrum', 'CMMI', and 'Basic'. Defaults to 'Agile'. + +.PARAMETER Visibility + The visibility of the Azure DevOps project. Valid values are 'Public' and 'Private'. Defaults to 'Private'. + +.PARAMETER LookupResult + A hashtable to store the lookup result. + +.PARAMETER Ensure + Specifies the desired state of the project. + +.OUTPUTS + [System.Management.Automation.PSObject[]] + Returns a hashtable containing the project's details and status. + +.EXAMPLE + Get-AzDoProject -ProjectName "MyProject" -ProjectDescription "Sample project" -SourceControlType "Git" -ProcessTemplate "Agile" -Visibility "Private" + + Retrieves information about the Azure DevOps project named "MyProject" with the specified parameters. + +.NOTES + This function relies on global variables and other functions such as Get-CacheItem and Test-AzDevOpsProjectName to perform lookups and validations. +#> +function Get-AzDoProject +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param ( + [Parameter()] + [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] + [Alias('Name')] + [System.String] $ProjectName, + + [Parameter()] + [Alias('Description')] + [System.String] $ProjectDescription = '', + + [Parameter()] + [ValidateSet('Git', 'Tfvc')] + [System.String] $SourceControlType = 'Git', + + [Parameter()] + [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] + [System.String] $ProcessTemplate = 'Agile', + + [Parameter()] + [ValidateSet('Public', 'Private')] + [System.String] $Visibility = 'Private', + + [Parameter()] + [HashTable] $LookupResult, + + [Parameter()] + [Ensure] $Ensure + ) + + Write-Verbose "[Get-AzDoProject] Started." + + # Set the organization name + $OrganizationName = $Global:DSCAZDO_OrganizationName + Write-Verbose "[Get-AzDoProject] Organization Name: $OrganizationName" + + # Construct a hashtable detailing the group + $result = @{ + Ensure = [Ensure]::Absent + ProjectName = $ProjectName + ProjectDescription = $ProjectDescription + SourceControlType = $SourceControlType + ProcessTemplate = $ProcessTemplate + Visibility = $Visibility + propertiesChanged = @() + status = $null + } + Write-Verbose "[Get-AzDoProject] Initial result hashtable constructed." + + # Perform a lookup to see if the project exists in Azure DevOps + $project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + # Set the project description to be a string if it is not already. + Write-Verbose "[Get-AzDoProject] Project lookup result: $project" + + $processTemplateObj = Get-CacheItem -Key $ProcessTemplate -Type 'LiveProcesses' + Write-Verbose "[Get-AzDoProject] Process template lookup result: $processTemplateObj" + + # Test if the project exists. If the project does not exist, return NotFound + if (($null -eq $project) -and ($null -ne $ProjectName)) + { + $result.Status = [DSCGetSummaryState]::NotFound + Write-Verbose "[Get-AzDoProject] Project not found." + return $result + } + + # Test if the process template exists. If the process template does not exist, throw an error. + if ($null -eq $processTemplateObj) + { + throw "[Get-AzDoProject] Process template '$processTemplateObj' not found." + } + + Write-Verbose "[Get-AzDoProject] Testing source control type." + + # Test if the project is using the same source control type. If the source control type is different, return a conflict. + if ($SourceControlType -ne $project.SourceControlType) + { + Write-Warning "[Get-AzDoProject] Source control type is different. Current: $($project.SourceControlType), Desired: $SourceControlType" + Write-Warning "[Get-AzDoProject] Source control type cannot be changed. Please delete the project and recreate it." + } + + # If the project description is null, set it to an empty string. + if ($null -eq $project.description) + { + $project | Add-Member -MemberType NoteProperty -Name description -Value '' + Write-Verbose "[Get-AzDoProject] Project description was null, set to empty string." + } + + # Test if the project description is the same. If the description is different, return a conflict. + if ($ProjectDescription.Trim() -ne $project.description.Trim()) + { + $result.Status = [DSCGetSummaryState]::Changed + $result.propertiesChanged += 'Description' + Write-Verbose "[Get-AzDoProject] Project description has changed." + } + + # Test if the project visibility is the same. If the visibility is different, return a conflict. + if ($Visibility -ne $project.Visibility) + { + $result.Status = [DSCGetSummaryState]::Changed + $result.propertiesChanged += 'Visibility' + Write-Verbose "[Get-AzDoProject] Project visibility has changed." + } + + # Return the group from the cache + Write-Verbose "[Get-AzDoProject] Returning final result." + + return [PSCustomObject]$result + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/New-AzDoProject.ps1 similarity index 52% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/New-AzDoProject.ps1 index e9d5c92c6..6e5676dc2 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/New-AzDoProject.ps1 @@ -1,4 +1,43 @@ -function New-xAzDoProject +<# +.SYNOPSIS +Creates a new Azure DevOps project. + +.DESCRIPTION +The New-AzDoProject function creates a new project in Azure DevOps with the specified parameters. +It supports setting the project name, description, source control type, process template, and visibility. +The function also ensures the project is created by waiting for the project creation job to complete and refreshes the cache once the project is created. + +.PARAMETER ProjectName +Specifies the name of the Azure DevOps project. The name is validated using the Test-AzDevOpsProjectName function. + +.PARAMETER ProjectDescription +Specifies the description of the Azure DevOps project. + +.PARAMETER SourceControlType +Specifies the type of source control for the project. Valid values are 'Git' and 'Tfvc'. The default value is 'Git'. + +.PARAMETER ProcessTemplate +Specifies the process template for the project. Valid values are 'Agile', 'Scrum', 'CMMI', and 'Basic'. The default value is 'Agile'. + +.PARAMETER Visibility +Specifies the visibility of the project. Valid values are 'Public' and 'Private'. The default value is 'Private'. + +.PARAMETER LookupResult +Specifies a hashtable for lookup results. + +.PARAMETER Ensure +Specifies the desired state of the project. + +.PARAMETER Force +Forces the creation of the project without prompting for confirmation. + +.EXAMPLE +PS C:\> New-AzDoProject -ProjectName "MyProject" -ProjectDescription "This is a sample project" -SourceControlType "Git" -ProcessTemplate "Agile" -Visibility "Private" + +Creates a new Azure DevOps project named "MyProject" with the specified description, source control type, process template, and visibility. + +#> +function New-AzDoProject { [CmdletBinding()] param @@ -36,7 +75,6 @@ function New-xAzDoProject [Parameter()] [System.Management.Automation.SwitchParameter] $Force - ) # Set the organization name diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Remove-AzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Remove-AzDoProject.ps1 new file mode 100644 index 000000000..49b30462f --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Remove-AzDoProject.ps1 @@ -0,0 +1,101 @@ +<# +.SYNOPSIS +Removes an Azure DevOps project. + +.DESCRIPTION +The Remove-AzDoProject function removes a specified project from Azure DevOps. It performs a lookup to check if the project exists in the cache, removes it from Azure DevOps, and updates the local cache accordingly. + +.PARAMETER ProjectName +Specifies the name of the Azure DevOps project to be removed. This parameter is validated using the Test-AzDevOpsProjectName function. + +.PARAMETER ProjectDescription +Specifies the description of the Azure DevOps project. + +.PARAMETER SourceControlType +Specifies the type of source control for the project. Valid values are 'Git' and 'Tfvc'. The default value is 'Git'. + +.PARAMETER ProcessTemplate +Specifies the process template for the project. Valid values are 'Agile', 'Scrum', 'CMMI', and 'Basic'. The default value is 'Agile'. + +.PARAMETER Visibility +Specifies the visibility of the project. Valid values are 'Public' and 'Private'. The default value is 'Private'. + +.PARAMETER LookupResult +Specifies a hashtable to store the lookup result. + +.PARAMETER Ensure +Specifies the desired state of the project. + +.PARAMETER Force +Forces the removal of the project without prompting for confirmation. + +.EXAMPLE +Remove-AzDoProject -ProjectName "MyProject" -Force + +This command removes the Azure DevOps project named "MyProject" without prompting for confirmation. + +.NOTES +The function uses global variable $Global:DSCAZDO_OrganizationName to get the organization name. +#> +function Remove-AzDoProject +{ + [CmdletBinding()] + param ( + [Parameter()] + [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] + [Alias('Name')] + [System.String] $ProjectName, + + [Parameter()] + [Alias('Description')] + [System.String] $ProjectDescription, + + [Parameter()] + [ValidateSet('Git', 'Tfvc')] + [System.String] $SourceControlType = 'Git', + + [Parameter()] + [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] + [System.String] $ProcessTemplate = 'Agile', + + [Parameter()] + [ValidateSet('Public', 'Private')] + [System.String] $Visibility = 'Private', + + [Parameter()] + [HashTable] $LookupResult, + + [Parameter()] + [Ensure] $Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] $Force + ) + + # Set the organization name + $OrganizationName = $Global:DSCAZDO_OrganizationName + Write-Verbose "[Remove-AzDoProject] Using organization name: $OrganizationName" + + # Perform a lookup to see if the group exists in Azure DevOps + Write-Verbose "[Remove-AzDoProject] Looking up project: $ProjectName" + $project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' + + if ($null -eq $project) + { + Write-Verbose "[Remove-AzDoProject] Project $ProjectName not found in cache." + return + } + + Write-Verbose "[Remove-AzDoProject] Found project $ProjectName with ID: $($project.id)" + + # Remove the project + Write-Verbose "[Remove-AzDoProject] Removing project $ProjectName from Azure DevOps" + Remove-DevOpsProject -Organization $OrganizationName -ProjectId $project.id + + # Remove the project from the cache and export the cache + Write-Verbose "[Remove-AzDoProject] Removing project $ProjectName from local cache" + Remove-CacheItem -Key $ProjectName -Type 'LiveProjects' + + Write-Verbose "[Remove-AzDoProject] Exporting updated cache object for LiveProjects" + Export-CacheObject -CacheType 'LiveProjects' -Content $AzDoLiveProjects +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Set-AzDoProject.ps1 similarity index 51% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Set-AzDoProject.ps1 index 8b4373973..bad8b7615 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Set-AzDoProject.ps1 @@ -1,4 +1,41 @@ -function Set-xAzDoProject +<# +.SYNOPSIS +Updates an existing Azure DevOps project with the specified parameters. + +.DESCRIPTION +The Set-AzDoProject function updates an existing Azure DevOps project with the provided project name, description, source control type, process template, and visibility. It performs a lookup to see if the project exists in Azure DevOps, constructs the parameters for the API call, updates the project, waits for the update to complete, and refreshes the cache. + +.PARAMETER ProjectName +Specifies the name of the Azure DevOps project to update. This parameter is validated using the Test-AzDevOpsProjectName function. + +.PARAMETER ProjectDescription +Specifies the description of the Azure DevOps project. + +.PARAMETER SourceControlType +Specifies the source control type for the project. Valid values are 'Git' and 'Tfvc'. The default value is 'Git'. + +.PARAMETER ProcessTemplate +Specifies the process template for the project. Valid values are 'Agile', 'Scrum', 'CMMI', and 'Basic'. The default value is 'Agile'. + +.PARAMETER Visibility +Specifies the visibility of the project. Valid values are 'Public' and 'Private'. The default value is 'Private'. + +.PARAMETER LookupResult +Specifies a hashtable to store the lookup result. + +.PARAMETER Ensure +Specifies whether to ensure the project exists or not. + +.PARAMETER Force +Specifies whether to force the update of the project. + +.EXAMPLE +Set-AzDoProject -ProjectName "MyProject" -ProjectDescription "This is a sample project" -SourceControlType "Git" -ProcessTemplate "Agile" -Visibility "Private" + +This example updates the Azure DevOps project named "MyProject" with the specified description, source control type, process template, and visibility. + +#> +function Set-AzDoProject { [CmdletBinding()] param @@ -36,7 +73,6 @@ function Set-xAzDoProject [Parameter()] [System.Management.Automation.SwitchParameter] $Force - ) $OrganizationName = $Global:DSCAZDO_OrganizationName diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.ps1 new file mode 100644 index 000000000..fd9ecfeb9 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.ps1 @@ -0,0 +1,80 @@ +<# +.SYNOPSIS + Tests the existence and properties of an Azure DevOps project. + +.DESCRIPTION + The Test-AzDoProject function checks if an Azure DevOps project exists and validates its properties such as name, description, source control type, process template, and visibility. + +.PARAMETER ProjectName + The name of the Azure DevOps project. This parameter is validated using the Test-AzDevOpsProjectName function. + +.PARAMETER ProjectDescription + The description of the Azure DevOps project. + +.PARAMETER SourceControlType + The type of source control used by the project. Valid values are 'Git' and 'Tfvc'. The default value is 'Git'. + +.PARAMETER ProcessTemplate + The process template used by the project. Valid values are 'Agile', 'Scrum', 'CMMI', and 'Basic'. The default value is 'Agile'. + +.PARAMETER Visibility + The visibility of the project. Valid values are 'Public' and 'Private'. The default value is 'Private'. + +.PARAMETER LookupResult + A PSCustomObject that contains the lookup result for the project. + +.PARAMETER Ensure + Specifies whether the project should exist or not. + +.PARAMETER Force + A switch parameter to force the operation. + +.EXAMPLE + Test-AzDoProject -ProjectName "MyProject" -ProjectDescription "This is a sample project" -SourceControlType "Git" -ProcessTemplate "Agile" -Visibility "Private" + +.NOTES + This function is a placeholder and should not be triggered. +#> +function Test-AzDoProject +{ + [CmdletBinding()] + param + ( + [Parameter()] + [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] + [Alias('Name')] + [System.String] + $ProjectName, + + [Parameter()] + [Alias('Description')] + [System.String] + $ProjectDescription, + + [Parameter()] + [ValidateSet('Git','Tfvc')] + [System.String] + $SourceControlType = 'Git', + + [Parameter()] + [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] + [System.String]$ProcessTemplate = 'Agile', + + [Parameter()] + [ValidateSet('Public', 'Private')] + [System.String]$Visibility = 'Private', + + [Parameter()] + [PSCustomObject]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Should not be triggered. This is a placeholder for the test function. + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Get-AzDoProjectGroup.ps1 similarity index 76% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Get-AzDoProjectGroup.ps1 index fbb9d8135..e91721064 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Get-AzDoProjectGroup.ps1 @@ -24,18 +24,17 @@ Retrieves the organization group named 'Developers' from the Azure DevOps instan #> -Function Get-xAzDoProjectGroup { - +Function Get-AzDoProjectGroup +{ [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( - - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Project')] [System.String]$ProjectName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$GroupName, @@ -48,7 +47,6 @@ Function Get-xAzDoProjectGroup { [Parameter()] [Ensure]$Ensure - ) # Logging @@ -86,29 +84,31 @@ Function Get-xAzDoProjectGroup { Write-Verbose "[Get-AzDoProjectGroup] Testing LocalCache, LiveCache and Parameters." - # # If the localgroup and lifegroup are present, compare the properties as well as the originId - if (($null -ne $livegroup.originId) -and ($null -ne $localgroup.originId)) { + if (($null -ne $livegroup.originId) -and ($null -ne $localgroup.originId)) + { Write-Verbose "[Get-AzDoProjectGroup] Testing LocalCache, LiveCache and Parameters." - # # Check if the originId is the same. If so, the group is unchanged. If not, the group has been renamed. - - if ($livegroup.originId -ne $localgroup.originId) { + if ($livegroup.originId -ne $localgroup.originId) + { # The group has been renamed or deleted and recreated. # Perform a lookup in the live cache to see if the group has been deleted and recreated. $renamedGroup = $livegroup | Find-CacheItem -Filter { $_.originId -eq $livegroup.originId } # If renamed group is not null, the group has been renamed. - if ($null -ne $renamedGroup) { + if ($null -ne $renamedGroup) + { # Add the renamed group to result $getGroupResult.renamedGroup = $renamedGroup # The group has been renamed. $getGroupResult.status = [DSCGetSummaryState]::Renamed - } else { + } + else + { # The group has been deleted and recreated. Treat the new group as the live group. # Remove the old group from the local cache @@ -117,38 +117,51 @@ Function Get-xAzDoProjectGroup { Add-CacheItem -Key $Key -Value $livegroup -Type 'Group' # Compare the properties of the live group with the parameters - if ($livegroup.description -ne $groupDescription) { $getGroupResult.propertiesChanged += 'description' } - if ($livegroup.name -ne $localgroup.name) { $getGroupResult.propertiesChanged += 'displayName' } + if ($livegroup.description -ne $groupDescription) + { + $getGroupResult.propertiesChanged += 'description' + } + + if ($livegroup.name -ne $localgroup.name) + { + $getGroupResult.propertiesChanged += 'displayName' + } # If the properties are the same, the group is unchanged. If not, the group has been changed. - if ($getGroupResult.propertiesChanged.count -ne 0) { + if ($getGroupResult.propertiesChanged.count -ne 0) + { # Update the Result $getGroupResult.status = [DSCGetSummaryState]::Changed # Add the reason - } else { + } + else + { # Update the Result $getGroupResult.status = [DSCGetSummaryState]::Unchanged } - } return $getGroupResult } - # # The Group hasn't been renamed. Test the properties to make sure they are the same as the parameters. # Compare the properties of the live group with the parameters - if ($livegroup.description -ne $groupDescription) { $getGroupResult.propertiesChanged += 'Description' } - if ($livegroup.name -ne $localgroup.name) { $getGroupResult.propertiesChanged += 'Name' } + if ($livegroup.description -ne $groupDescription) + { + $getGroupResult.propertiesChanged += 'Description' + } + + if ($livegroup.name -ne $localgroup.name) + { + $getGroupResult.propertiesChanged += 'Name' + } # If the properties are the same, the group is unchanged. If not, the group has been changed. $getGroupResult.status = ($getGroupResult.propertiesChanged.count -ne 0) ? [DSCGetSummaryState]::Changed : [DSCGetSummaryState]::Unchanged - if ($getGroupResult.status -eq [DSCGetSummaryState]::Changed) { - # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Changed', 'The group has changed') - } else { + if ($getGroupResult.status -ne [DSCGetSummaryState]::Changed) + { $getGroupResult.Ensure = [Ensure]::Present } @@ -157,21 +170,22 @@ Function Get-xAzDoProjectGroup { } - # # If the livegroup is not present and the localgroup is present, the group is missing and recreate it. - if (($null -eq $livegroup) -and ($null -ne $localgroup)) { + if (($null -eq $livegroup) -and ($null -ne $localgroup)) + { $getGroupResult.status = [DSCGetSummaryState]::NotFound $getGroupResult.propertiesChanged = @('description', 'displayName') - # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Removed', 'The group is missing') + return $getGroupResult } - # - # If the localgroup is not present and the livegroup is present, the group is not found. Check the properties are the same as the parameters. - # If the properties are the same, the group is unchanged. If not, the group has been deleted and then recreated and the new group will become authoritative. - # - if (($null -eq $localgroup) -and ($null -ne $livegroup)) { + <# + If the localgroup is not present and the livegroup is present, the group is not found. Check the properties are the same as the parameters. + If the properties are the same, the group is unchanged. If not, the group has been deleted and then recreated and the new group will become authoritative. + #> + + if (($null -eq $localgroup) -and ($null -ne $livegroup)) + { # Validate that the live properties are the same as the parameters if ($livegroup.description -ne $GroupDescription ) { $getGroupResult.propertiesChanged += 'description' } @@ -179,9 +193,8 @@ Function Get-xAzDoProjectGroup { # If the properties are the same, the group is unchanged. If not, the group has been changed. $getGroupResult.status = ($getGroupResult.propertiesChanged.count -ne 0) ? [DSCGetSummaryState]::Changed : [DSCGetSummaryState]::Unchanged - if ($getGroupResult.status -ne [DSCGetSummaryState]::Unchanged) { - # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:Missing', 'The group is missing') + if ($getGroupResult.status -ne [DSCGetSummaryState]::Unchanged) + { # Set the Ensure to Present $getGroupResult.Ensure = [Ensure]::Present } else { @@ -194,13 +207,12 @@ Function Get-xAzDoProjectGroup { } - # # If the livegroup and localgroup are not present, the group is missing and recreate it. - if (($null -eq $livegroup) -and ($null -eq $localgroup)) { + if (($null -eq $livegroup) -and ($null -eq $localgroup)) + { $getGroupResult.status = [DSCGetSummaryState]::NotFound $getGroupResult.propertiesChanged = @('description', 'displayName') - # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('xAzDoOrganizationGroup:xAzDoOrganizationGroup:NotFound', 'The group is not found') + return $getGroupResult } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/New-AzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/New-AzDoProjectGroup.ps1 new file mode 100644 index 000000000..96abb971d --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/New-AzDoProjectGroup.ps1 @@ -0,0 +1,108 @@ +<# +.SYNOPSIS +Creates a new Azure DevOps project group. + +.DESCRIPTION +The New-AzDoProjectGroup function creates a new group within a specified Azure DevOps project. +It requires the project name and group name as mandatory parameters. Optionally, a description +for the group and a lookup result can be provided. The function also supports a force switch +parameter to override existing settings. + +.PARAMETER GroupName +The name of the new group to be created. This parameter is mandatory. + +.PARAMETER GroupDescription +An optional description for the new group. + +.PARAMETER ProjectName +The name of the Azure DevOps project where the group will be created. This parameter is mandatory. + +.PARAMETER LookupResult +An optional hashtable containing lookup results. + +.PARAMETER Ensure +An optional parameter to specify the desired state of the group. + +.PARAMETER Force +A switch parameter to force the creation of the group, overriding any existing settings. + +.EXAMPLE +PS> New-AzDoProjectGroup -GroupName "Developers" -ProjectName "MyProject" + +Creates a new group named "Developers" in the "MyProject" Azure DevOps project. + +.EXAMPLE +PS> New-AzDoProjectGroup -GroupName "Testers" -GroupDescription "QA Team" -ProjectName "MyProject" -Force + +Creates a new group named "Testers" with the description "QA Team" in the "MyProject" Azure DevOps project, +forcing the creation even if the group already exists. + +.NOTES +This function relies on the global variable $Global:DSCAZDO_OrganizationName to construct the API URI. +It also interacts with cache functions like Get-CacheItem, Refresh-CacheIdentity, Add-CacheItem, and Set-CacheObject. +#> +Function New-AzDoProjectGroup +{ + + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$GroupName, + + [Parameter()] + [Alias('Description')] + [System.String]$GroupDescription, + + [Parameter(Mandatory = $true)] + [Alias('Project')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Lookup')] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Define parameters for creating a new DevOps group + $params = @{ + GroupName = $GroupName + GroupDescription = $GroupDescription + ApiUri = 'https://vssps.dev.azure.com/{0}' -f $Global:DSCAZDO_OrganizationName + ProjectScopeDescriptor = (Get-CacheItem -Key $ProjectName -Type 'LiveProjects').ProjectDescriptor + } + + # If the project scope descriptor is not found, write a warning message to the console and return. + if ($null -eq $params.ProjectScopeDescriptor) + { + Write-Warning "[New-AzDoProjectGroup] Unable to find project scope descriptor for project '$ProjectName'. Aborting group creation." + return + } + + # Write verbose log before creating a new group + Write-Verbose "[New-AzDoProjectGroup] Creating a new DevOps group with the following parameters: $($params | Out-String)" + + # Create a new group + $group = New-DevOpsGroup @params + + # Write verbose log after group creation + Write-Verbose "[New-AzDoProjectGroup] New DevOps group created: $($group | Out-String)" + + # Update the cache with the new group + Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' + + Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' + Write-Verbose "[New-AzDoProjectGroup] Added new group to Group cache with key: $($group.principalName)" + + Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' + Write-Verbose "[New-AzDoProjectGroup] Updated global AzDoGroup cache object." + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Remove-AzDoProjectGroup.ps1 similarity index 54% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Remove-AzDoProjectGroup.ps1 index 944564ef4..f098526d6 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Remove-AzDoProjectGroup.ps1 @@ -1,11 +1,42 @@ -Function Remove-xAzDoProjectGroup { +<# +.SYNOPSIS +Removes an Azure DevOps project group. +.DESCRIPTION +The Remove-AzDoProjectGroup function removes a specified Azure DevOps project group by its name and project. +It also updates the cache to reflect the removal. + +.PARAMETER GroupName +The name of the group to be removed. This parameter is mandatory. + +.PARAMETER GroupDescription +The description of the group to be removed. This parameter is optional. + +.PARAMETER ProjectName +The name of the project that the group belongs to. This parameter is mandatory. + +.PARAMETER LookupResult +A hashtable containing the lookup results for the group. This parameter is optional. + +.PARAMETER Ensure +Specifies whether the group should be present or absent. This parameter is optional. + +.PARAMETER Force +A switch parameter to force the removal of the group without confirmation. This parameter is optional. + +.EXAMPLE +Remove-AzDoProjectGroup -GroupName "Developers" -ProjectName "MyProject" + +This command removes the "Developers" group from the "MyProject" project. + +#> +Function Remove-AzDoProjectGroup +{ [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( - - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$GroupName, @@ -13,7 +44,7 @@ Function Remove-xAzDoProjectGroup { [Alias('Description')] [System.String]$GroupDescription, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Project')] [System.String]$ProjectName, @@ -30,13 +61,14 @@ Function Remove-xAzDoProjectGroup { ) # If no cache items exist, return. - if (($null -eq $LookupResult.liveCache) -and ($null -eq $LookupResult.localCache)) { + if (($null -eq $LookupResult.liveCache) -and ($null -eq $LookupResult.localCache)) + { return } $params = @{ GroupDescriptor = $LookupResult.liveCache.Descriptor - ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName + ApiUri = 'https://vssps.dev.azure.com/{0}' -f $Global:DSCAZDO_OrganizationName } $cacheItem = @{ @@ -44,7 +76,8 @@ Function Remove-xAzDoProjectGroup { } # If the group is not found, return - if (($null -ne $LookupResult.localCache) -and ($null -eq $LookupResult.liveCache)) { + if (($null -ne $LookupResult.localCache) -and ($null -eq $LookupResult.liveCache)) + { $cacheItem.Key = $LookupResult.localCache.principalName $params.GroupDescriptor = $LookupResult.localCache.Descriptor } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Set-AzDoProjectGroup.ps1 similarity index 50% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Set-AzDoProjectGroup.ps1 index 7d19525ef..5760ec00b 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Set-AzDoProjectGroup.ps1 @@ -1,8 +1,42 @@ -Function Set-xAzDoProjectGroup { +<# +.SYNOPSIS +Sets or updates an Azure DevOps project group. - param( +.DESCRIPTION +The Set-AzDoProjectGroup function sets or updates an Azure DevOps project group based on the provided parameters. +It handles renaming, updating group details, and managing cache for the group. + +.PARAMETER GroupName +The name of the Azure DevOps project group. This parameter is mandatory. + +.PARAMETER GroupDescription +The description of the Azure DevOps project group. This parameter is optional. + +.PARAMETER ProjectName +The name of the Azure DevOps project. This parameter is mandatory. + +.PARAMETER LookupResult +A hashtable containing the lookup result for the group. This parameter is optional. + +.PARAMETER Ensure +Specifies whether the group should be present or absent. This parameter is optional. + +.PARAMETER Force +A switch parameter to force the operation. This parameter is optional. + +.EXAMPLE +Set-AzDoProjectGroup -GroupName "Developers" -ProjectName "MyProject" -GroupDescription "Development Team" - [Parameter(Mandatory)] +This example sets or updates the "Developers" group in the "MyProject" Azure DevOps project with the description "Development Team". + +.NOTES +If the group has been renamed, a warning is issued and the function returns without making changes. +The function updates both the live and local cache with the new group details. +#> +Function Set-AzDoProjectGroup +{ + param( + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$GroupName, @@ -10,7 +44,7 @@ Function Set-xAzDoProjectGroup { [Alias('Description')] [System.String]$GroupDescription, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Project')] [System.String]$ProjectName, @@ -24,32 +58,33 @@ Function Set-xAzDoProjectGroup { [Parameter()] [System.Management.Automation.SwitchParameter] $Force - ) # # Depending on the type of lookup status, the group has been renamed the group has been deleted and recreated. - if ($LookupResult.Status -eq [DSCGetSummaryState]::Renamed) { - + if ($LookupResult.Status -eq [DSCGetSummaryState]::Renamed) + { # For the time being write a warning and return Write-Warning "[Set-AzDoProjectGroup] The group has been renamed. The group will not be set." return - } # # Update the group $params = @{ - ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName + ApiUri = 'https://vssps.dev.azure.com/{0}' -f $Global:DSCAZDO_OrganizationName GroupName = $GroupName GroupDescription = $GroupDescription GroupDescriptor = $LookupResult.liveCache.descriptor } - try { + try + { # Set the group from the API $group = Set-DevOpsGroup @params - } catch { + } + catch + { throw $_ } @@ -61,9 +96,11 @@ Function Set-xAzDoProjectGroup { # # Secondarily Replace the local cache with the new group - if ($null -ne $LookupResult.localCache) { + if ($null -ne $LookupResult.localCache) + { Remove-CacheItem -Key $LookupResult.localCache.principalName -Type 'Group' } + Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Test-AzDoProjectGroup.ps1 similarity index 79% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Test-AzDoProjectGroup.ps1 index b3891ae0d..cb305450c 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Test-AzDoProjectGroup.ps1 @@ -22,7 +22,7 @@ Returns $true if the organization group exists, otherwise returns $false. .EXAMPLE - Test-xAzDoOrganizationGroup -GroupName 'MyGroup' -Pat '********' -ApiUri 'https://dev.azure.com/myorg' + Test-AzDoOrganizationGroup -GroupName 'MyGroup' -Pat '********' -ApiUri 'https://dev.azure.com/myorg' Description ----------- @@ -30,10 +30,10 @@ using the specified personal access token and API URI. #> -Function Test-xAzDoProjectGroup { - +Function Test-AzDoProjectGroup +{ param( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $GroupName, @@ -49,13 +49,13 @@ Function Test-xAzDoProjectGroup { [Parameter()] [Alias('Name')] [hashtable]$GetResult - ) # # Firstly we need to compare to see if the group names are the same. If so we can return $false. - if ($GetResult.Status -eq [DSCGetSummaryState]::Unchanged ) { + if ($GetResult.Status -eq [DSCGetSummaryState]::Unchanged ) + { $result = $true @@ -65,46 +65,53 @@ Function Test-xAzDoProjectGroup { $result = $false } - return $true } + return $true + } - # # If the status has been flagged as 'Renamed', returned $true. This means that the originId has changed. - if ($GetResult.Status -eq [DSCGetSummaryState]::Renamed) { return $false } + if ($GetResult.Status -eq [DSCGetSummaryState]::Renamed) + { + return $false + } - # # If the status has been flagged as 'Missing', returned $true. This means that the group is missing from the live cache. + if ($GetResult.Status -eq [DSCGetSummaryState]::Changed) + { - - if ($GetResult.Status -eq [DSCGetSummaryState]::Changed) { - - # # If the group is present in the live cache and the local cache. This means that the originId has changed. This needs to be updated. - if (($null -ne $GetResult.Current) -and ($null -ne $GetResult.Cache)) { + if (($null -ne $GetResult.Current) -and ($null -ne $GetResult.Cache)) + { return $true } - # # If the group is present in the live cache but not in the local cache. Flag as Changed. - if ($GetResult.Current -and -not($GetResult.Cache)) { + if ($GetResult.Current -and -not($GetResult.Cache)) + { return $true } # # If the group is not present in the live cache but is in the local cache. Flag as Changed. - if (-not($GetResult.Current) -and $GetResult.Cache) { + if (-not($GetResult.Current) -and $GetResult.Cache) + { return $true } } - # Format the Key According to the Principal Name $Key = Format-AzDoGroup -Prefix "[$Global:DSCAZDO_OrganizationName]" -GroupName $GroupName - # # Check the cache for the group $group = Get-CacheItem -Key $Key -Type 'LiveGroups' - if (-not($group)) { $false } else { $true } + if (-not($group)) + { + $false + } + else + { + $true + } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Get-xAzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Get-AzDoProjectServices.ps1 similarity index 62% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Get-xAzDoProjectServices.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Get-AzDoProjectServices.ps1 index 6931c07e0..d6f9779c3 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Get-xAzDoProjectServices.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Get-AzDoProjectServices.ps1 @@ -1,10 +1,55 @@ -Function Get-xAzDoProjectServices { +<# +.SYNOPSIS + Retrieves the status of various Azure DevOps project services. +.DESCRIPTION + The Get-AzDoProjectServices function retrieves the status of various services (Git Repositories, Work Boards, Build Pipelines, Test Plans, and Azure Artifacts) for a specified Azure DevOps project. It compares the current state of these services with the desired state and returns a summary of the differences. + +.PARAMETER ProjectName + The name of the Azure DevOps project. + +.PARAMETER GitRepositories + The desired state of Git Repositories service. Valid values are 'Enabled' or 'Disabled'. Default is 'Enabled'. + +.PARAMETER WorkBoards + The desired state of Work Boards service. Valid values are 'Enabled' or 'Disabled'. Default is 'Enabled'. + +.PARAMETER BuildPipelines + The desired state of Build Pipelines service. Valid values are 'Enabled' or 'Disabled'. Default is 'Enabled'. + +.PARAMETER TestPlans + The desired state of Test Plans service. Valid values are 'Enabled' or 'Disabled'. Default is 'Enabled'. + +.PARAMETER AzureArtifact + The desired state of Azure Artifacts service. Valid values are 'Enabled' or 'Disabled'. Default is 'Enabled'. + +.PARAMETER LookupResult + A hashtable to store lookup results. + +.PARAMETER Ensure + Specifies whether the project services should be present or absent. + +.PARAMETER Force + Forces the command to run without asking for user confirmation. + +.OUTPUTS + [System.Management.Automation.PSObject[]] + Returns a hashtable containing the status of the project services and any properties that have changed. + +.EXAMPLE + PS C:\> Get-AzDoProjectServices -ProjectName "MyProject" -GitRepositories "Enabled" -WorkBoards "Enabled" -BuildPipelines "Enabled" -TestPlans "Enabled" -AzureArtifact "Enabled" + Retrieves the status of the specified project services for the project "MyProject" and compares them with the desired state. + +.NOTES + This function relies on the presence of a live cache and specific global variables and localized data parameters. +#> +Function Get-AzDoProjectServices +{ [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$ProjectName, @@ -54,7 +99,6 @@ Function Get-xAzDoProjectServices { status = [DSCGetSummaryState]::Unchanged } - # # Attempt to retrive the Project from the Live Cache. Write-Verbose "[Get-xAzDevOpsProjectServices] Retriving the Project from the Live Cache." @@ -62,7 +106,8 @@ Function Get-xAzDoProjectServices { $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' # If the Project does not exist in the Live Cache, return the Project object. - if ($null -eq $Project) { + if ($null -eq $Project) + { Write-Warning "[Get-xAzDevOpsProjectServices] The Project '$ProjectName' was not found in the Live Cache." $Result.Status = [DSCGetSummaryState]::NotFound return $Result @@ -83,35 +128,44 @@ Function Get-xAzDoProjectServices { } # Compare the Project Services with the desired state. - if ($GitRepositories -ne $Result.LiveServices.Repos.state) { + if ($GitRepositories -ne $Result.LiveServices.Repos.state) + { $Result.Status = [DSCGetSummaryState]::Changed $Result.propertiesChanged += @{ Expected = $GitRepositories FeatureId = $LocalizedDataAzURLParams.ProjectService_Repos } } - if ($WorkBoards -ne $Result.LiveServices.Boards.state) { + + if ($WorkBoards -ne $Result.LiveServices.Boards.state) + { $Result.Status = [DSCGetSummaryState]::Changed $Result.propertiesChanged += @{ Expected = $WorkBoards FeatureId = $LocalizedDataAzURLParams.ProjectService_Boards } } - if ($BuildPipelines -ne $Result.LiveServices.Pipelines.state) { + + if ($BuildPipelines -ne $Result.LiveServices.Pipelines.state) + { $Result.Status = [DSCGetSummaryState]::Changed $Result.propertiesChanged += @{ Expected = $BuildPipelines FeatureId = $LocalizedDataAzURLParams.ProjectService_Pipelines } } - if ($TestPlans -ne $Result.LiveServices.Tests.state) { + + if ($TestPlans -ne $Result.LiveServices.Tests.state) + { $Result.Status = [DSCGetSummaryState]::Changed $Result.propertiesChanged += @{ Expected = $TestPlans FeatureId = $LocalizedDataAzURLParams.ProjectService_TestPlans } } - if ($AzureArtifact -ne $Result.LiveServices.Artifacts.state) { + + if ($AzureArtifact -ne $Result.LiveServices.Artifacts.state) + { $Result.Status = [DSCGetSummaryState]::Changed $Result.propertiesChanged += @{ Expected = $AzureArtifact diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/New-AzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/New-AzDoProjectServices.ps1 new file mode 100644 index 000000000..454c7fd6c --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/New-AzDoProjectServices.ps1 @@ -0,0 +1,91 @@ +<# +.SYNOPSIS +Creates a new Azure DevOps project with specified services. + +.DESCRIPTION +The New-AzDoProjectServices function creates a new Azure DevOps project and configures various services such as Git repositories, work boards, build pipelines, test plans, and Azure artifacts based on the provided parameters. + +.PARAMETER ProjectName +Specifies the name of the Azure DevOps project to be created. This parameter is mandatory. + +.PARAMETER GitRepositories +Specifies whether Git repositories should be enabled or disabled for the project. Default is 'Enabled'. + +.PARAMETER WorkBoards +Specifies whether work boards should be enabled or disabled for the project. Default is 'Enabled'. + +.PARAMETER BuildPipelines +Specifies whether build pipelines should be enabled or disabled for the project. Default is 'Enabled'. + +.PARAMETER TestPlans +Specifies whether test plans should be enabled or disabled for the project. Default is 'Enabled'. + +.PARAMETER AzureArtifact +Specifies whether Azure artifacts should be enabled or disabled for the project. Default is 'Enabled'. + +.PARAMETER LookupResult +A hashtable that can be used to store lookup results. + +.PARAMETER Ensure +Specifies whether the project should be present or absent. + +.PARAMETER Force +If specified, forces the creation of the project even if it already exists. + +.OUTPUTS +System.Management.Automation.PSObject[] + +.EXAMPLE +PS C:\> New-AzDoProjectServices -ProjectName "MyProject" -GitRepositories "Enabled" -WorkBoards "Enabled" -BuildPipelines "Enabled" -TestPlans "Enabled" -AzureArtifact "Enabled" + +Creates a new Azure DevOps project named "MyProject" with all services enabled. +#> +Function New-AzDoProjectServices +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Repos')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$GitRepositories = 'Enabled', + + [Parameter()] + [Alias('Board')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$WorkBoards = 'Enabled', + + [Parameter()] + [Alias('Pipelines')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$BuildPipelines = 'Enabled', + + [Parameter()] + [Alias('Tests')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$TestPlans = 'Enabled', + + [Parameter()] + [Alias('Artifacts')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$AzureArtifact = 'Enabled', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Won't be triggered. + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Remove-AzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Remove-AzDoProjectServices.ps1 new file mode 100644 index 000000000..11a9bf1da --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Remove-AzDoProjectServices.ps1 @@ -0,0 +1,96 @@ +<# +.SYNOPSIS +Removes specified Azure DevOps project services. + +.DESCRIPTION +The Remove-AzDoProjectServices function removes specified services from an Azure DevOps project. +You can specify which services to remove, such as Git repositories, work boards, build pipelines, +test plans, and Azure artifacts. + +.PARAMETER ProjectName +Specifies the name of the Azure DevOps project from which services will be removed. This parameter is mandatory. + +.PARAMETER GitRepositories +Specifies whether Git repositories should be enabled or disabled. The default value is 'Enabled'. +Valid values are 'Enabled' and 'Disabled'. + +.PARAMETER WorkBoards +Specifies whether work boards should be enabled or disabled. The default value is 'Enabled'. +Valid values are 'Enabled' and 'Disabled'. + +.PARAMETER BuildPipelines +Specifies whether build pipelines should be enabled or disabled. The default value is 'Enabled'. +Valid values are 'Enabled' and 'Disabled'. + +.PARAMETER TestPlans +Specifies whether test plans should be enabled or disabled. The default value is 'Enabled'. +Valid values are 'Enabled' and 'Disabled'. + +.PARAMETER AzureArtifact +Specifies whether Azure artifacts should be enabled or disabled. The default value is 'Enabled'. +Valid values are 'Enabled' and 'Disabled'. + +.PARAMETER LookupResult +A hashtable containing lookup results for the project services. + +.PARAMETER Ensure +Specifies whether to ensure the state of the project services. + +.PARAMETER Force +If specified, forces the removal of the project services without prompting for confirmation. + +.EXAMPLE +Remove-AzDoProjectServices -ProjectName "MyProject" -GitRepositories Disabled -Force + +This command removes the Git repositories from the Azure DevOps project named "MyProject" without prompting for confirmation. + +#> +Function Remove-AzDoProjectServices +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Repos')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$GitRepositories = 'Enabled', + + [Parameter()] + [Alias('Board')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$WorkBoards = 'Enabled', + + [Parameter()] + [Alias('Pipelines')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$BuildPipelines = 'Enabled', + + [Parameter()] + [Alias('Tests')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$TestPlans = 'Enabled', + + [Parameter()] + [Alias('Artifacts')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$AzureArtifact = 'Enabled', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Won't be triggered. + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Set-xAzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Set-AzDoProjectServices.ps1 similarity index 51% rename from source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Set-xAzDoProjectServices.ps1 rename to source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Set-AzDoProjectServices.ps1 index e6058ca7b..be2548ea8 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Set-xAzDoProjectServices.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Set-AzDoProjectServices.ps1 @@ -1,10 +1,56 @@ -Function Set-xAzDoProjectServices { +<# +.SYNOPSIS + Configures the services for an Azure DevOps project. +.DESCRIPTION + The Set-AzDoProjectServices function enables or disables various services for a specified Azure DevOps project. + It retrieves the project details from the live cache and updates the service status based on the provided parameters. + +.PARAMETER ProjectName + The name of the Azure DevOps project. This parameter is mandatory. + +.PARAMETER GitRepositories + Specifies whether Git repositories should be enabled or disabled. Default is 'Enabled'. + Acceptable values are 'Enabled' and 'Disabled'. + +.PARAMETER WorkBoards + Specifies whether work boards should be enabled or disabled. Default is 'Enabled'. + Acceptable values are 'Enabled' and 'Disabled'. + +.PARAMETER BuildPipelines + Specifies whether build pipelines should be enabled or disabled. Default is 'Enabled'. + Acceptable values are 'Enabled' and 'Disabled'. + +.PARAMETER TestPlans + Specifies whether test plans should be enabled or disabled. Default is 'Enabled'. + Acceptable values are 'Enabled' and 'Disabled'. + +.PARAMETER AzureArtifact + Specifies whether Azure artifacts should be enabled or disabled. Default is 'Enabled'. + Acceptable values are 'Enabled' and 'Disabled'. + +.PARAMETER LookupResult + A hashtable containing the lookup results for the project services. + +.PARAMETER Ensure + Specifies whether to ensure the services are in the desired state. + +.PARAMETER Force + A switch parameter to force the operation. + +.EXAMPLE + Set-AzDoProjectServices -ProjectName "MyProject" -GitRepositories "Enabled" -WorkBoards "Disabled" + +.NOTES + This function requires the Get-CacheItem and Set-ProjectServiceStatus functions to be defined. +#> +Function Set-AzDoProjectServices +{ [CmdletBinding()] [OutputType([System.Management.Automation.PSObject[]])] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('Name')] [System.String]$ProjectName, @@ -48,7 +94,8 @@ Function Set-xAzDoProjectServices { $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' # Construct a hashtable detailing the group - ForEach ($PropertyChanged in $LookupResult.propertiesChanged) { + ForEach ($PropertyChanged in $LookupResult.propertiesChanged) + { $params = @{ Organization = $Global:DSCAZDO_OrganizationName diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Test-AzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Test-AzDoProjectServices.ps1 new file mode 100644 index 000000000..630396b04 --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Test-AzDoProjectServices.ps1 @@ -0,0 +1,96 @@ +<# +.SYNOPSIS + Tests the Azure DevOps project services configuration. + +.DESCRIPTION + The Test-AzDoProjectServices function checks the configuration of various services within an Azure DevOps project, such as Git repositories, work boards, build pipelines, test plans, and Azure artifacts. + +.PARAMETER ProjectName + The name of the Azure DevOps project to test. + +.PARAMETER GitRepositories + Specifies whether Git repositories are enabled or disabled. Default is 'Enabled'. + Valid values are 'Enabled' and 'Disabled'. + +.PARAMETER WorkBoards + Specifies whether work boards are enabled or disabled. Default is 'Enabled'. + Valid values are 'Enabled' and 'Disabled'. + +.PARAMETER BuildPipelines + Specifies whether build pipelines are enabled or disabled. Default is 'Enabled'. + Valid values are 'Enabled' and 'Disabled'. + +.PARAMETER TestPlans + Specifies whether test plans are enabled or disabled. Default is 'Enabled'. + Valid values are 'Enabled' and 'Disabled'. + +.PARAMETER AzureArtifact + Specifies whether Azure artifacts are enabled or disabled. Default is 'Enabled'. + Valid values are 'Enabled' and 'Disabled'. + +.PARAMETER LookupResult + A hashtable containing lookup results for the project services. + +.PARAMETER Ensure + Specifies whether to ensure the configuration is present or absent. + +.PARAMETER Force + Forces the command to run without asking for user confirmation. + +.OUTPUTS + [System.Management.Automation.PSObject[]] + Returns an array of PSObject representing the status of the project services. + +.EXAMPLE + Test-AzDoProjectServices -ProjectName "MyProject" -GitRepositories "Enabled" -WorkBoards "Enabled" -BuildPipelines "Enabled" -TestPlans "Enabled" -AzureArtifact "Enabled" + This command tests the configuration of the specified Azure DevOps project services for the project named "MyProject". +#> +Function Test-AzDoProjectServices +{ + [CmdletBinding()] + [OutputType([System.Management.Automation.PSObject[]])] + param + ( + [Parameter(Mandatory = $true)] + [Alias('Name')] + [System.String]$ProjectName, + + [Parameter()] + [Alias('Repos')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$GitRepositories = 'Enabled', + + [Parameter()] + [Alias('Board')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$WorkBoards = 'Enabled', + + [Parameter()] + [Alias('Pipelines')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$BuildPipelines = 'Enabled', + + [Parameter()] + [Alias('Tests')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$TestPlans = 'Enabled', + + [Parameter()] + [Alias('Artifacts')] + [ValidateSet('Enabled', 'Disabled')] + [System.String]$AzureArtifact = 'Enabled', + + [Parameter()] + [HashTable]$LookupResult, + + [Parameter()] + [Ensure]$Ensure, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + # Won't be triggered. + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsOperation.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsOperation.ps1 index 70bf16a4d..7bf17d04a 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsOperation.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/Get-AzDevOpsOperation.ps1 @@ -51,7 +51,9 @@ function Get-AzDevOpsOperation Pat = $Pat; ResourceName = 'Operation' } - If(![System.String]::IsNullOrWhiteSpace($OperationId)){ + + if (-not[System.String]::IsNullOrWhiteSpace($OperationId)) + { $azDevOpsApiResourceParameters.ResourceId = $OperationId } @@ -59,9 +61,11 @@ function Get-AzDevOpsOperation [System.Management.Automation.PSObject[]]$apiResources = Get-AzDevOpsApiResource @azDevOpsApiResourceParameters # Filter "Operation" resources - If(![System.String]::IsNullOrWhiteSpace($OperationId)){ + if (-not[System.String]::IsNullOrWhiteSpace($OperationId)) + { $apiResources = $apiResources | Where-Object { $_.id -eq $OperationId } } return [System.Management.Automation.PSObject[]]$apiResources + } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 index a00da8375..95b1c3198 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 @@ -14,26 +14,26 @@ New-AzDoAuthenticationProvider -OrganizationName "Contoso" This example creates a new Azure Managed Identity for the organization named "Contoso". #> -Function New-AzDoAuthenticationProvider { - +Function New-AzDoAuthenticationProvider +{ [CmdletBinding(DefaultParameterSetName = 'PersonalAccessToken')] param ( # Organization Name - [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] - [Parameter(Mandatory, ParameterSetName = 'SecureStringPersonalAccessToken')] - [Parameter(Mandatory, ParameterSetName = 'ManagedIdentity')] + [Parameter(Mandatory = $true, ParameterSetName = 'PersonalAccessToken')] + [Parameter(Mandatory = $true, ParameterSetName = 'SecureStringPersonalAccessToken')] + [Parameter(Mandatory = $true, ParameterSetName = 'ManagedIdentity')] [Alias('OrgName')] [String] $OrganizationName, # Personal Access Token - [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] + [Parameter(Mandatory = $true, ParameterSetName = 'PersonalAccessToken')] [Alias('PAT')] [String] $PersonalAccessToken, # SecureString Personal Access Token - [Parameter(Mandatory, ParameterSetName = 'SecureStringPersonalAccessToken')] + [Parameter(Mandatory = $true, ParameterSetName = 'SecureStringPersonalAccessToken')] [Alias('SecureStringPAT')] [SecureString] $SecureStringPersonalAccessToken, @@ -60,7 +60,8 @@ Function New-AzDoAuthenticationProvider { ) # Test if $ENV:AZDODSC_CACHE_DIRECTORY is set. If not, throw an error. - if ($null -eq $ENV:AZDODSC_CACHE_DIRECTORY) { + if ($null -eq $ENV:AZDODSC_CACHE_DIRECTORY) + { Throw "[New-AzDoAuthenticationProvider] The Environment Variable 'AZDODSC_CACHE_DIRECTORY' is not set. Please set the Environment Variable 'AZDODSC_CACHE_DIRECTORY' to the Cache Directory." } @@ -70,52 +71,55 @@ Function New-AzDoAuthenticationProvider { # # If the parameterset is PersonalAccessToken - if ($PSCmdlet.ParameterSetName -eq 'PersonalAccessToken') { + if ($PSCmdlet.ParameterSetName -eq 'PersonalAccessToken') + { Write-Verbose "[New-AzDoAuthenticationProvider] Creating a new Personal Access Token with OrganizationName $OrganizationName." # if the NoVerify switch is not set, verify the Token. - if ($NoVerify) { + if ($NoVerify) + { $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -PersonalAccessToken $PersonalAccessToken - } else { + } + else + { $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -PersonalAccessToken $PersonalAccessToken -Verify } } - # # If the parameterset is ManagedIdentity - elseif ($PSCmdlet.ParameterSetName -eq 'ManagedIdentity') { + elseif ($PSCmdlet.ParameterSetName -eq 'ManagedIdentity') + { Write-Verbose "[New-AzDoAuthenticationProvider] Creating a new Azure Managed Identity with OrganizationName $OrganizationName." # If the Token is not Valid. Get a new Token. - if ($NoVerify) { + if ($NoVerify) + { $Global:DSCAZDO_AuthenticationToken = Get-AzManagedIdentityToken -OrganizationName $OrganizationName - } else { + } + else + { $Global:DSCAZDO_AuthenticationToken = Get-AzManagedIdentityToken -OrganizationName $OrganizationName -Verify } } - # # If the parameterset is SecureStringPersonalAccessToken - elseif ($PSCmdlet.ParameterSetName -eq 'SecureStringPersonalAccessToken') { + elseif ($PSCmdlet.ParameterSetName -eq 'SecureStringPersonalAccessToken') + { Write-Verbose "[New-AzDoAuthenticationProvider] Creating a new Personal Access Token with OrganizationName $OrganizationName." # If the Token is not Valid. Get a new Token. $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -SecureStringPersonalAccessToken $SecureStringPersonalAccessToken } - # # Export the Token information to the Cache Directory - - if ($isResource.IsPresent) { + if ($isResource.IsPresent) + { Write-Verbose "[New-AzDoAuthenticationProvider] isResource is set. The Token will not be exported." return } - # # Initialize the Cache - - # Initialize the Cache Objects Get-AzDoCacheObjects | ForEach-Object { Initialize-CacheObject -CacheType $_ } @@ -125,7 +129,6 @@ Function New-AzDoAuthenticationProvider { . $_.Name -OrganizationName $AzureDevopsOrganizationName } - # # Export the Token to the Cache Directory # Create an Object Containing the Organization Name. diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.ps1 deleted file mode 100644 index d43224ea9..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -Function New-xAzDoGitPermission { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [string]$ProjectName, - - [Parameter(Mandatory)] - [string]$RepositoryName, - - [Parameter(Mandatory)] - [bool]$isInherited, - - [Parameter()] - [HashTable[]]$Permissions, - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - Write-Verbose "[New-xAzDoGitPermission] Started." - - # - # Security Namespace ID - - $SecurityNamespace = Get-CacheItem -Key 'Git Repositories' -Type 'SecurityNamespaces' - $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' - - if (($null -eq $SecurityNamespace) -or ($null -eq $Project)) { - Write-Warning "[New-xAzDoGitPermission] Security Namespace or Project not found." - return - } - - # - # Serialize the ACLs - - $serializeACLParams = @{ - ReferenceACLs = $LookupResult.propertiesChanged - DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' - DescriptorMatchToken = ($LocalizedDataAzSerializationPatten.GitRepository -f $Project.id) - } - - $params = @{ - OrganizationName = $Global:DSCAZDO_OrganizationName - SecurityNamespaceID = $SecurityNamespace.namespaceId - SerializedACLs = ConvertTo-ACLHashtable @serializeACLParams - } - - # - # Set the Git Repository Permissions - - Set-xAzDoPermission @params - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.ps1 deleted file mode 100644 index 891b4dc4f..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.ps1 +++ /dev/null @@ -1,68 +0,0 @@ - - -Function Get-xAzDoGitRepository { - - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$ProjectName, - - [Parameter(Mandatory)] - [Alias('Repository')] - [System.String]$RepositoryName, - - [Parameter()] - [Alias('Source')] - [System.String]$SourceRepository, - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - # - # Construct a hashtable detailing the group - - $getRepositoryResult = @{ - #Reasons = $() - Ensure = [Ensure]::Absent - liveCache = $livegroup - propertiesChanged = @() - status = $null - } - - - # - # Attempt to retrive the Project Group from the Live and Local Cache. - Write-Verbose "[Get-xAzDoGitRepository] Retriving the Project Group from the Live and Local Cache." - - # Format the Key for the Project Group. - $projectGroupKey = "$ProjectName\$RepositoryName" - - # Retrive the Repositories from the Live Cache. - $repository = Get-CacheItem -Key $projectGroupKey -Type 'LiveRepositories' - - # If the Repository exists in the Live Cache, return the Repository object. - if ($repository) { - Write-Verbose "[Get-xAzDoGitRepository] The Repository '$RepositoryName' was found in the Live Cache." - $getRepositoryResult.status = [DSCGetSummaryState]::Unchanged - return $getRepositoryResult - - } else { - Write-Verbose "[Get-xAzDoGitRepository] The Repository '$RepositoryName' was not found in the Live Cache." - $getRepositoryResult.status = [DSCGetSummaryState]::NotFound - } - - # Return the Repository object. - return $getRepositoryResult - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.ps1 deleted file mode 100644 index 8c3c2829c..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.ps1 +++ /dev/null @@ -1,57 +0,0 @@ - - -Function New-xAzDoGitRepository { - - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$ProjectName, - - [Parameter(Mandatory)] - [Alias('Repository')] - [System.String]$RepositoryName, - - [Parameter()] - [Alias('Source')] - [System.String]$SourceRepository, - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - Write-Verbose "[New-xAzDoGitRepository] Creating new repository '$($RepositoryName)' in project '$($ProjectName)'" - - # Define parameters for creating a new DevOps group - $params = @{ - ApiUri = "https://dev.azure.com/{0}/" -f $Global:DSCAZDO_OrganizationName - Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' - RepositoryName = $RepositoryName - SourceRepository = $SourceRepository - } - - if ($null -eq $params.Project) { - Write-Error "[New-xAzDoGitRepository] Project '$($ProjectName)' does not exist in the LiveProjects cache. Skipping change." - return - } - - - # Create a new repository - $value = New-GitRepository @params - - # Add the repository to the LiveRepositories cache and write to verbose log - Add-CacheItem -Key "$ProjectName\$RepositoryName" -Value $value -Type 'LiveRepositories' - Export-CacheObject -CacheType 'LiveRepositories' -Content $AzDoLiveRepositories - Refresh-CacheObject -CacheType 'LiveRepositories' - Write-Verbose "[New-xAzDoGitRepository] Added new group to LiveGroups cache with key: '$($value.Name)'" - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.ps1 deleted file mode 100644 index c8783d123..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.ps1 +++ /dev/null @@ -1,54 +0,0 @@ -Function Remove-xAzDoGitRepository -{ - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$ProjectName, - - [Parameter(Mandatory)] - [Alias('Repository')] - [System.String]$RepositoryName, - - [Parameter()] - [Alias('Source')] - [System.String]$SourceRepository, - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - - Write-Verbose "[Remove-xAzDoGitRepository] Removing repository '$($RepositoryName)' in project '$($ProjectName)'" - - # Define parameters for creating a new DevOps group - $params = @{ - ApiUri = "https://dev.azure.com/{0}/" -f $Global:DSCAZDO_OrganizationName - Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' - Repository = Get-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' - } - - # Check if the project exists in the LiveProjects cache - if (($null -eq $params.Project) -or ($null -eq $params.Repository)) { - Write-Error "[Remove-xAzDoGitRepository] Project '$($ProjectName)' or Repository '$($RepositoryName)' does not exist in the LiveProjects or LiveRepositories cache. Skipping change." - return - } - - # Create a new repository - $value = Remove-GitRepository @params - - # Add the repository to the LiveRepositories cache and write to verbose log - Remove-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' - Export-CacheObject -CacheType 'LiveRepositories' -Content $AzDoLiveRepositories - Write-Verbose "[Remove-xAzDoGitRepository] Added new group to LiveGroups cache with key: '$($value.Name)'" - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.ps1 deleted file mode 100644 index a3b673c50..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -Function Set-xAzDoGitRepository { - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$ProjectName, - - [Parameter(Mandatory)] - [Alias('Repository')] - [System.String]$RepositoryName, - - [Parameter()] - [Alias('Source')] - [System.String]$SourceRepository, - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - # Skipped - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.ps1 deleted file mode 100644 index 54ba1492e..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.ps1 +++ /dev/null @@ -1,103 +0,0 @@ -Function New-xAzDoGroupMember { - - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$GroupName, - - [Parameter()] - [Alias('Members')] - [System.String[]]$GroupMembers=@(), - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - - ) - - # Write a verbose log message indicating that the function has started executing. - Write-Verbose "[New-xAzDoGroupMember] Starting group member addition process for group '$GroupName'." - - # Fetch the Group Identity - $GroupIdentity = Find-AzDoIdentity $GroupName - Write-Verbose "[New-xAzDoGroupMember] Fetched group identity for '$GroupName'." - - # Retrieve the group members from the cache - $CachedGroupMembers = Get-CacheObject -CacheType 'LiveGroupMembers' - Write-Verbose "[New-xAzDoGroupMember] Retrieved cached group members." - - # Check if the group members are already cached - if (($null -ne $CachedGroupMembers) -and ($CachedGroupMembers.ContainsKey($GroupIdentity.principalName))) { - Write-Error "[New-xAzDoGroupMember] Group members are already cached for group '$GroupName'." - return - } - - - $params = @{ - GroupIdentity = $GroupIdentity - ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName - } - - Write-Verbose "[New-xAzDoGroupMember] Starting group member addition process for group '$GroupName'." - Write-Verbose "[New-xAzDoGroupMember] Group members: $($GroupMembers -join ',')." - - # Define the members - $members = [System.Collections.Generic.List[object]]::new() - - # Fetch the group members and perform a lookup of the members - ForEach ($MemberIdentity in $GroupMembers) { - - # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. - Write-Verbose "[New-xAzDoGroupMember] Looking up identity for member '$MemberIdentity'." - $identity = Find-AzDoIdentity -Identity $MemberIdentity - - # If the identity is not found, write a warning message to the console and continue to the next member. - if ($null -eq $identity) { - Write-Warning "[New-xAzDoGroupMember] Unable to find identity for member '$MemberIdentity'." - continue - } - - # Check for circular reference - if ($GroupIdentity.originId -eq $identity.originId) { - Write-Warning "[New-xAzDoGroupMember] Circular reference detected for member '$MemberIdentity'." - continue - } - - Write-Verbose "[New-xAzDoGroupMember] Found identity for member '$MemberIdentity'." - - # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. - Write-Verbose "[New-xAzDoGroupMember] Adding member '$MemberIdentity' to group '$($params.GroupIdentity.displayName)'." - - $result = New-DevOpsGroupMember @params -MemberIdentity $identity - - # Add the member to the list - $members.Add($identity) - Write-Verbose "[New-xAzDoGroupMember] Member '$MemberIdentity' added to the internal list." - } - - # If the group members are not found, write a warning message to the console and return. - if ($members.Count -eq 0) { - Write-Warning "[New-xAzDoGroupMember] No group members found: $($GroupMembers -join ',')." - return - } - - # Add the group to the cache - Write-Verbose "[New-xAzDoGroupMember] Added group '$GroupName' with members to the cache." - Add-CacheItem -Key $GroupIdentity.principalName -Value $members -Type 'LiveGroupMembers' - - Write-Verbose "[New-xAzDoGroupMember] Updated global cache with live group information." - Set-CacheObject -Content $Global:AzDoLiveGroupMembers -CacheType 'LiveGroupMembers' - - # Write a verbose log message indicating that the function has completed the group member addition process. - Write-Verbose "[New-xAzDoGroupMember] Completed group member addition process for group '$GroupName'." - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.ps1 deleted file mode 100644 index 30c740cc8..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.ps1 +++ /dev/null @@ -1,91 +0,0 @@ -Function Remove-xAzDoGroupMember { - - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$GroupName, - - [Parameter()] - [Alias('Members')] - [System.String[]]$GroupMembers=@(), - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - - ) - - # Group Identity - $GroupIdentity = Find-AzDoIdentity $GroupName - - # Format the According to the Group Name - $Key = Format-AzDoProjectName -GroupName $GroupName -OrganizationName $Global:DSCAZDO_OrganizationName - - # Check the cache for the group - $LiveGroupMembers = @(Get-CacheItem -Key $Key -Type 'LiveGroupMembers') - - # If the group identity or key is not found, write a warning message to the console and return. - if ([String]::IsNullOrEmpty($GroupIdentity) -or [String]::IsNullOrWhiteSpace($GroupIdentity)) { - Write-Warning "[Remove-xAzDoGroupMember] Unable to find identity for group '$GroupName'." - return - } - - $params = @{ - GroupIdentity = $GroupIdentity - ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName - } - - Write-Verbose "[Remove-xAzDoGroupMember] Starting group member removal process for group '$GroupName'." - Write-Verbose "[Remove-xAzDoGroupMember] Group members: $($LiveGroupMembers.principalName -join ',')." - - # Define the members - #$members = [System.Collections.Generic.List[object]]::new() - - # Fetch the group members and perform a lookup of the members - ForEach ($MemberIdentity in $LiveGroupMembers) { - - # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. - Write-Verbose "[Remove-xAzDoGroupMember] Looking up identity for member '$($MemberIdentity.principalName)'." - $identity = Find-AzDoIdentity -Identity $MemberIdentity.principalName - - # If the identity is not found, write a warning message to the console and continue to the next member. - if ([String]::IsNullOrEmpty($identity) -or [String]::IsNullOrWhiteSpace($identity)) { - Write-Warning "[Remove-xAzDoGroupMember] Unable to find identity for member '$($MemberIdentity.principalName)'." - continue - } - - # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. - Write-Verbose "[Remove-xAzDoGroupMember] Removing member '$($MemberIdentity.principalName)' from group '$($params.GroupIdentity.displayName)'." - - $result = Remove-DevOpsGroupMember @params -MemberIdentity $identity - - } - - <# - # If the group members are not found, write a warning message to the console and return. - if ($members.Count -eq 0) { - Write-Warning "[Remove-xAzDoGroupMember] No group members found: $($GroupMembers -join ',')." - return - } - #> - - # Add the group to the cache - Write-Verbose "[Remove-xAzDoGroupMember] Removed group '$GroupName' with members to the cache." - Remove-CacheItem -Key $GroupIdentity.principalName -Type 'LiveGroupMembers' - - Write-Verbose "[Remove-xAzDoGroupMember] Updated global cache with live group information." - Set-CacheObject -Content $Global:AzDoLiveGroupMembers -CacheType 'LiveGroupMembers' - - # Write a verbose log message indicating that the function has completed the group member removal process. - Write-Verbose "[Remove-xAzDoGroupMember] Completed group member removal process for group '$GroupName'." - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.ps1 deleted file mode 100644 index 58e454e72..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.ps1 +++ /dev/null @@ -1,124 +0,0 @@ -Function Set-xAzDoGroupMember { - - param( - - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$GroupName, - - [Parameter()] - [Alias('Members')] - [System.String[]]$GroupMembers=@(), - - [Parameter()] - [Alias('Lookup')] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - - ) - - # Group Identity - $GroupIdentity = Find-AzDoIdentity $GroupName - - # Format the According to the Group Name - $Key = Format-AzDoProjectName -GroupName $GroupName -OrganizationName $Global:DSCAZDO_OrganizationName - # Check the cache for the group - $members = [System.Collections.ArrayList]::New() - Get-CacheItem -Key $Key -Type 'LiveGroupMembers' | ForEach-Object { $members.Add($_) } - - # If the members are null or empty, stop. - if (($null -eq $GroupMembers) -or ($members.Count -eq 0)) { - Write-Error "[Set-xAzDoGroupMember] No members found in the LiveGroupMembers cache for group '$Key'." - return - } - - # If the lookup result is not provided, we need to look it up. - if ($null -eq $LookupResult.propertiesChanged) { - Throw "[Set-xAzDoGroupMember] - LookupResult.propertiesChanged is required." - } - - # Fetch the Group Identity - $params = @{ - GroupIdentity = $GroupIdentity - ApiUri = 'https://vssps.dev.azure.com/{0}/' -f $Global:DSCAZDO_OrganizationName - } - - Write-Verbose "[Set-xAzDoGroupMember] Starting group member addition process for group '$GroupName'." - - # If the lookup result is not provided, we need to look it up. - switch ($LookupResult.propertiesChanged) { - - # Add members - { $_.action -eq "Add" } { - - # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. - Write-Verbose "[Set-xAzDoGroupMember][ADD] Adding Identity for Principal Name '$($_.value.principalName)'." - $identity = $_.value - - # Check for circular reference - if ($GroupIdentity.originId -eq $identity.originId) { - Write-Warning "[Set-xAzDoGroupMember][ADD] Circular reference detected for member '$($GroupIdentity.principalName)'." - continue - } - - # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. - Write-Verbose "[Set-xAzDoGroupMember][ADD] Adding member '$($identity.displayName)' to group '$($params.GroupIdentity.displayName)'." - - $result = New-DevOpsGroupMember @params -MemberIdentity $identity - - # Add the member to the list - $members.Add($identity) - Write-Verbose "[Set-xAzDoGroupMember][ADD] Member '$($identity.displayName)' added to the internal list." - - } - - # Remove - { $_.action -eq "Remove" } { - - # Use the Find-AzDoIdentity function to search for an Azure DevOps identity that matches the given $MemberIdentity. - Write-Verbose "[Set-xAzDoGroupMember][REMOVE] Removing Identity for Principal Name '$($_.value.principalName)'." - $identity = $_.value - - # Check for circular reference - if ($GroupIdentity.originId -eq $identity.originId) { - Write-Warning "[Set-xAzDoGroupMember][REMOVE] Circular reference detected for member '$($GroupIdentity.principalName)'." - continue - } - - # Call the New-DevOpsGroupMember function with a hashtable of parameters to add the found identity as a new member to a group. - Write-Verbose "[Set-xAzDoGroupMember][REMOVE] Removing member '$($identity.displayName)' to group '$($params.GroupIdentity.displayName)'." - - $result = Remove-DevOpsGroupMember @params -MemberIdentity $identity - - # Remove the member from the list - - Write-Verbose "[Set-xAzDoGroupMember][REMOVE] Removing member '$($identity.displayName)' from the internal list." - Write-Verbose "[Set-xAzDoGroupMember][REMOVE] members count: $($members.count)" - - $id = 0 .. $members.count | Where-Object { $members[$_].originId -eq $identity.originId } - $members.RemoveAt($id) - Write-Verbose "[Set-xAzDoGroupMember][REMOVE] Member '$($identity.displayName)' removed from the internal list." - - } - - # Default - Default { - Write-Warning "[Set-xAzDoGroupMember] Invalid action '$($_.action)' provided." - } - - } - - # Add the group to the cache - Write-Verbose "[Set-xAzDoGroupMember] Added group '$GroupName' with the updated member list to the cache." - Add-CacheItem -Key $GroupIdentity.principalName -Value $members -Type 'LiveGroupMembers' - - Write-Verbose "[Set-xAzDoGroupMember] Updated global cache with live group information." - Set-CacheObject -Content $Global:AzDoLiveGroupMembers -CacheType 'LiveGroupMembers' - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.ps1 deleted file mode 100644 index 2f471eea1..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.ps1 +++ /dev/null @@ -1,28 +0,0 @@ - -Function Test-xAzDoGroupMember{ - - param( - - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$GroupName, - - [Parameter()] - [Alias('Members')] - [System.String[]]$GroupMembers=@(), - - [Parameter()] - [Alias('Lookup')] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - - ) - - $return -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.ps1 deleted file mode 100644 index 63b077127..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.ps1 +++ /dev/null @@ -1,70 +0,0 @@ -Function New-xAzDoGroupPermission { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [string]$GroupName, - - [Parameter(Mandatory)] - [bool]$isInherited, - - [Parameter()] - [HashTable[]]$Permissions, - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - - Write-Verbose "[New-xAzDoProjectGroupPermission] Started." - - # - # Format the Group Name - - # Split the Group Name - $split = $GroupName.Split('\').Split('/') - - # Test if the Group Name is valid - if ($split.Count -ne 2) { - Write-Warning "[Get-xAzDoProjectGroupPermission] Invalid Group Name: $GroupName" - return - } - - # Define the Project and Group Name - $ProjectName = $split[0] - $GroupName = $split[1] - - # - # Security Namespace ID - - $SecurityNamespace = Get-CacheItem -Key 'Identity' -Type 'SecurityNamespaces' - $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' - $Group = Get-CacheItem -Key $('[{0}]\{1}' -f $ProjectName, $GroupName) -Type 'LiveGroups' - - # - # Serialize the ACLs - - $serializeACLParams = @{ - ReferenceACLs = $LookupResult.propertiesChanged - DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' - DescriptorMatchToken = ($LocalizedDataAzSerializationPatten.GroupPermission -f $Project.id, $Group.id) - } - - $params = @{ - OrganizationName = $Global:DSCAZDO_OrganizationName - SecurityNamespaceID = $SecurityNamespace.namespaceId - SerializedACLs = ConvertTo-ACLHashtable @serializeACLParams - } - - # - # Set the Git Repository Permissions - - Set-xAzDoPermission @params - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.ps1 deleted file mode 100644 index f328f89dc..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.ps1 +++ /dev/null @@ -1,73 +0,0 @@ -Function Remove-xAzDoGroupPermission { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [string]$GroupName, - - [Parameter(Mandatory)] - [bool]$isInherited, - - [Parameter()] - [HashTable[]]$Permissions, - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - - Write-Verbose "[New-xAzDoGitPermission] Started." - - # - # Format the Group Name - - # Split the Group Name - $split = $GroupName.Split('\').Split('/') - - # Test if the Group Name is valid - if ($split.Count -ne 2) { - Write-Warning "[Get-xAzDoProjectGroupPermission] Invalid Group Name: $GroupName" - return - } - - # Define the Project and Group Name - $ProjectName = $split[0] - $GroupName = $split[1] - - # - # Security Namespace ID - - $SecurityNamespace = Get-CacheItem -Key 'Identity' -Type 'SecurityNamespaces' - $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' - $Repository = Get-CacheItem -Key "$ProjectName\$RepositoryName" -Type 'LiveRepositories' - $DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' - - # - # Filter the ACLs that pertain to the Git Repository - - $searchString = "repoV2/{0}/{1}" -f $Project.id, $Repository.id - - # Test if the Token exists - $Filtered = $DescriptorACLList | Where-Object { $_.token -eq $searchString } - - # If the ACLs are not null, remove them - if ($Filtered) { - - $params = @{ - OrganizationName = $Global:DSCAZDO_OrganizationName - SecurityNamespaceID = $SecurityNamespace.namespaceId - TokenName = $searchString - } - - # Remove the ACLs - Remove-xAzDoPermission @params - - } - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.ps1 deleted file mode 100644 index 073729c89..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.ps1 +++ /dev/null @@ -1,70 +0,0 @@ -Function Set-xAzDoGroupPermission { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [string]$GroupName, - - [Parameter(Mandatory)] - [bool]$isInherited, - - [Parameter()] - [HashTable[]]$Permissions, - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - - Write-Verbose "[Set-xAzDoGroupPermission] Started." - - # - # Format the Group Name - - # Split the Group Name - $split = $GroupName.Split('\').Split('/') - - # Test if the Group Name is valid - if ($split.Count -ne 2) { - Write-Warning "[Get-xAzDoProjectGroupPermission] Invalid Group Name: $GroupName" - return - } - - # Define the Project and Group Name - $ProjectName = $split[0] - $GroupName = $split[1] - - - # - # Security Namespace ID - - $SecurityNamespace = Get-CacheItem -Key 'Identity' -Type 'SecurityNamespaces' - $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' - - # - # Serialize the ACLs - - $serializeACLParams = @{ - ReferenceACLs = $LookupResult.propertiesChanged - DescriptorACLList = Get-CacheItem -Key $SecurityNamespace.namespaceId -Type 'LiveACLList' - DescriptorMatchToken = ($LocalizedDataAzSerializationPatten.GitRepository -f $Project.id) - } - - $params = @{ - OrganizationName = $Global:DSCAZDO_OrganizationName - SecurityNamespaceID = $SecurityNamespace.namespaceId - SerializedACLs = ConvertTo-ACLHashtable @serializeACLParams - } - - # - # Set the Git Repository Permissions - - Set-xAzDoPermission @params - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.ps1 deleted file mode 100644 index 936aec29f..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.ps1 +++ /dev/null @@ -1,53 +0,0 @@ -Function New-xAzDoOrganizationGroup { - - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$GroupName, - - [Parameter()] - [Alias('Description')] - [System.String]$GroupDescription, - - [Parameter()] - [Alias('Lookup')] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - - ) - - # Define parameters for creating a new DevOps group - $params = @{ - GroupName = $GroupName - GroupDescription = $GroupDescription - ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName - } - - # Write verbose log with the parameters used for creating the group - Write-Verbose "[New-xAzDoOrganizationGroup] Creating a new DevOps group with GroupName: '$($params.GroupName)', GroupDescription: '$($params.GroupDescription)' and ApiUri: '$($params.ApiUri)'" - - # Create a new group - $group = New-DevOpsGroup @params - - # Update the cache with the new group - Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' - - # Add the group to the Group cache and write to verbose log - Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' - Write-Verbose "[New-xAzDoOrganizationGroup] Added new group to Group cache with key: '$($group.principalName)'" - - # Update the global AzDoGroup object and write to verbose log - Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' - Write-Verbose "[New-xAzDoOrganizationGroup] Updated global AzDoGroup cache object." - - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.ps1 deleted file mode 100644 index adc0ed7aa..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.ps1 +++ /dev/null @@ -1,69 +0,0 @@ -Function Set-xAzDoOrganizationGroup { - - param( - - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$GroupName, - - [Parameter()] - [Alias('Description')] - [System.String]$GroupDescription, - - [Parameter()] - [Alias('Lookup')] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - - ) - - # - # Depending on the type of lookup status, the group has been renamed the group has been deleted and recreated. - if ($LookupResult.Status -eq [DSCGetSummaryState]::Renamed) { - - # For the time being write a warning and return - Write-Warning "[Set-xAzDoOrganizationGroup] The group has been renamed. The group will not be set." - return - - } - - # - # Update the group - $params = @{ - ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName - GroupName = $GroupName - GroupDescription = $GroupDescription - GroupDescriptor = $LookupResult.liveCache.descriptor - } - - try { - # Set the group from the API - $group = Set-DevOpsGroup @params - } catch { - throw $_ - } - - # - # Update the cache with the new group - Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' - - # - # Secondarily Replace the local cache with the new group - - if ($null -ne $LookupResult.localCache) { - Remove-CacheItem -Key $LookupResult.localCache.principalName -Type 'Group' - } - Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' - Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' - - # - # Return the group from the cache - return $group - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.ps1 deleted file mode 100644 index 56ca572c2..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.ps1 +++ /dev/null @@ -1,118 +0,0 @@ -function Get-xAzDoProject -{ - [CmdletBinding()] - param ( - [Parameter()] - [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] - [Alias('Name')] - [System.String] $ProjectName, - - [Parameter()] - [Alias('Description')] - [System.String] $ProjectDescription = '', - - [Parameter()] - [ValidateSet('Git', 'Tfvc')] - [System.String] $SourceControlType = 'Git', - - [Parameter()] - [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] - [System.String] $ProcessTemplate = 'Agile', - - [Parameter()] - [ValidateSet('Public', 'Private')] - [System.String] $Visibility = 'Private', - - [Parameter()] - [HashTable] $LookupResult, - - [Parameter()] - [Ensure] $Ensure - ) - - Write-Verbose "[Get-xAzDoProject] Started." - - # Set the organization name - $OrganizationName = $Global:DSCAZDO_OrganizationName - Write-Verbose "[Get-xAzDoProject] Organization Name: $OrganizationName" - - # Construct a hashtable detailing the group - $result = @{ - Ensure = [Ensure]::Absent - ProjectName = $ProjectName - ProjectDescription = $ProjectDescription - SourceControlType = $SourceControlType - ProcessTemplate = $ProcessTemplate - Visibility = $Visibility - propertiesChanged = @() - status = $null - } - Write-Verbose "[Get-xAzDoProject] Initial result hashtable constructed." - - # Perform a lookup to see if the project exists in Azure DevOps - $project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' - # Set the project description to be a string if it is not already. - Write-Verbose "[Get-xAzDoProject] Project lookup result: $project" - - $processTemplateObj = Get-CacheItem -Key $ProcessTemplate -Type 'LiveProcesses' - Write-Verbose "[Get-xAzDoProject] Process template lookup result: $processTemplateObj" - - # Test if the project exists. If the project does not exist, return NotFound - if (($null -eq $project) -and ($null -ne $ProjectName)) - { - $result.Status = [DSCGetSummaryState]::NotFound - Write-Verbose "[Get-xAzDoProject] Project not found." - return $result - } - - # Test if the process template exists. If the process template does not exist, throw an error. - if ($null -eq $processTemplateObj) - { - throw "[Get-xAzDoProject] Process template '$processTemplateObj' not found." - } - - Write-Verbose "[Get-xAzDoProject] Testing source control type." - - # Test if the project is using the same source control type. If the source control type is different, return a conflict. - if ($SourceControlType -ne $project.SourceControlType) - { - Write-Warning "[Get-xAzDoProject] Source control type is different. Current: $($project.SourceControlType), Desired: $SourceControlType" - Write-Warning "[Get-xAzDoProject] Source control type cannot be changed. Please delete the project and recreate it." - } - - # If the project description is null, set it to an empty string. - if ($null -eq $project.description) - { - $project | Add-Member -MemberType NoteProperty -Name description -Value '' - Write-Verbose "[Get-xAzDoProject] Project description was null, set to empty string." - } - - # Test if the project description is the same. If the description is different, return a conflict. - if ($ProjectDescription.Trim() -ne $project.description.Trim()) - { - $result.Status = [DSCGetSummaryState]::Changed - $result.propertiesChanged += 'Description' - Write-Verbose "[Get-xAzDoProject] Project description has changed." - } - - <# - # Test if the project is using the same process template. If the process template is different, return a conflict. - if ($ProcessTemplate -ne $project.ProcessTemplate) - { - $result.Status = [DSCGetSummaryState]::Changed - $result.propertiesChanged += 'ProcessTemplate' - } - #> - - # Test if the project visibility is the same. If the visibility is different, return a conflict. - if ($Visibility -ne $project.Visibility) - { - $result.Status = [DSCGetSummaryState]::Changed - $result.propertiesChanged += 'Visibility' - Write-Verbose "[Get-xAzDoProject] Project visibility has changed." - } - - # Return the group from the cache - Write-Verbose "[Get-xAzDoProject] Returning final result." - return $result -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.ps1 deleted file mode 100644 index 43f27185d..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.ps1 +++ /dev/null @@ -1,62 +0,0 @@ -function Remove-xAzDoProject -{ - [CmdletBinding()] - param ( - [Parameter()] - [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] - [Alias('Name')] - [System.String] $ProjectName, - - [Parameter()] - [Alias('Description')] - [System.String] $ProjectDescription, - - [Parameter()] - [ValidateSet('Git', 'Tfvc')] - [System.String] $SourceControlType = 'Git', - - [Parameter()] - [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] - [System.String] $ProcessTemplate = 'Agile', - - [Parameter()] - [ValidateSet('Public', 'Private')] - [System.String] $Visibility = 'Private', - - [Parameter()] - [HashTable] $LookupResult, - - [Parameter()] - [Ensure] $Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] $Force - ) - - # Set the organization name - $OrganizationName = $Global:DSCAZDO_OrganizationName - Write-Verbose "[Remove-xAzDoProject] Using organization name: $OrganizationName" - - # Perform a lookup to see if the group exists in Azure DevOps - Write-Verbose "[Remove-xAzDoProject] Looking up project: $ProjectName" - $project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' - - if ($null -eq $project) - { - Write-Verbose "[Remove-xAzDoProject] Project $ProjectName not found in cache." - return - } - - Write-Verbose "[Remove-xAzDoProject] Found project $ProjectName with ID: $($project.id)" - - # Remove the project - Write-Verbose "[Remove-xAzDoProject] Removing project $ProjectName from Azure DevOps" - Remove-DevOpsProject -Organization $OrganizationName -ProjectId $project.id - - # Remove the project from the cache and export the cache - Write-Verbose "[Remove-xAzDoProject] Removing project $ProjectName from local cache" - Remove-CacheItem -Key $ProjectName -Type 'LiveProjects' - - Write-Verbose "[Remove-xAzDoProject] Exporting updated cache object for LiveProjects" - Export-CacheObject -CacheType 'LiveProjects' -Content $AzDoLiveProjects -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.ps1 deleted file mode 100644 index 30f7dbdf6..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -function Test-xAzDoProject -{ - [CmdletBinding()] - param - ( - [Parameter()] - [ValidateScript({ Test-AzDevOpsProjectName -ProjectName $_ -IsValid -AllowWildcard })] - [Alias('Name')] - [System.String] - $ProjectName, - - [Parameter()] - [Alias('Description')] - [System.String] - $ProjectDescription, - - [Parameter()] - [ValidateSet('Git','Tfvc')] - [System.String] - $SourceControlType = 'Git', - - [Parameter()] - [ValidateSet('Agile', 'Scrum', 'CMMI', 'Basic')] - [System.String]$ProcessTemplate = 'Agile', - - [Parameter()] - [ValidateSet('Public', 'Private')] - [System.String]$Visibility = 'Private', - - [Parameter()] - [PSCustomObject]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - - ) - - # Should not be triggered. This is a placeholder for the test function. - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.ps1 deleted file mode 100644 index a2b9f4c5e..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.ps1 +++ /dev/null @@ -1,64 +0,0 @@ -Function New-xAzDoProjectGroup { - - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$GroupName, - - [Parameter()] - [Alias('Description')] - [System.String]$GroupDescription, - - [Parameter(Mandatory)] - [Alias('Project')] - [System.String]$ProjectName, - - [Parameter()] - [Alias('Lookup')] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - - ) - - # Define parameters for creating a new DevOps group - $params = @{ - GroupName = $GroupName - GroupDescription = $GroupDescription - ApiUri = "https://vssps.dev.azure.com/{0}" -f $Global:DSCAZDO_OrganizationName - ProjectScopeDescriptor = (Get-CacheItem -Key $ProjectName -Type 'LiveProjects').ProjectDescriptor - } - - # If the project scope descriptor is not found, write a warning message to the console and return. - if ($null -eq $params.ProjectScopeDescriptor) { - Write-Warning "[New-xAzDoProjectGroup] Unable to find project scope descriptor for project '$ProjectName'. Aborting group creation." - return - } - - # Write verbose log before creating a new group - Write-Verbose "[New-xAzDoProjectGroup] Creating a new DevOps group with the following parameters: $($params | Out-String)" - - # Create a new group - $group = New-DevOpsGroup @params - - # Write verbose log after group creation - Write-Verbose "[New-xAzDoProjectGroup] New DevOps group created: $($group | Out-String)" - - # Update the cache with the new group - Refresh-CacheIdentity -Identity $group -Key $group.principalName -CacheType 'LiveGroups' - - Add-CacheItem -Key $group.principalName -Value $group -Type 'Group' - Write-Verbose "[New-xAzDoProjectGroup] Added new group to Group cache with key: $($group.principalName)" - - Set-CacheObject -Content $Global:AzDoGroup -CacheType 'Group' - Write-Verbose "[New-xAzDoProjectGroup] Updated global AzDoGroup cache object." - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/New-xAzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/New-xAzDoProjectServices.ps1 deleted file mode 100644 index 66237dccd..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/New-xAzDoProjectServices.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -Function New-xAzDoProjectServices { - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$ProjectName, - - [Parameter()] - [Alias('Repos')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$GitRepositories = 'Enabled', - - [Parameter()] - [Alias('Board')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$WorkBoards = 'Enabled', - - [Parameter()] - [Alias('Pipelines')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$BuildPipelines = 'Enabled', - - [Parameter()] - [Alias('Tests')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$TestPlans = 'Enabled', - - [Parameter()] - [Alias('Artifacts')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$AzureArtifact = 'Enabled', - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - # Won't be triggered. - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Remove-xAzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Remove-xAzDoProjectServices.ps1 deleted file mode 100644 index 849e320f9..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Remove-xAzDoProjectServices.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -Function Remove-xAzDoProjectServices { - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$ProjectName, - - [Parameter()] - [Alias('Repos')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$GitRepositories = 'Enabled', - - [Parameter()] - [Alias('Board')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$WorkBoards = 'Enabled', - - [Parameter()] - [Alias('Pipelines')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$BuildPipelines = 'Enabled', - - [Parameter()] - [Alias('Tests')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$TestPlans = 'Enabled', - - [Parameter()] - [Alias('Artifacts')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$AzureArtifact = 'Enabled', - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - # Won't be triggered. - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Test-xAzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Test-xAzDoProjectServices.ps1 deleted file mode 100644 index ea67dc312..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectServices/Test-xAzDoProjectServices.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -Function Test-xAzDoProjectServices { - [CmdletBinding()] - [OutputType([System.Management.Automation.PSObject[]])] - param - ( - [Parameter(Mandatory)] - [Alias('Name')] - [System.String]$ProjectName, - - [Parameter()] - [Alias('Repos')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$GitRepositories = 'Enabled', - - [Parameter()] - [Alias('Board')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$WorkBoards = 'Enabled', - - [Parameter()] - [Alias('Pipelines')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$BuildPipelines = 'Enabled', - - [Parameter()] - [Alias('Tests')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$TestPlans = 'Enabled', - - [Parameter()] - [Alias('Artifacts')] - [ValidateSet('Enabled', 'Disabled')] - [System.String]$AzureArtifact = 'Enabled', - - [Parameter()] - [HashTable]$LookupResult, - - [Parameter()] - [Ensure]$Ensure, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Force - ) - - # Won't be triggered. - -} diff --git a/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Initalize-AzDevOpsCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Initalize-AzDevOpsCache.ps1 index 6391c608f..c1e030a39 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Initalize-AzDevOpsCache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Initalize-AzDevOpsCache.ps1 @@ -24,10 +24,12 @@ function Initialize-Cache # Write initial verbose message Write-Verbose "[Initialize-Cache] Starting cache initialization process." - try { + try + { # Attempt to load the cache from the file for each type $cacheTypes = @('Project', 'Team', 'Group', 'SecurityDescriptor', 'LiveGroups', 'LiveProjects') - foreach ($cacheType in $cacheTypes) { + foreach ($cacheType in $cacheTypes) + { Write-Verbose "[Initialize-Cache] Initializing cache object of type: $cacheType" Initialize-CacheObject -CacheType $cacheType } @@ -35,10 +37,8 @@ function Initialize-Cache # Confirm completion of cache initialization Write-Verbose "[Initialize-Cache] Cache initialization process completed successfully." - } catch { - - Write-Error "[Initialize-Cache] An error occurred during cache initialization: $_" - throw - + } catch + { + throw "[Initialize-Cache] An error occurred during cache initialization: $_" } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 index e69de29bb..792d60054 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 @@ -0,0 +1 @@ +# diff --git a/source/WikiSource/Home.md b/source/WikiSource/Home.md index 6f736b10f..e2d60fa2d 100644 --- a/source/WikiSource/Home.md +++ b/source/WikiSource/Home.md @@ -35,14 +35,14 @@ Get-DscResource -Module AzureDevOpsDsc ## DSC Resource Documentation -* [xAzDoGitPermission](\Resources\xAzDoGitPermission.md) -* [xAzDoGitRepository](\Resources\xAzDoGitRepository.md) -* [xAzDoGroupMember](\Resources\xAzDoGroupMember.md) -* [xAzDoGroupPermission](\Resources\xAzDoGroupPermission.md) -* [xAzDoOrganizationGroup](\Resources\xAzDoOrganizationGroup.md) -* [xAzDoProject](\Resources\xAzDoProject.md) -* [xAzDoProjectGroup](\Resources\xAzDoProjectGroup.md) -* [xAzDoProjectServices](\Resources\xAzDoProjectServices.md) +* [AzDoGitPermission](\Resources\AzDoGitPermission.md) +* [AzDoGitRepository](\Resources\AzDoGitRepository.md) +* [AzDoGroupMember](\Resources\AzDoGroupMember.md) +* [AzDoGroupPermission](\Resources\AzDoGroupPermission.md) +* [AzDoOrganizationGroup](\Resources\AzDoOrganizationGroup.md) +* [AzDoProject](\Resources\AzDoProject.md) +* [AzDoProjectGroup](\Resources\AzDoProjectGroup.md) +* [AzDoProjectServices](\Resources\AzDoProjectServices.md) ## Prerequisites diff --git a/source/WikiSource/Resources/xAzDoGitPermission.md b/source/WikiSource/Resources/AzDoGitPermission.md similarity index 89% rename from source/WikiSource/Resources/xAzDoGitPermission.md rename to source/WikiSource/Resources/AzDoGitPermission.md index 3aa3fa3db..494ade5fc 100644 --- a/source/WikiSource/Resources/xAzDoGitPermission.md +++ b/source/WikiSource/Resources/AzDoGitPermission.md @@ -1,9 +1,9 @@ -# DSC xAzDoGitPermission Resource +# DSC AzDoGitPermission Resource # Syntax ``` PowerShell -xAzDoGitPermission [string] #ResourceName +AzDoGitPermission [string] #ResourceName { ProjectName = [String]$ProjectName RepositoryName = [String]$RepositoryName @@ -15,7 +15,7 @@ xAzDoGitPermission [string] #ResourceName ## Permissions Syntax ``` PowerShell -xAzDoGitPermission/Permissions +AzDoGitPermission/Permissions { Identity = [String]$Identity # Syntax # SYNTAX: '[ProjectName | OrganizationName]\ServicePrincipalName, UserPrincipalName, UserDisplayName, GroupDisplayName' @@ -28,7 +28,7 @@ xAzDoGitPermission/Permissions ## Permission Usage ``` PowerShell -xAzDoGitPermission/Permissions/Permission +AzDoGitPermission/Permissions/Permission { PermissionName|PermissionDisplayName = [String]$Name { 'Allow, Deny' } } @@ -75,14 +75,14 @@ It includes properties for specifying the project name, description, source cont # Examples -## Example 1: Sample Configuration using xAzDoGitPermission Resource +## Example 1: Sample Configuration using AzDoGitPermission Resource ``` PowerShell Configuration ExampleConfig { Import-DscResource -ModuleName 'AzDevOpsDsc' Node localhost { - xAzDoGitPermission GitPermission { + AzDoGitPermission GitPermission { Ensure = 'Present' ProjectName = 'SampleProject' RepositoryName = 'SampleGitRepository' @@ -109,7 +109,7 @@ Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose ## Example 2: Sample Configuration using Invoke-DSCResource ``` PowerShell -# Return the current configuration for xAzDoGitPermission +# Return the current configuration for AzDoGitPermission # Ensure is not required $properties = @{ ProjectName = 'SampleProject' @@ -127,7 +127,7 @@ $properties = @{ ) } -Invoke-DSCResource -Name 'xAzDoGitPermission' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +Invoke-DSCResource -Name 'AzDoGitPermission' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' ``` ## Example 3: Sample Configuration to clear permissions for an identity within a group @@ -146,10 +146,10 @@ $properties = @{ ) } -Invoke-DSCResource -Name 'xAzDoGitPermission' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +Invoke-DSCResource -Name 'AzDoGitPermission' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' ``` -## Example 4: Sample Configuration using xAzDoDSCDatum +## Example 4: Sample Configuration using AzDO-DSC-LCM ``` YAML parameters: {} @@ -162,9 +162,9 @@ variables: { resources: - name: SampleGroup Permissions - type: AzureDevOpsDsc/xAzDoGitPermission + type: AzureDevOpsDsc/AzDoGitPermission dependsOn: - - AzureDevOpsDsc/xAzDoProjectGroup/SampleGroupReadAccess + - AzureDevOpsDsc/AzDoProjectGroup/SampleGroupReadAccess properties: projectName: $ProjectName RepositoryName: $RepositoryName diff --git a/source/WikiSource/Resources/xAzDoGitRepository.md b/source/WikiSource/Resources/AzDoGitRepository.md similarity index 84% rename from source/WikiSource/Resources/xAzDoGitRepository.md rename to source/WikiSource/Resources/AzDoGitRepository.md index e71979d7e..2dbe24069 100644 --- a/source/WikiSource/Resources/xAzDoGitRepository.md +++ b/source/WikiSource/Resources/AzDoGitRepository.md @@ -1,9 +1,9 @@ -# DSC xAzDoGitRepository Resource +# DSC AzDoGitRepository Resource ## Syntax ```PowerShell -xAzDoGitRepository [string] #ResourceName +AzDoGitRepository [string] #ResourceName { ProjectName = [String]$ProjectName RepositoryName = [String]$RepositoryName @@ -40,13 +40,13 @@ This resource allows you to manage Git repositories in Azure DevOps projects usi ### Example 1: Create a Git Repository ```PowerShell -Configuration Sample_xAzDoGitRepository +Configuration Sample_AzDoGitRepository { Import-DscResource -ModuleName AzDevOpsDsc Node localhost { - xAzDoGitRepository MyRepository + AzDoGitRepository MyRepository { ProjectName = 'MySampleProject' RepositoryName = 'MySampleRepository' @@ -56,20 +56,20 @@ Configuration Sample_xAzDoGitRepository } } -Sample_xAzDoGitRepository -OutputPath 'C:\DSC\' +Sample_AzDoGitRepository -OutputPath 'C:\DSC\' Start-DscConfiguration -Path 'C:\DSC\' -Wait -Verbose -Force ``` ### Example 2: Remove a Git Repository ```PowerShell -Configuration Remove_xAzDoGitRepository +Configuration Remove_AzDoGitRepository { Import-DscResource -ModuleName AzDevOpsDsc Node localhost { - xAzDoGitRepository MyRepository + AzDoGitRepository MyRepository { ProjectName = 'MySampleProject' RepositoryName = 'MySampleRepository' @@ -78,6 +78,6 @@ Configuration Remove_xAzDoGitRepository } } -Remove_xAzDoGitRepository -OutputPath 'C:\DSC\' +Remove_AzDoGitRepository -OutputPath 'C:\DSC\' Start-DscConfiguration -Path 'C:\DSC\' -Wait -Verbose -Force ``` diff --git a/source/WikiSource/Resources/xAzDoGroupMember.md b/source/WikiSource/Resources/AzDoGroupMember.md similarity index 85% rename from source/WikiSource/Resources/xAzDoGroupMember.md rename to source/WikiSource/Resources/AzDoGroupMember.md index 3a27fc5e1..42c59879a 100644 --- a/source/WikiSource/Resources/xAzDoGroupMember.md +++ b/source/WikiSource/Resources/AzDoGroupMember.md @@ -1,9 +1,9 @@ -# DSC xAzDoGroupMember Resource +# DSC AzDoGroupMember Resource ## Syntax ```PowerShell -xAzDoGroupMember [string] #ResourceName +AzDoGroupMember [string] #ResourceName { GroupName = [String]$GroupName # [ProjectName|OrganizationName]\GroupName # For GroupMember Syntax, refer to # GroupMembers Syntax @@ -49,14 +49,14 @@ This resource is used to manage Azure DevOps group memberships using Desired Sta ## Examples -### Example 1: Sample Configuration for Azure DevOps Group using xAzDoGroupMember Resource +### Example 1: Sample Configuration for Azure DevOps Group using AzDoGroupMember Resource ```PowerShell Configuration ExampleConfig { Import-DscResource -ModuleName 'AzDevOpsDsc' Node localhost { - xAzDoGroupMember GroupExample { + AzDoGroupMember GroupExample { GroupName = 'MySampleGroup' GroupMembers = @('user1@example.com', 'user2@example.com') } @@ -70,13 +70,13 @@ Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose ### Example 2: Sample Configuration for Azure DevOps Group using Invoke-DSCResource ```PowerShell -# Return the current configuration for xAzDoGroupMember +# Return the current configuration for AzDoGroupMember $properties = @{ GroupName = 'MySampleGroup' GroupMembers = @('user1@example.com', 'user2@example.com') } -Invoke-DSCResource -Name 'xAzDoGroupMember' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +Invoke-DSCResource -Name 'AzDoGroupMember' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' ``` ### Example 3: Sample Configuration to remove/exclude an Azure DevOps Group using Invoke-DSCResource @@ -88,10 +88,10 @@ $properties = @{ Ensure = 'Absent' } -Invoke-DSCResource -Name 'xAzDoGroupMember' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +Invoke-DSCResource -Name 'AzDoGroupMember' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' ``` -### Example 4: Sample Configuration using xAzDoDSCDatum +### Example 4: Sample Configuration using AzDO-DSC-LCM ```YAML parameters: {} @@ -104,7 +104,7 @@ variables: { resources: - name: Group - type: AzureDevOpsDsc/xAzDoGroupMember + type: AzureDevOpsDsc/AzDoGroupMember properties: groupName: $GroupName groupMembers: $GroupMembers diff --git a/source/WikiSource/Resources/xAzDoGroupPermission.md b/source/WikiSource/Resources/AzDoGroupPermission.md similarity index 79% rename from source/WikiSource/Resources/xAzDoGroupPermission.md rename to source/WikiSource/Resources/AzDoGroupPermission.md index 797b7cd83..990575577 100644 --- a/source/WikiSource/Resources/xAzDoGroupPermission.md +++ b/source/WikiSource/Resources/AzDoGroupPermission.md @@ -1,13 +1,13 @@ -# xAzDoGroupPermission Resource Documentation (Currently Disabled) +# AzDoGroupPermission Resource Documentation (Currently Disabled) ## Overview -The `xAzDoGroupPermission` resource is part of the Azure DevOps Desired State Configuration (DSC) module. It allows you to manage group permissions within an Azure DevOps project repository. This resource provides properties for specifying the group name, permission inheritance, and a list of permissions to be set. +The `AzDoGroupPermission` resource is part of the Azure DevOps Desired State Configuration (DSC) module. It allows you to manage group permissions within an Azure DevOps project repository. This resource provides properties for specifying the group name, permission inheritance, and a list of permissions to be set. ## Syntax ```PowerShell -xAzDoGroupPermission [string] #ResourceName +AzDoGroupPermission [string] #ResourceName { GroupName = [String]$GroupName [ isInherited = [Boolean]$isInherited ] @@ -24,7 +24,7 @@ xAzDoGroupPermission [string] #ResourceName ## Permissions Syntax ```PowerShell -xAzDoGroupPermission/Permissions +AzDoGroupPermission/Permissions { Identity = [String]$Identity # SYNTAX: '[ProjectName | OrganizationName]\ServicePrincipalName, UserPrincipalName, UserDisplayName, GroupDisplayName' @@ -38,7 +38,7 @@ xAzDoGroupPermission/Permissions ### Permission Usage ```PowerShell -xAzDoGroupPermission/Permissions/Permission +AzDoGroupPermission/Permissions/Permission { PermissionName|PermissionDisplayName = [String]$Name { 'Allow, Deny' } } @@ -66,7 +66,7 @@ Configuration ExampleConfig { Import-DscResource -ModuleName 'AzDevOpsDsc' Node localhost { - xAzDoGroupPermission GroupPermission { + AzDoGroupPermission GroupPermission { GroupName = 'SampleGroup' isInherited = $true Permissions = @( @@ -102,19 +102,19 @@ $properties = @{ ) } -Invoke-DSCResource -Name 'xAzDoGroupPermission' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +Invoke-DSCResource -Name 'AzDoGroupPermission' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' ``` ## Methods ### Get Method -Retrieves the current state properties of the `xAzDoGroupPermission` resource. +Retrieves the current state properties of the `AzDoGroupPermission` resource. ```PowerShell -[xAzDoGroupPermission] Get() +[AzDoGroupPermission] Get() { - return [xAzDoGroupPermission]$($this.GetDscCurrentStateProperties()) + return [AzDoGroupPermission]$($this.GetDscCurrentStateProperties()) } ``` @@ -129,13 +129,16 @@ hidden [Hashtable] GetDscCurrentStateProperties([PSCustomObject]$CurrentResource Ensure = [Ensure]::Absent } - if ($null -eq $CurrentResourceObject) { return $properties } + if ($null -eq $CurrentResourceObject) + { + return $properties + } $properties.GroupName = $CurrentResourceObject.GroupName $properties.isInherited = $CurrentResourceObject.isInherited $properties.Permissions = $CurrentResourceObject.Permissions - Write-Verbose "[xAzDoGroupPermission] Current state properties: $($properties | Out-String)" + Write-Verbose "[AzDoGroupPermission] Current state properties: $($properties | Out-String)" return $properties } diff --git a/source/WikiSource/Resources/xAzDoOrganizationGroup.md b/source/WikiSource/Resources/AzDoOrganizationGroup.md similarity index 80% rename from source/WikiSource/Resources/xAzDoOrganizationGroup.md rename to source/WikiSource/Resources/AzDoOrganizationGroup.md index 8ea8371ba..a271e5a99 100644 --- a/source/WikiSource/Resources/xAzDoOrganizationGroup.md +++ b/source/WikiSource/Resources/AzDoOrganizationGroup.md @@ -1,9 +1,9 @@ -# DSC xAzDoOrganizationGroup Resource +# DSC AzDoOrganizationGroup Resource ## Syntax ```PowerShell -xAzDoOrganizationGroup [string] #ResourceName +AzDoOrganizationGroup [string] #ResourceName { GroupName = [String]$GroupName [ GroupDescription = [String]$GroupDescription ] @@ -23,14 +23,14 @@ This resource is used to manage Azure DevOps organization groups using Desired S ## Examples -## Example 1: Sample Configuration using xAzDoOrganizationGroup Resource +## Example 1: Sample Configuration using AzDoOrganizationGroup Resource ``` PowerShell Configuration ExampleConfig { Import-DscResource -ModuleName 'AzDevOpsDsc' Node localhost { - xAzDoOrganizationGroup OrgGroup { + AzDoOrganizationGroup OrgGroup { Ensure = 'Present' GroupName = 'SampleGroup' GroupDescription = 'This is a sample group!' @@ -46,17 +46,17 @@ Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose ## Example 2: Sample Configuration using Invoke-DSCResource ``` PowerShell -# Return the current configuration for xAzDoGitPermission +# Return the current configuration for AzDoGitPermission # Ensure is not required $properties = @{ GroupName = 'SampleGroup' GroupDescription = 'This is a sample group!' } -Invoke-DSCResource -Name 'xAzDoOrganizationGroup' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +Invoke-DSCResource -Name 'AzDoOrganizationGroup' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' ``` -## Example 3: Sample Configuration using xAzDoDSCDatum +## Example 3: Sample Configuration using AzDO-DSC-LCM ``` YAML parameters: {} @@ -67,13 +67,13 @@ variables: { resources: - name: Team Leaders Organization Group - type: AzureDevOpsDsc/xAzDoOrganizationGroup + type: AzureDevOpsDsc/AzDoOrganizationGroup properties: GroupName: AZDO_TeamLeaders_Group GroupDescription: Team Leaders Organization Group - name: Service Accounts Organization Group - type: AzureDevOpsDsc/xAzDoOrganizationGroup + type: AzureDevOpsDsc/AzDoOrganizationGroup properties: GroupName: AZDO_ServiceAccounts_Group GroupDescription: Service Accounts Organization Group diff --git a/source/WikiSource/Resources/xAzDoProject.md b/source/WikiSource/Resources/AzDoProject.md similarity index 86% rename from source/WikiSource/Resources/xAzDoProject.md rename to source/WikiSource/Resources/AzDoProject.md index 5df0cad48..4f042c0fa 100644 --- a/source/WikiSource/Resources/xAzDoProject.md +++ b/source/WikiSource/Resources/AzDoProject.md @@ -1,9 +1,9 @@ -# DSC xAzDoProject Resource +# DSC AzDoProject Resource # Syntax ``` PowerShell -xAzDoProject [string] #ResourceName +AzDoProject [string] #ResourceName { ProjectName = [String]$ProjectName [ Ensure = [String] {'Present', 'Absent'}] @@ -31,14 +31,14 @@ It allows you to define the properties of an Azure DevOps project and ensures th # Examples -## Example 1: Sample Configuration for Azure DevOps Project using xAzDoProject Resource +## Example 1: Sample Configuration for Azure DevOps Project using AzDoProject Resource ``` PowerShell Configuration ExampleConfig { Import-DscResource -ModuleName 'AzDevOpsDsc' Node localhost { - xAzDoProject ProjectExample { + AzDoProject ProjectExample { Ensure = 'Present' ProjectName = 'MySampleProject' ProjectDescription = 'This is a sample Azure DevOps project.' @@ -57,7 +57,7 @@ Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose ## Example 2: Sample Configuration for Azure DevOps Project using Invoke-DSCResource ``` PowerShell -# Return the current configuration for xAzDoProject +# Return the current configuration for AzDoProject # Ensure is not required $properties = @{ ProjectName = 'MySameProject' @@ -67,7 +67,7 @@ $properties = @{ Visibility = 'Private' } -Invoke-DSCResource -Name 'xAzDoProject' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +Invoke-DSCResource -Name 'AzDoProject' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' ``` ## Example 3: Sample Configuration to remove/exclude an Azure DevOps Project using Invoke-DSCResource @@ -79,10 +79,10 @@ $properties = @{ Ensure = 'Absent' } -Invoke-DSCResource -Name 'xAzDoProject' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +Invoke-DSCResource -Name 'AzDoProject' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' ``` -## Example 4: Sample Configuration using xAzDoDSCDatum +## Example 4: Sample Configuration using AzDO-DSC-LCM ``` YAML parameters: {} @@ -95,7 +95,7 @@ variables: { resources: - name: Project - type: AzureDevOpsDsc/xAzDoProject + type: AzureDevOpsDsc/AzDoProject properties: projectName: $ProjectName projectDescription: $ProjectDescription diff --git a/source/WikiSource/Resources/xAzDoProjectGroup.md b/source/WikiSource/Resources/AzDoProjectGroup.md similarity index 83% rename from source/WikiSource/Resources/xAzDoProjectGroup.md rename to source/WikiSource/Resources/AzDoProjectGroup.md index 7bc167b9b..cdf7ffd85 100644 --- a/source/WikiSource/Resources/xAzDoProjectGroup.md +++ b/source/WikiSource/Resources/AzDoProjectGroup.md @@ -1,9 +1,9 @@ -# DSC xAzDoProjectGroup Resource +# DSC AzDoProjectGroup Resource ## Syntax ```PowerShell -xAzDoProjectGroup [string] #ResourceName +AzDoProjectGroup [string] #ResourceName { GroupName = [String]$GroupName ProjectName = [String]$ProjectName @@ -25,14 +25,14 @@ This resource is used to manage Azure DevOps project groups using Desired State ## Examples -### Example 1: Sample Configuration using xAzDoProjectGroup Resource +### Example 1: Sample Configuration using AzDoProjectGroup Resource ```PowerShell Configuration ExampleConfig { Import-DscResource -ModuleName 'AzDevOpsDsc' Node localhost { - xAzDoProjectGroup ProjectGroup { + AzDoProjectGroup ProjectGroup { Ensure = 'Present' GroupName = 'SampleProjectGroup' ProjectName = 'SampleProject' @@ -48,7 +48,7 @@ Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose ### Example 2: Sample Configuration using Invoke-DSCResource ```PowerShell -# Return the current configuration for xAzDoProjectGroup +# Return the current configuration for AzDoProjectGroup # Ensure is not required $properties = @{ GroupName = 'SampleProjectGroup' @@ -56,10 +56,10 @@ $properties = @{ GroupDescription = 'This is a sample project group!' } -Invoke-DSCResource -Name 'xAzDoProjectGroup' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +Invoke-DSCResource -Name 'AzDoProjectGroup' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' ``` -### Example 3: Sample Configuration using xAzDoDSCDatum +### Example 3: Sample Configuration using AzDO-DSC-LCM ```YAML parameters: {} @@ -70,14 +70,14 @@ variables: { resources: - name: Team Leaders Project Group - type: AzureDevOpsDsc/xAzDoProjectGroup + type: AzureDevOpsDsc/AzDoProjectGroup properties: GroupName: AZDO_TeamLeaders_ProjectGroup ProjectName: SampleProject GroupDescription: Team Leaders Project Group - name: Service Accounts Project Group - type: AzureDevOpsDsc/xAzDoProjectGroup + type: AzureDevOpsDsc/AzDoProjectGroup properties: GroupName: AZDO_ServiceAccounts_ProjectGroup ProjectName: SampleProject diff --git a/source/WikiSource/Resources/xAzDoProjectServices.md b/source/WikiSource/Resources/AzDoProjectServices.md similarity index 87% rename from source/WikiSource/Resources/xAzDoProjectServices.md rename to source/WikiSource/Resources/AzDoProjectServices.md index 55526e594..2c088aa31 100644 --- a/source/WikiSource/Resources/xAzDoProjectServices.md +++ b/source/WikiSource/Resources/AzDoProjectServices.md @@ -1,9 +1,9 @@ -# DSC xAzDoProjectServices Resource +# DSC AzDoProjectServices Resource ## Syntax ```PowerShell -xAzDoProjectServices [string] #ResourceName +AzDoProjectServices [string] #ResourceName { ProjectName = [String]$ProjectName [ GitRepositories = [String]$GitRepositories { 'Enabled' | 'Disabled' } ] @@ -31,14 +31,14 @@ This resource is used to manage Azure DevOps project services using Desired Stat ## Examples -### Example 1: Sample Configuration using xAzDoProjectServices Resource +### Example 1: Sample Configuration using AzDoProjectServices Resource ```PowerShell Configuration ExampleConfig { Import-DscResource -ModuleName 'AzDevOpsDsc' Node localhost { - xAzDoProjectServices ProjectServices { + AzDoProjectServices ProjectServices { Ensure = 'Present' ProjectName = 'SampleProject' GitRepositories = 'Enabled' @@ -57,7 +57,7 @@ Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose ### Example 2: Sample Configuration using Invoke-DSCResource ```PowerShell -# Return the current configuration for xAzDoProjectServices +# Return the current configuration for AzDoProjectServices # Ensure is not required $properties = @{ ProjectName = 'SampleProject' @@ -68,10 +68,10 @@ $properties = @{ AzureArtifact = 'Enabled' } -Invoke-DSCResource -Name 'xAzDoProjectServices' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +Invoke-DSCResource -Name 'AzDoProjectServices' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' ``` -### Example 3: Sample Configuration using xAzDoDSCDatum +### Example 3: Sample Configuration using AzDO-DSC-LCM ```YAML parameters: {} @@ -82,7 +82,7 @@ variables: { resources: - name: Sample Project Services - type: AzureDevOpsDsc/xAzDoProjectServices + type: AzureDevOpsDsc/AzDoProjectServices properties: ProjectName: SampleProject GitRepositories: Enabled diff --git a/tests/Integration/Invoke-Tests.ps1 b/tests/Integration/Invoke-Tests.ps1 index b2579b5dc..15404ac4e 100644 --- a/tests/Integration/Invoke-Tests.ps1 +++ b/tests/Integration/Invoke-Tests.ps1 @@ -1,6 +1,6 @@ #Requires -Modules @{ ModuleName="Pester"; ModuleVersion="5.0.0" } param( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String]$TestFrameworkConfigurationPath ) diff --git a/tests/Integration/Resources/xAzDoGitPermission.tests.ps1 b/tests/Integration/Resources/xAzDoGitPermission.tests.ps1 index 195fb9952..7e6f95b49 100644 --- a/tests/Integration/Resources/xAzDoGitPermission.tests.ps1 +++ b/tests/Integration/Resources/xAzDoGitPermission.tests.ps1 @@ -1,6 +1,6 @@ -Describe "xAzDoGitPermission Integration Tests" { +Describe "AzDoGitPermission Integration Tests" { BeforeAll { @@ -8,7 +8,7 @@ Describe "xAzDoGitPermission Integration Tests" { $PROJECTNAME = 'TESTPROJECT_GIT_PERMISSION' $parameters = @{ - Name = 'xAzDoGitPermission' + Name = 'AzDoGitPermission' ModuleName = 'AzureDevOpsDsc' property = @{ ProjectName = $PROJECTNAME diff --git a/tests/Integration/Resources/xAzDoGitRepository.tests.ps1 b/tests/Integration/Resources/xAzDoGitRepository.tests.ps1 index 2af3fe06f..574e932bb 100644 --- a/tests/Integration/Resources/xAzDoGitRepository.tests.ps1 +++ b/tests/Integration/Resources/xAzDoGitRepository.tests.ps1 @@ -1,4 +1,4 @@ -Describe "xAzDoGitRepository Integration Tests" { +Describe "AzDoGitRepository Integration Tests" { BeforeAll { @@ -7,7 +7,7 @@ Describe "xAzDoGitRepository Integration Tests" { # Define common parameters $parameters = @{ - Name = 'xAzDoGitRepository' + Name = 'AzDoGitRepository' ModuleName = 'AzureDevOpsDsc' } diff --git a/tests/Integration/Resources/xAzDoGroupMember.tests.ps1 b/tests/Integration/Resources/xAzDoGroupMember.tests.ps1 index a7021adbe..e10395dc2 100644 --- a/tests/Integration/Resources/xAzDoGroupMember.tests.ps1 +++ b/tests/Integration/Resources/xAzDoGroupMember.tests.ps1 @@ -1,4 +1,4 @@ -Describe "xAzDoGroupMember Integration Tests" { +Describe "AzDoGroupMember Integration Tests" { BeforeAll { @@ -7,7 +7,7 @@ Describe "xAzDoGroupMember Integration Tests" { # Define common parameters $parameters = @{ - Name = 'xAzDoGroupMember' + Name = 'AzDoGroupMember' ModuleName = 'AzureDevOpsDsc' } @@ -36,7 +36,7 @@ Describe "xAzDoGroupMember Integration Tests" { # Define properties for the DSC resource. # In this case, we specify a project name 'TESTPROJECT_GROUPMEMBER'. $parameters.property = @{ - GroupName = "[{0}]\TESTGROUP" -f $PROJECTNAME + GroupName = '[{0}]\TESTGROUP' -f $PROJECTNAME GroupMembers = "[$PROJECTNAME]\Group1", "[$PROJECTNAME]\Group2" } diff --git a/tests/Integration/Resources/xAzDoGroupPermission.tests.ps1 b/tests/Integration/Resources/xAzDoGroupPermission.tests.ps1 index 4b144202c..a8204235d 100644 --- a/tests/Integration/Resources/xAzDoGroupPermission.tests.ps1 +++ b/tests/Integration/Resources/xAzDoGroupPermission.tests.ps1 @@ -1,4 +1,4 @@ -Describe "xAzDoGroupPermission intergration tests" -skip { +Describe "AzDoGroupPermission intergration tests" -skip { BeforeAll { @@ -7,7 +7,7 @@ Describe "xAzDoGroupPermission intergration tests" -skip { $GroupName = 'TESTGROUP' $parameters = @{ - Name = 'xAzDoGroupPermission' + Name = 'AzDoGroupPermission' ModuleName = 'AzureDevOpsDsc' property = @{ GroupName = "[$PROJECTNAME]\$GroupName" diff --git a/tests/Integration/Resources/xAzDoOrganizationGroup.Description.tests.ps1 b/tests/Integration/Resources/xAzDoOrganizationGroup.Description.tests.ps1 index 16da8fa0d..529976e10 100644 --- a/tests/Integration/Resources/xAzDoOrganizationGroup.Description.tests.ps1 +++ b/tests/Integration/Resources/xAzDoOrganizationGroup.Description.tests.ps1 @@ -1,4 +1,4 @@ -Describe "xAzDoOrganizationGroup Integration Tests - With Description" { +Describe "AzDoOrganizationGroup Integration Tests - With Description" { BeforeAll { @@ -6,7 +6,7 @@ Describe "xAzDoOrganizationGroup Integration Tests - With Description" { # Define common parameters $parameters = @{ - Name = 'xAzDoOrganizationGroup' + Name = 'AzDoOrganizationGroup' ModuleName = 'AzureDevOpsDsc' } diff --git a/tests/Integration/Resources/xAzDoOrganizationGroup.NoDescription.tests.ps1 b/tests/Integration/Resources/xAzDoOrganizationGroup.NoDescription.tests.ps1 index a33af6be2..a6943d6f4 100644 --- a/tests/Integration/Resources/xAzDoOrganizationGroup.NoDescription.tests.ps1 +++ b/tests/Integration/Resources/xAzDoOrganizationGroup.NoDescription.tests.ps1 @@ -1,4 +1,4 @@ -Describe "xAzDoOrganizationGroup Integration Tests - No Description" { +Describe "AzDoOrganizationGroup Integration Tests - No Description" { BeforeAll { @@ -6,7 +6,7 @@ Describe "xAzDoOrganizationGroup Integration Tests - No Description" { # Define common parameters $parameters = @{ - Name = 'xAzDoOrganizationGroup' + Name = 'AzDoOrganizationGroup' ModuleName = 'AzureDevOpsDsc' } diff --git a/tests/Integration/Resources/xAzDoProject.Description.tests.ps1 b/tests/Integration/Resources/xAzDoProject.Description.tests.ps1 index 31d1796b6..2559a46e2 100644 --- a/tests/Integration/Resources/xAzDoProject.Description.tests.ps1 +++ b/tests/Integration/Resources/xAzDoProject.Description.tests.ps1 @@ -1,4 +1,4 @@ -Describe "xAzDoProject Integration Tests - With Description" { +Describe "AzDoProject Integration Tests - With Description" { BeforeAll { @@ -6,7 +6,7 @@ Describe "xAzDoProject Integration Tests - With Description" { # Define common parameters $parameters = @{ - Name = 'xAzDoProject' + Name = 'AzDoProject' ModuleName = 'AzureDevOpsDsc' } diff --git a/tests/Integration/Resources/xAzDoProject.NoDescription.tests.ps1 b/tests/Integration/Resources/xAzDoProject.NoDescription.tests.ps1 index caa0978e2..0930262c7 100644 --- a/tests/Integration/Resources/xAzDoProject.NoDescription.tests.ps1 +++ b/tests/Integration/Resources/xAzDoProject.NoDescription.tests.ps1 @@ -1,4 +1,4 @@ -Describe "xAzDoProject Integration Tests - No Description" { +Describe "AzDoProject Integration Tests - No Description" { BeforeAll { @@ -6,7 +6,7 @@ Describe "xAzDoProject Integration Tests - No Description" { # Define common parameters $parameters = @{ - Name = 'xAzDoProject' + Name = 'AzDoProject' ModuleName = 'AzureDevOpsDsc' } diff --git a/tests/Integration/Resources/xAzDoProjectGroup.Description.tests.ps1 b/tests/Integration/Resources/xAzDoProjectGroup.Description.tests.ps1 index c9b74a4a1..c6186d323 100644 --- a/tests/Integration/Resources/xAzDoProjectGroup.Description.tests.ps1 +++ b/tests/Integration/Resources/xAzDoProjectGroup.Description.tests.ps1 @@ -1,4 +1,4 @@ -Describe "xAzDoProjectGroup Integration Tests - With Description" { +Describe "AzDoProjectGroup Integration Tests - With Description" { BeforeAll { @@ -10,7 +10,7 @@ Describe "xAzDoProjectGroup Integration Tests - With Description" { # Define common parameters $parameters = @{ - Name = 'xAzDoProjectGroup' + Name = 'AzDoProjectGroup' ModuleName = 'AzureDevOpsDsc' } diff --git a/tests/Integration/Resources/xAzDoProjectServices.tests.ps1 b/tests/Integration/Resources/xAzDoProjectServices.tests.ps1 index d96046609..a18101ec3 100644 --- a/tests/Integration/Resources/xAzDoProjectServices.tests.ps1 +++ b/tests/Integration/Resources/xAzDoProjectServices.tests.ps1 @@ -1,4 +1,4 @@ -Describe "xAzDoProjectServices Integration Tests" { +Describe "AzDoProjectServices Integration Tests" { BeforeAll { @@ -12,7 +12,7 @@ Describe "xAzDoProjectServices Integration Tests" { # Define common parameters $parameters = @{ - Name = 'xAzDoProjectServices' + Name = 'AzDoProjectServices' ModuleName = 'AzureDevOpsDsc' } diff --git a/tests/Integration/Supporting/API/Add-Header.ps1 b/tests/Integration/Supporting/API/Add-Header.ps1 index dec6b7985..733013d1f 100644 --- a/tests/Integration/Supporting/API/Add-Header.ps1 +++ b/tests/Integration/Supporting/API/Add-Header.ps1 @@ -4,7 +4,8 @@ Function Add-Header { $headerValue = "" - switch ($Global:DSCAZDO_AuthenticationToken.tokenType) { + switch ($Global:DSCAZDO_AuthenticationToken.tokenType) + { # If the token is null {$null} { @@ -16,7 +17,7 @@ Function Add-Header { # Personal Access Token # Add the Personal Access Token to the header - $headerValue = "Authorization: Basic {0}" -f $Global:DSCAZDO_AuthenticationToken.Token + $headerValue = 'Authorization: Basic {0}' -f $Global:DSCAZDO_AuthenticationToken.Token break } {$_ -eq 'ManagedIdentity'} { diff --git a/tests/Integration/Supporting/API/ConvertTo-Base64String.ps1 b/tests/Integration/Supporting/API/ConvertTo-Base64String.ps1 index fb2170d50..a7caec5b2 100644 --- a/tests/Integration/Supporting/API/ConvertTo-Base64String.ps1 +++ b/tests/Integration/Supporting/API/ConvertTo-Base64String.ps1 @@ -2,7 +2,7 @@ function ConvertTo-Base64String { [CmdletBinding()] param ( - [Parameter(Mandatory, ValueFromPipeline)] + [Parameter(Mandatory = $true, ValueFromPipeline)] [ValidateNotNullOrEmpty()] [String] $InputObject diff --git a/tests/Integration/Supporting/API/Get-MIToken.ps1 b/tests/Integration/Supporting/API/Get-MIToken.ps1 index 5b213a538..964174f48 100644 --- a/tests/Integration/Supporting/API/Get-MIToken.ps1 +++ b/tests/Integration/Supporting/API/Get-MIToken.ps1 @@ -2,7 +2,7 @@ Function Get-MIToken { [CmdletBinding()] param ( # Organization Name - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String] $OrganizationName ) @@ -24,7 +24,10 @@ Function Get-MIToken { # Invoke the RestAPI try { $response = Invoke-RestMethod @ManagedIdentityParams } catch { Throw $_ } # Test the response - if ($null -eq $response.access_token) { throw "Error. Access token not returned from Azure Instance Metadata Service. Please ensure that the Azure Instance Metadata Service is available." } + if ($null -eq $response.access_token) + { + throw "Error. Access token not returned from Azure Instance Metadata Service. Please ensure that the Azure Instance Metadata Service is available." + } # Return the token if the verify switch is not set return @{ diff --git a/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 b/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 index fdf55eb28..94a86d30b 100644 --- a/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 +++ b/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 @@ -140,7 +140,13 @@ function Invoke-APIRestMethod if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::TooManyRequests) { # If so, wait for the specified number of seconds before retrying - $retryAfter = $_.Exception.Response.Headers | ForEach-Object { if ($_.Key -eq "Retry-After") { return $_.Value } } + $retryAfter = $_.Exception.Response.Headers | ForEach-Object { + if ($_.Key -eq "Retry-After") + { + return $_.Value + } + } + if ($retryAfter) { $retryAfter = [int]$retryAfter diff --git a/tests/Integration/Supporting/API/New-AuthProvider.ps1 b/tests/Integration/Supporting/API/New-AuthProvider.ps1 index 377d0ca38..2d7bed2d9 100644 --- a/tests/Integration/Supporting/API/New-AuthProvider.ps1 +++ b/tests/Integration/Supporting/API/New-AuthProvider.ps1 @@ -4,14 +4,14 @@ Function New-AuthProvider { [CmdletBinding(DefaultParameterSetName = 'PersonalAccessToken')] param ( # Organization Name - [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] - [Parameter(Mandatory, ParameterSetName = 'ManagedIdentity')] + [Parameter(Mandatory = $true, ParameterSetName = 'PersonalAccessToken')] + [Parameter(Mandatory = $true, ParameterSetName = 'ManagedIdentity')] [Alias('OrgName')] [String] $OrganizationName, # Personal Access Token - [Parameter(Mandatory, ParameterSetName = 'PersonalAccessToken')] + [Parameter(Mandatory = $true, ParameterSetName = 'PersonalAccessToken')] [Alias('PAT')] [String] $PersonalAccessToken, @@ -26,19 +26,18 @@ Function New-AuthProvider { $Global:DSCAZDO_OrganizationName = $OrganizationName $Global:DSCAZDO_AuthenticationToken = $null - # # If the parameterset is PersonalAccessToken - if ($PSCmdlet.ParameterSetName -eq 'PersonalAccessToken') { + if ($PSCmdlet.ParameterSetName -eq 'PersonalAccessToken') + { Write-Verbose "[New-AuthProvider] Creating a new Personal Access Token with OrganizationName $OrganizationName." $Global:DSCAZDO_AuthenticationToken = @{ - 'token' = ":{0}" -f (ConvertTo-Base64String $PersonalAccessToken) + 'token' = ':{0}' -f (ConvertTo-Base64String $PersonalAccessToken) 'type' = 'PAT' } } - - # # If the parameterset is ManagedIdentity - elseif ($PSCmdlet.ParameterSetName -eq 'ManagedIdentity') { + elseif ($PSCmdlet.ParameterSetName -eq 'ManagedIdentity') + { Write-Verbose "[New-AuthProvider] Creating a new Azure Managed Identity with OrganizationName $OrganizationName." # If the Token is not Valid. Get a new Token. $Global:DSCAZDO_AuthenticationToken = @{ @@ -47,5 +46,4 @@ Function New-AuthProvider { } } - } diff --git a/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 b/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 index 2740620bc..77b54ed7d 100644 --- a/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 +++ b/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 @@ -3,7 +3,7 @@ Function List-DevOpsGroups { [OutputType([System.Object])] Param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string] $Organization, [Parameter()] @@ -20,7 +20,8 @@ Function List-DevOpsGroups { # Invoke the Rest API to get the groups $groups = Invoke-APIRestMethod @params - if ($null -eq $groups.value) { + if ($null -eq $groups.value) + { return $null } diff --git a/tests/Integration/Supporting/APICalls/List-DevOpsProjects.ps1 b/tests/Integration/Supporting/APICalls/List-DevOpsProjects.ps1 index 7ecebca2a..4e064a6f0 100644 --- a/tests/Integration/Supporting/APICalls/List-DevOpsProjects.ps1 +++ b/tests/Integration/Supporting/APICalls/List-DevOpsProjects.ps1 @@ -3,7 +3,7 @@ function List-DevOpsProjects { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$OrganizationName, [Parameter()] diff --git a/tests/Integration/Supporting/APICalls/Remove-DevOpsGroup.ps1 b/tests/Integration/Supporting/APICalls/Remove-DevOpsGroup.ps1 index 01ee17dc3..6fc39666a 100644 --- a/tests/Integration/Supporting/APICalls/Remove-DevOpsGroup.ps1 +++ b/tests/Integration/Supporting/APICalls/Remove-DevOpsGroup.ps1 @@ -33,7 +33,7 @@ Function Remove-DevOpsGroup { [String] $ApiVersion = $(Get-AzDevOpsApiVersion -Default), - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String] $OrganizationName, @@ -43,7 +43,7 @@ Function Remove-DevOpsGroup { ) $params = @{ - Uri = "https://vssps.dev.azure.com/{0}/_apis/graph/groups/{1}?api-version={2}" -f $OrganizationName, $GroupDescriptor, $ApiVersion + Uri = 'https://vssps.dev.azure.com/{0}/_apis/graph/groups/{1}?api-version={2}' -f $OrganizationName, $GroupDescriptor, $ApiVersion Method = 'Delete' ContentType = 'application/json' } diff --git a/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 b/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 index d619b8f20..8b1d6d8ed 100644 --- a/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 +++ b/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 @@ -40,7 +40,7 @@ function Remove-DevOpsProject # Define the API version to use $params = @{ - Uri = "https://dev.azure.com/{0}/_apis/projects/{1}?api-version={2}" -f $Organization, $ProjectId, $ApiVersion + Uri = 'https://dev.azure.com/{0}/_apis/projects/{1}?api-version={2}' -f $Organization, $ProjectId, $ApiVersion Method = "DELETE" } diff --git a/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 b/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 index 57901cb70..e05415d5d 100644 --- a/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 +++ b/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 @@ -9,7 +9,7 @@ Function New-Project { # Create a new project $projectParams = @{ - Name = 'xAzDoProject' + Name = 'AzDoProject' ModuleName = 'AzureDevOpsDsc' Method = 'Set' property = @{ @@ -32,7 +32,7 @@ Function New-Repository { # Create a new repository $parameters = @{ - Name = 'xAzDoGitRepository' + Name = 'AzDoGitRepository' ModuleName = 'AzureDevOpsDsc' Method = 'Set' property = @{ @@ -58,7 +58,7 @@ Function New-Group { # Create a new group $groupParams = @{ - Name = 'xAzDoProjectGroup' + Name = 'AzDoProjectGroup' ModuleName = 'AzureDevOpsDsc' Method = 'Set' property = @{ diff --git a/tests/Integration/Supporting/Initalize-TestFramework.ps1 b/tests/Integration/Supporting/Initalize-TestFramework.ps1 index 78d18ce5d..695d37814 100644 --- a/tests/Integration/Supporting/Initalize-TestFramework.ps1 +++ b/tests/Integration/Supporting/Initalize-TestFramework.ps1 @@ -1,5 +1,5 @@ param( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String]$TestFrameworkConfigurationPath ) @@ -30,11 +30,15 @@ if ($TestFrameworkConfiguration.AuthenticationType -eq 'PAT') # Authenticate with a Personal Access Token #New-AuthProvider -OrganizationName $TestFrameworkConfiguration.Organization -PersonalAccessToken $TestFrameworkConfiguration.PATToken New-AzDoAuthenticationProvider -OrganizationName $TestFrameworkConfiguration.Organization -PersonalAccessToken $TestFrameworkConfiguration.PATToken -} elseif ($TestFrameworkConfiguration.AuthenticationType -eq 'ManagedIdentity') { +} +elseif ($TestFrameworkConfiguration.AuthenticationType -eq 'ManagedIdentity') +{ # Authenticate with a Managed Identity #New-AuthProvider -OrganizationName $TestFrameworkConfiguration.Organization -useManagedIdentity New-AzDoAuthenticationProvider -OrganizationName $TestFrameworkConfiguration.Organization -useManagedIdentity -} else { +} +else +{ throw "[Initialize-TestFramework] Invalid Authentication Type: $($TestFrameworkConfiguration.AuthenticationType)" } diff --git a/tests/Unit/Classes/Classes.BeforeAll.ps1 b/tests/Unit/Classes/Classes.BeforeAll.ps1 index 173a40707..ebb497892 100644 --- a/tests/Unit/Classes/Classes.BeforeAll.ps1 +++ b/tests/Unit/Classes/Classes.BeforeAll.ps1 @@ -1,5 +1,5 @@ param( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [String] $RepositoryPath ) diff --git a/tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 b/tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 index e2e7935ae..cb2f21133 100644 --- a/tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 +++ b/tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 @@ -1,20 +1,20 @@ -# Import the module containing the xAzDoGroupPermission class -# Describe block for xAzDoGroupPermission tests -Describe 'xAzDoGroupPermission Tests' { +# Import the module containing the AzDoGroupPermission class +# Describe block for AzDoGroupPermission tests +Describe 'AzDoGroupPermission Tests' { # Test case to check if the class can be instantiated Context 'Instantiation' { - It 'Should create an instance of the xAzDoGroupPermission class' { - $groupPermission = [xAzDoGroupPermission]::new() + It 'Should create an instance of the AzDoGroupPermission class' { + $groupPermission = [AzDoGroupPermission]::new() $groupPermission | Should -Not -BeNullOrEmpty - $groupPermission | Should -BeOfType 'xAzDoGroupPermission' + $groupPermission | Should -BeOfType 'AzDoGroupPermission' } } # Test case to check default values Context 'Default Values' { It 'Should have default value for isInherited as $true' { - $groupPermission = [xAzDoGroupPermission]::new() + $groupPermission = [AzDoGroupPermission]::new() $groupPermission.isInherited | Should -Be $true } } @@ -22,13 +22,13 @@ Describe 'xAzDoGroupPermission Tests' { # Test case to check property assignments Context 'Property Assignments' { It 'Should allow setting and getting GroupName property' { - $groupPermission = [xAzDoGroupPermission]::new() + $groupPermission = [AzDoGroupPermission]::new() $groupPermission.GroupName = 'TestGroup' $groupPermission.GroupName | Should -Be 'TestGroup' } It 'Should allow setting and getting Permissions property' { - $groupPermission = [xAzDoGroupPermission]::new() + $groupPermission = [AzDoGroupPermission]::new() $permissions = @( @{ Permission = 'Read'; Allow = $true }, @{ Permission = 'Write'; Allow = $false } @@ -41,7 +41,7 @@ Describe 'xAzDoGroupPermission Tests' { # Test case for Get method Context 'Get Method' { It 'Should return current state properties' { - $groupPermission = [xAzDoGroupPermission]::new() + $groupPermission = [AzDoGroupPermission]::new() $groupPermission.GroupName = 'TestGroup' $groupPermission.isInherited = $false $groupPermission.Permissions = @( diff --git a/tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 index 1c6863743..c4aeba15a 100644 --- a/tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 @@ -11,7 +11,7 @@ if ($Global:ClassesLoaded -eq $null) . $preInitialize.FullName -RepositoryPath $RepositoryRoot } -Describe 'xAzDoOrganizationGroup' { +Describe 'AzDoOrganizationGroup' { # Mocking AzDevOpsDscResourceBase class since it's not provided Class AzDevOpsDscResourceBase { [void] Construct() {} @@ -19,7 +19,7 @@ Describe 'xAzDoOrganizationGroup' { Context 'Constructor' { It 'should initialize properties correctly when given valid parameters' { - $organizationGroup = [xAzDoOrganizationGroup]::new() + $organizationGroup = [AzDoOrganizationGroup]::new() $organizationGroup.GroupName = "MyGroup" $organizationGroup.GroupDescription = "This is my group." @@ -30,7 +30,7 @@ Describe 'xAzDoOrganizationGroup' { Context 'GetDscResourcePropertyNamesWithNoSetSupport Method' { It 'should return an empty array' { - $organizationGroup = [xAzDoOrganizationGroup]::new() + $organizationGroup = [AzDoOrganizationGroup]::new() $result = $organizationGroup.GetDscResourcePropertyNamesWithNoSetSupport() @@ -40,7 +40,7 @@ Describe 'xAzDoOrganizationGroup' { Context 'GetDscCurrentStateProperties Method' { It 'should return properties with Ensure set to Absent if CurrentResourceObject is null' { - $organizationGroup = [xAzDoOrganizationGroup]::new() + $organizationGroup = [AzDoOrganizationGroup]::new() $result = $organizationGroup.GetDscCurrentStateProperties($null) @@ -48,7 +48,7 @@ Describe 'xAzDoOrganizationGroup' { } It 'should return current state properties from CurrentResourceObject' { - $organizationGroup = [xAzDoOrganizationGroup]::new() + $organizationGroup = [AzDoOrganizationGroup]::new() $currentResourceObject = [PSCustomObject]@{ GroupName = "MyGroup" GroupDescription = "This is my group" diff --git a/tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 b/tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 index 09b125306..721add929 100644 --- a/tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 @@ -11,10 +11,10 @@ if ($Global:ClassesLoaded -eq $null) . $preInitialize.FullName -RepositoryPath $RepositoryRoot } -Describe 'xAzDoProjectGroup' { +Describe 'AzDoProjectGroup' { BeforeAll { # Mock functions that interact with external resources - function Get-xAzDoProjectGroup + function Get-AzDoProjectGroup { param ( [string]$GroupName, @@ -30,7 +30,7 @@ Describe 'xAzDoProjectGroup' { } } - function New-xAzDoProjectGroup + function New-AzDoProjectGroup { param ( [string]$ProjectName, @@ -43,7 +43,7 @@ Describe 'xAzDoProjectGroup' { Write-Output "New project group created: $GroupName in project $ProjectName" } - function Update-xAzDoProjectGroup + function Update-AzDoProjectGroup { param ( [string]$ProjectName, @@ -56,7 +56,7 @@ Describe 'xAzDoProjectGroup' { Write-Output "Project group updated: $GroupName in project $ProjectName" } - function Remove-xAzDoProjectGroup + function Remove-AzDoProjectGroup { param ( [string]$ProjectName, @@ -72,7 +72,7 @@ Describe 'xAzDoProjectGroup' { Context 'When getting the current state of a project group' { It 'Should return the current state properties' { # Arrange - $projectGroup = [xAzDoProjectGroup]::new() + $projectGroup = [AzDoProjectGroup]::new() $projectGroup.ProjectName = "MyProject" $projectGroup.GroupName = "MyGroup" $projectGroup.GroupDescription = "This is my project group." diff --git a/tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 b/tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 index 0f72be87c..2d4371695 100644 --- a/tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 @@ -11,10 +11,10 @@ if ($Global:ClassesLoaded -eq $null) . $preInitialize.FullName -RepositoryPath $RepositoryRoot } -Describe 'xAzDoGroupMember' { +Describe 'AzDoGroupMember' { BeforeAll { # Mock functions that interact with external resources - function Get-xAzDoGroupMember + function Get-AzDoGroupMember { param ( [string]$GroupName, @@ -28,7 +28,7 @@ Describe 'xAzDoGroupMember' { } } - function New-xAzDoGroupMember + function New-AzDoGroupMember { param ( [string]$GroupName, @@ -40,7 +40,7 @@ Describe 'xAzDoGroupMember' { Write-Output "New group member added to: $GroupName" } - function Update-xAzDoGroupMember + function Update-AzDoGroupMember { param ( [string]$GroupName, @@ -52,7 +52,7 @@ Describe 'xAzDoGroupMember' { Write-Output "Group members updated in: $GroupName" } - function Remove-xAzDoGroupMember + function Remove-AzDoGroupMember { param ( [string]$GroupName, @@ -67,7 +67,7 @@ Describe 'xAzDoGroupMember' { Context 'When getting the current state of group members' { It 'Should return the current state properties' { # Arrange - $groupMember = [xAzDoGroupMember]::new() + $groupMember = [AzDoGroupMember]::new() $groupMember.GroupName = "MyGroup" $groupMember.GroupMembers = @("User1", "User2") diff --git a/tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 b/tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 index 0b3ce7efb..780dd63a3 100644 --- a/tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 +++ b/tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 @@ -11,10 +11,10 @@ if ($Global:ClassesLoaded -eq $null) . $preInitialize.FullName -RepositoryPath $RepositoryRoot } -Describe 'xAzDoGitRepository' { +Describe 'AzDoGitRepository' { BeforeAll { # Mock functions that interact with external resources - function Get-xAzDoGitRepository + function Get-AzDoGitRepository { param ( [string]$ProjectName, @@ -29,7 +29,7 @@ Describe 'xAzDoGitRepository' { } } - function New-xAzDoGitRepository + function New-AzDoGitRepository { param ( [string]$ProjectName, @@ -42,7 +42,7 @@ Describe 'xAzDoGitRepository' { Write-Output "New Git repository created: $ProjectName/$GitRepositoryName" } - function Update-xAzDoGitRepository + function Update-AzDoGitRepository { param ( [string]$ProjectName, @@ -55,7 +55,7 @@ Describe 'xAzDoGitRepository' { Write-Output "Git repository updated: $ProjectName/$GitRepositoryName" } - function Remove-xAzDoGitRepository + function Remove-AzDoGitRepository { param ( [string]$ProjectName, @@ -71,7 +71,7 @@ Describe 'xAzDoGitRepository' { Context 'When getting the current state of a Git repository' { It 'Should return the current state properties' { # Arrange - $gitRepository = [xAzDoGitRepository]::new() + $gitRepository = [AzDoGitRepository]::new() $gitRepository.ProjectName = "MyProject" $gitRepository.RepositoryName = "MyRepository" diff --git a/tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 b/tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 index 57e3e6de9..87e01071e 100644 --- a/tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 +++ b/tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 @@ -11,10 +11,10 @@ if ($Global:ClassesLoaded -eq $null) . $preInitialize.FullName -RepositoryPath $RepositoryRoot } -Describe 'xAzDoGitPermission' { +Describe 'AzDoGitPermission' { BeforeAll { # Mock functions that interact with external resources - function Get-xAzDoGitPermission + function Get-AzDoGitPermission { param ( [string]$ProjectName, @@ -30,7 +30,7 @@ Describe 'xAzDoGitPermission' { } } - function New-xAzDoGitPermission + function New-AzDoGitPermission { param ( [string]$ProjectName, @@ -44,7 +44,7 @@ Describe 'xAzDoGitPermission' { Write-Output "New Git permissions set for: $ProjectName/$RepositoryName" } - function Update-xAzDoGitPermission + function Update-AzDoGitPermission { param ( [string]$ProjectName, @@ -58,7 +58,7 @@ Describe 'xAzDoGitPermission' { Write-Output "Git permissions updated for: $ProjectName/$RepositoryName" } - function Remove-xAzDoPermission + function Remove-AzDoPermission { param ( [string]$ProjectName, @@ -74,7 +74,7 @@ Describe 'xAzDoGitPermission' { Context 'When getting the current state of Git permissions' { It 'Should return the current state properties' { # Arrange - $gitPermission = [xAzDoGitPermission]::new() + $gitPermission = [AzDoGitPermission]::new() $gitPermission.ProjectName = "MyProject" $gitPermission.RepositoryName = "MyRepository" diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 index 32e02e967..1da955f64 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 @@ -1,12 +1,12 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Remove-xAzDoPermission' -Tags "Unit", "API" { +Describe 'Remove-AzDoPermission' -Tags "Unit", "API" { BeforeAll { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoPermission.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-AzDoPermission.tests.ps1' } # Load the functions to test @@ -34,7 +34,7 @@ Describe 'Remove-xAzDoPermission' -Tags "Unit", "API" { Mock -CommandName Write-Error # Act - $result = Remove-xAzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -TokenName $TokenName -ApiVersion $ApiVersion + $result = Remove-AzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -TokenName $TokenName -ApiVersion $ApiVersion # Assert $result | Should -BeNullOrEmpty @@ -58,7 +58,7 @@ Describe 'Remove-xAzDoPermission' -Tags "Unit", "API" { Mock -CommandName Write-Error # Act - $result = Remove-xAzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -TokenName $TokenName -ApiVersion $ApiVersion + $result = Remove-AzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -TokenName $TokenName -ApiVersion $ApiVersion $result | Should -BeNullOrEmpty # Assert diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 index 33eab2655..59b01eb65 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 @@ -1,12 +1,12 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Set-xAzDoPermission Tests' -Tags "Unit", "API" { +Describe 'Set-AzDoPermission Tests' -Tags "Unit", "API" { BeforeAll { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoPermission.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-AzDoPermission.tests.ps1' } # Load the functions to test @@ -28,9 +28,9 @@ Describe 'Set-xAzDoPermission Tests' -Tags "Unit", "API" { Context 'When Mandatory Parameters are provided' { It 'Should call Invoke-AzDevOpsApiRestMethod with correct parameters' { - $expectedUri = "https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}" -f $OrganizationName, $SecurityNamespaceID, $ApiVersion + $expectedUri = 'https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}' -f $OrganizationName, $SecurityNamespaceID, $ApiVersion - Set-xAzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -SerializedACLs $SerializedACLs -ApiVersion $ApiVersion + Set-AzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -SerializedACLs $SerializedACLs -ApiVersion $ApiVersion Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly 1 -ParameterFilter { $Uri -eq $expectedUri @@ -42,9 +42,9 @@ Describe 'Set-xAzDoPermission Tests' -Tags "Unit", "API" { Context 'When ApiVersion is not provided' { It 'Should call ExampleFunction and get default ApiVersion' { - $expectedUri = "https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}" -f $OrganizationName, $SecurityNamespaceID, "6.0-preview.1" + $expectedUri = 'https://dev.azure.com/{0}/_apis/accesscontrollists/{1}?api-version={2}' -f $OrganizationName, $SecurityNamespaceID, "6.0-preview.1" - Set-xAzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -SerializedACLs $SerializedACLs + Set-AzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -SerializedACLs $SerializedACLs Assert-MockCalled -CommandName Get-AzDevOpsApiVersion -Exactly 1 Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly 1 -ParameterFilter { @@ -62,7 +62,7 @@ Describe 'Set-xAzDoPermission Tests' -Tags "Unit", "API" { Mock -CommandName Invoke-AzDevOpsApiRestMethod { throw "API call failed" } Mock -CommandName Write-Error - $result = Set-xAzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -SerializedACLs $SerializedACLs -ApiVersion $ApiVersion + $result = Set-AzDoPermission -OrganizationName $OrganizationName -SecurityNamespaceID $SecurityNamespaceID -SerializedACLs $SerializedACLs -ApiVersion $ApiVersion $result | Should -BeNullOrEmpty Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Exactly 1 diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 index 7ab3a9d9c..24b3cab6a 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 @@ -25,7 +25,7 @@ Describe 'Format-AzDoGroup' { [string]$GroupName ) - return "[{0}]\{1}" -f $Prefix.Trim('[]'), $GroupName + return '[{0}]\{1}' -f $Prefix.Trim('[]'), $GroupName } $result = Format-AzDoGroup -Prefix "Contoso" -GroupName "Developers" @@ -39,7 +39,7 @@ Describe 'Format-AzDoGroup' { [string]$GroupName ) - return "[{0}]\{1}" -f $Prefix.Trim('[]'), $GroupName + return '[{0}]\{1}' -f $Prefix.Trim('[]'), $GroupName } $result = Format-AzDoGroup -Prefix "[Contoso]" -GroupName "Developers" @@ -53,7 +53,7 @@ Describe 'Format-AzDoGroup' { [string]$GroupName ) - return "[{0}]\{1}" -f $Prefix.Trim('[]'), $GroupName + return '[{0}]\{1}' -f $Prefix.Trim('[]'), $GroupName } $result = Format-AzDoGroup -Prefix "[Contoso]" -GroupName "Developers" diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.tests.ps1 index 421108c02..076c47ee1 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.tests.ps1 @@ -3,7 +3,7 @@ Describe 'New-Thread' -skip { function New-Thread { [CmdletBinding()] param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [ScriptBlock]$ScriptBlock ) diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 index afafca243..5d160a907 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Get-xAzDoGitPermission Tests' { +Describe 'Get-AzDoGitPermission Tests' { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -12,7 +12,7 @@ Describe 'Get-xAzDoGitPermission Tests' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoGitPermission.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-AzDoGitPermission.tests.ps1' } # Load the functions to test @@ -41,9 +41,9 @@ Describe 'Get-xAzDoGitPermission Tests' { Function Mock-Get-DevOpsACL { param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$OrganizationName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [string]$SecurityDescriptorId ) return @( @{ Token = @{ Type = 'GitRepository'; RepoId = 123 }; Permission = 'Allow' } ) @@ -51,9 +51,9 @@ Describe 'Get-xAzDoGitPermission Tests' { Function Mock-ConvertTo-FormattedACL { param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $SecurityNamespace, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $OrganizationName ) return @( @{ Token = @{ Type = 'GitRepository'; RepoId = 123 }; Permission = 'Allow' } ) @@ -61,15 +61,15 @@ Describe 'Get-xAzDoGitPermission Tests' { Function Mock-ConvertTo-ACL { param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $Permissions, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $SecurityNamespace, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $isInherited, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $OrganizationName, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $TokenName ) return @( @{ Token = @{ Type = 'GitRepository'; RepoId = 123 }; Permission = 'Deny' } ) @@ -77,9 +77,9 @@ Describe 'Get-xAzDoGitPermission Tests' { Function Mock-Test-ACLListforChanges { param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $ReferenceACLs, - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] $DifferenceACLs ) return @{ @@ -104,7 +104,7 @@ Describe 'Get-xAzDoGitPermission Tests' { $isInherited = $true $Permissions = @(@{ 'Permission' = 'Deny' }) - $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + $result = Get-AzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions $result | Should -Not -BeNullOrEmpty $result.status | Should -Be 'Changed' @@ -125,7 +125,7 @@ Describe 'Get-xAzDoGitPermission Tests' { } } - $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + $result = Get-AzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions $result | Should -Not -BeNullOrEmpty $result.status | Should -Be 'Unchanged' @@ -138,7 +138,7 @@ Describe 'Get-xAzDoGitPermission Tests' { $isInherited = $true $Permissions = @(@{ 'Permission' = $null }) - $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + $result = Get-AzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions $result | Should -Not -BeNullOrEmpty $result.status | Should -Be 'Changed' @@ -154,7 +154,7 @@ Describe 'Get-xAzDoGitPermission Tests' { $isInherited = $true $Permissions = @(@{ 'Permission' = 'Allow' }) - $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + $result = Get-AzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions $result | Should -Not -BeNullOrEmpty $result.status | Should -Be 'NotFound' @@ -170,7 +170,7 @@ Describe 'Get-xAzDoGitPermission Tests' { $isInherited = $true $Permissions = @(@{ 'Permission' = 'Allow' }) - $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + $result = Get-AzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions $result | Should -Not -BeNullOrEmpty $result.status | Should -Be 'NotFound' @@ -186,7 +186,7 @@ Describe 'Get-xAzDoGitPermission Tests' { $isInherited = $true $Permissions = @(@{ 'Permission' = 'Allow' }) - $result = Get-xAzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions + $result = Get-AzDoGitPermission -ProjectName $ProjectName -RepositoryName $RepositoryName -isInherited $isInherited -Permissions $Permissions $result | Should -Not -BeNullOrEmpty $result.status | Should -Be 'NotFound' diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 index bf37d325d..f0f46b8cd 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'New-xAzDoGitPermission' { +Describe 'New-AzDoGitPermission' { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -12,7 +12,7 @@ Describe 'New-xAzDoGitPermission' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoGitPermission.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-AzDoGitPermission.tests.ps1' } # Load the functions to test @@ -30,7 +30,7 @@ Describe 'New-xAzDoGitPermission' { Mock -CommandName Get-CacheItem -MockWith { return @{ namespaceId = '12345'; id = '67890' } } Mock -CommandName ConvertTo-ACLHashtable -MockWith { return @{} } - Mock -CommandName Set-xAzDoPermission -MockWith { } + Mock -CommandName Set-AzDoPermission -MockWith { } } Context 'With mandatory parameters provided' { @@ -40,22 +40,22 @@ Describe 'New-xAzDoGitPermission' { RepositoryName = 'TestRepo' isInherited = $true } - New-xAzDoGitPermission @params + New-AzDoGitPermission @params - Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly 1 + Assert-MockCalled -CommandName Set-AzDoPermission -Exactly 1 } - It 'should call ConvertTo-ACLHashtable and Set-xAzDoPermission' { + It 'should call ConvertTo-ACLHashtable and Set-AzDoPermission' { $params = @{ ProjectName = 'TestProject' RepositoryName = 'TestRepo' isInherited = $true LookupResult = @{ propertiesChanged = @{} } } - New-xAzDoGitPermission @params + New-AzDoGitPermission @params Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Exactly 1 - Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly 1 + Assert-MockCalled -CommandName Set-AzDoPermission -Exactly 1 } } @@ -72,16 +72,16 @@ Describe 'New-xAzDoGitPermission' { Ensure = 'Present' Force = $true } - New-xAzDoGitPermission @params + New-AzDoGitPermission @params Assert-MockCalled -CommandName Get-CacheItem -Times 2 Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Exactly 1 - Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly 1 + Assert-MockCalled -CommandName Set-AzDoPermission -Exactly 1 } } Context 'When Get-CacheItem returns nothing' { - It 'should not call ConvertTo-ACLHashtable or Set-xAzDoPermission' { + It 'should not call ConvertTo-ACLHashtable or Set-AzDoPermission' { Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'SecurityNamespaces' } -MockWith { return $null } Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'LiveProjects' } -MockWith { return $null } @@ -92,10 +92,10 @@ Describe 'New-xAzDoGitPermission' { RepositoryName = 'TestRepo' isInherited = $true } - New-xAzDoGitPermission @params + New-AzDoGitPermission @params Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Exactly 0 - Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly 0 + Assert-MockCalled -CommandName Set-AzDoPermission -Exactly 0 Assert-VerifiableMock } } @@ -109,7 +109,7 @@ Describe 'New-xAzDoGitPermission' { isInherited = $true Force = $true } - New-xAzDoGitPermission @params + New-AzDoGitPermission @params # Verify if any additional logic related to -Force was executed # This is a placeholder as the current implementation does not use -Force @@ -127,7 +127,7 @@ Describe 'New-xAzDoGitPermission' { isInherited = $true } - New-xAzDoGitPermission @params + New-AzDoGitPermission @params Assert-VerifiableMock diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 index 07a31994a..74f18f23e 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe "Remove-xAzDoGitPermission" { +Describe "Remove-AzDoGitPermission" { AfterAll { @@ -13,7 +13,7 @@ Describe "Remove-xAzDoGitPermission" { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoGitPermission.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-AzDoGitPermission.tests.ps1' } # Load the functions to test @@ -29,7 +29,7 @@ Describe "Remove-xAzDoGitPermission" { . (Get-ClassFilePath 'Ensure') . (Get-ClassFilePath '002.LocalizedDataAzSerializationPatten') - Mock -CommandName Remove-xAzDoPermission + Mock -CommandName Remove-AzDoPermission Mock -CommandName Get-CacheItem -MockWith { switch ($Type) { @@ -63,9 +63,9 @@ Describe "Remove-xAzDoGitPermission" { Mock -CommandName 'Write-Verbose' -Verifiable - Remove-xAzDoGitPermission @params + Remove-AzDoGitPermission @params - Assert-MockCalled -CommandName Remove-xAzDoPermission -Times 1 + Assert-MockCalled -CommandName Remove-AzDoPermission -Times 1 Assert-VerifiableMock } @@ -80,9 +80,9 @@ Describe "Remove-xAzDoGitPermission" { } } - Remove-xAzDoGitPermission @params + Remove-AzDoGitPermission @params - Assert-MockCalled -CommandName Remove-xAzDoPermission -Exactly 0 + Assert-MockCalled -CommandName Remove-AzDoPermission -Exactly 0 Assert-VerifiableMock } @@ -97,9 +97,9 @@ Describe "Remove-xAzDoGitPermission" { } } - Remove-xAzDoGitPermission @params + Remove-AzDoGitPermission @params - Assert-MockCalled -CommandName Remove-xAzDoPermission -Exactly 0 + Assert-MockCalled -CommandName Remove-AzDoPermission -Exactly 0 Assert-VerifiableMock } @@ -114,9 +114,9 @@ Describe "Remove-xAzDoGitPermission" { } } - Remove-xAzDoGitPermission @params + Remove-AzDoGitPermission @params - Assert-MockCalled -CommandName Remove-xAzDoPermission -Exactly 0 + Assert-MockCalled -CommandName Remove-AzDoPermission -Exactly 0 Assert-VerifiableMock } @@ -131,9 +131,9 @@ Describe "Remove-xAzDoGitPermission" { } } - Remove-xAzDoGitPermission @params + Remove-AzDoGitPermission @params - Assert-MockCalled -CommandName Remove-xAzDoPermission -Exactly 0 + Assert-MockCalled -CommandName Remove-AzDoPermission -Exactly 0 Assert-VerifiableMock } @@ -148,9 +148,9 @@ Describe "Remove-xAzDoGitPermission" { } } - Remove-xAzDoGitPermission @params + Remove-AzDoGitPermission @params - Assert-MockCalled -CommandName Remove-xAzDoPermission -Exactly 0 + Assert-MockCalled -CommandName Remove-AzDoPermission -Exactly 0 Assert-VerifiableMock } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 index 78d50316a..95bbf6c82 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Set-xAzDoGitPermission' { +Describe 'Set-AzDoGitPermission' { AfterAll { @@ -13,7 +13,7 @@ Describe 'Set-xAzDoGitPermission' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoGitPermission.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-AzDoGitPermission.tests.ps1' } # Load the functions to test @@ -31,7 +31,7 @@ Describe 'Set-xAzDoGitPermission' { Mock -CommandName Get-CacheItem -MockWith { return @{ namespaceId = 'SampleNamespaceId' } } Mock -CommandName ConvertTo-ACLHashtable -MockWith { return 'SerializedACLs' } - Mock -CommandName Set-xAzDoPermission + Mock -CommandName Set-AzDoPermission $ProjectName = 'TestProject' $RepositoryName = 'TestRepo' @@ -63,25 +63,25 @@ Describe 'Set-xAzDoGitPermission' { Mock ConvertTo-ACLHashtable -MockWith { return 'SerializedACLs' } - Mock Set-xAzDoPermission -MockWith { + Mock Set-AzDoPermission -MockWith { return $null } } It 'Calls Get-CacheItem with the correct parameters for security namespace' { - Set-xAzDoGitPermission @params + Set-AzDoGitPermission @params Assert-MockCalled Get-CacheItem -Exactly 1 -ParameterFilter { ($Key -eq 'Git Repositories') -and ($Type -eq 'SecurityNamespaces') } } It 'Calls Get-CacheItem with the correct parameters for the project' { - Set-xAzDoGitPermission @params + Set-AzDoGitPermission @params Assert-MockCalled Get-CacheItem -Exactly 1 -ParameterFilter { ($Key -eq $ProjectName) -and ($Type -eq 'LiveProjects') } } - It 'Calls Set-xAzDoPermission with the correct parameters' { - Set-xAzDoGitPermission @params - Assert-MockCalled Set-xAzDoPermission -Exactly 1 -ParameterFilter { + It 'Calls Set-AzDoPermission with the correct parameters' { + Set-AzDoGitPermission @params + Assert-MockCalled Set-AzDoPermission -Exactly 1 -ParameterFilter { ($OrganizationName -eq 'TestOrg') -and ($SecurityNamespaceID -eq 'SampleNamespaceId') -and ($SerializedACLs -eq 'SerializedACLs') @@ -89,7 +89,7 @@ Describe 'Set-xAzDoGitPermission' { } It 'Serializes ACLs using ConvertTo-ACLHashtable with correct parameters' { - Set-xAzDoGitPermission @params + Set-AzDoGitPermission @params Assert-MockCalled ConvertTo-ACLHashtable -Exactly 1 -ParameterFilter { $ReferenceACLs -eq 'someValue' } @@ -100,7 +100,7 @@ Describe 'Set-xAzDoGitPermission' { Mock Get-CacheItem -MockWith { return $null } Mock Write-Error -Verifiable - Set-xAzDoGitPermission @params + Set-AzDoGitPermission @params Assert-VerifiableMock } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 index 8cd3d8829..84ed70bef 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe "Get-xAzDoGitRepository Tests" { +Describe "Get-AzDoGitRepository Tests" { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -12,7 +12,7 @@ Describe "Get-xAzDoGitRepository Tests" { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoGitRepository.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-AzDoGitRepository.tests.ps1' } # Load the functions to test @@ -47,7 +47,7 @@ Describe "Get-xAzDoGitRepository Tests" { $Type -eq 'LiveRepositories' } - $result = Get-xAzDoGitRepository -ProjectName $projectName -RepositoryName $repositoryName + $result = Get-AzDoGitRepository -ProjectName $projectName -RepositoryName $repositoryName $result.status | Should -Be "Unchanged" $result.Ensure | Should -Be "Absent" @@ -67,7 +67,7 @@ Describe "Get-xAzDoGitRepository Tests" { ($Key -eq $projectGroupKey) -and ($Type -eq 'Repositories') } - $result = Get-xAzDoGitRepository -ProjectName $projectName -RepositoryName $repositoryName + $result = Get-AzDoGitRepository -ProjectName $projectName -RepositoryName $repositoryName Assert-MockCalled -CommandName Get-CacheItem -Times 2 -Exactly } @@ -81,7 +81,7 @@ Describe "Get-xAzDoGitRepository Tests" { $Type -eq 'LiveRepositories' } - $result = Get-xAzDoGitRepository -ProjectName $projectName -RepositoryName $repositoryName + $result = Get-AzDoGitRepository -ProjectName $projectName -RepositoryName $repositoryName $result.status | Should -Be "NotFound" $result.Ensure | Should -Be "Absent" diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 index 8278af524..bdc8789fb 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 @@ -1,7 +1,7 @@ $currentFile = $MyInvocation.MyCommand.Path -# Pester tests for New-xAzDoGitRepository function -Describe "New-xAzDoGitRepository Tests" { +# Pester tests for New-AzDoGitRepository function +Describe "New-AzDoGitRepository Tests" { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -13,7 +13,7 @@ Describe "New-xAzDoGitRepository Tests" { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoGitRepository.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-AzDoGitRepository.tests.ps1' } # Load the functions to test @@ -48,7 +48,7 @@ Describe "New-xAzDoGitRepository Tests" { ProjectName = 'TestProject' RepositoryName = 'TestRepo' } - New-xAzDoGitRepository @params + New-AzDoGitRepository @params Assert-MockCalled -CommandName New-GitRepository -Exactly -Times 1 } @@ -58,7 +58,7 @@ Describe "New-xAzDoGitRepository Tests" { ProjectName = 'TestProject' RepositoryName = 'TestRepo' } - New-xAzDoGitRepository @params + New-AzDoGitRepository @params Assert-MockCalled -CommandName Add-CacheItem -Exactly -Times 1 } @@ -68,7 +68,7 @@ Describe "New-xAzDoGitRepository Tests" { ProjectName = 'TestProject' RepositoryName = 'TestRepo' } - New-xAzDoGitRepository @params + New-AzDoGitRepository @params Assert-MockCalled -CommandName Export-CacheObject -Exactly -Times 1 } @@ -78,7 +78,7 @@ Describe "New-xAzDoGitRepository Tests" { ProjectName = 'TestProject' RepositoryName = 'TestRepo' } - New-xAzDoGitRepository @params + New-AzDoGitRepository @params Assert-MockCalled -CommandName Refresh-CacheObject -Exactly -Times 1 } @@ -93,7 +93,7 @@ Describe "New-xAzDoGitRepository Tests" { RepositoryName = 'TestRepo' SourceRepository= 'SourceRepo' } - New-xAzDoGitRepository @params + New-AzDoGitRepository @params Assert-MockCalled -CommandName New-GitRepository -ParameterFilter { $RepositoryName -eq 'TestRepo' -and $SourceRepository -eq 'SourceRepo' } } @@ -104,7 +104,7 @@ Describe "New-xAzDoGitRepository Tests" { RepositoryName = 'TestRepo' Force = $true } - New-xAzDoGitRepository @params + New-AzDoGitRepository @params # Since Force is not used in function logic directly, verifying other aspects Assert-MockCalled -CommandName New-GitRepository -Exactly -Times 1 @@ -126,7 +126,7 @@ Describe "New-xAzDoGitRepository Tests" { ProjectName = 'TestProject' RepositoryName = 'TestRepo' } - New-xAzDoGitRepository @params + New-AzDoGitRepository @params Assert-VerifiableMock Assert-MockCalled -CommandName New-GitRepository -Exactly -Times 0 diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 index 21c6a6398..636cced52 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Remove-xAzDoGitRepository' { +Describe 'Remove-AzDoGitRepository' { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -12,7 +12,7 @@ Describe 'Remove-xAzDoGitRepository' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoGitRepository.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-AzDoGitRepository.tests.ps1' } # Load the functions to test @@ -52,7 +52,7 @@ Describe 'Remove-xAzDoGitRepository' { } It 'Calls Get-CacheItem with appropriate parameters for Project' { - Remove-xAzDoGitRepository @params + Remove-AzDoGitRepository @params Assert-MockCalled -CommandName Get-CacheItem -Times 1 -Exactly -ParameterFilter { $Key -eq "TestProject" -and $Type -eq "LiveProjects" @@ -60,7 +60,7 @@ Describe 'Remove-xAzDoGitRepository' { } It 'Calls Get-CacheItem with appropriate parameters for Repository' { - Remove-xAzDoGitRepository @params + Remove-AzDoGitRepository @params Assert-MockCalled -CommandName Get-CacheItem -Times 1 -Exactly -ParameterFilter { $Key -eq "TestProject\TestRepository" -and $Type -eq "LiveRepositories" @@ -68,12 +68,12 @@ Describe 'Remove-xAzDoGitRepository' { } It 'Calls Remove-GitRepository with appropriate parameters' { - Remove-xAzDoGitRepository @params + Remove-AzDoGitRepository @params Assert-MockCalled -CommandName Remove-GitRepository -Exactly 1 } It 'Calls Remove-CacheItem with appropriate parameters' { - Remove-xAzDoGitRepository @params + Remove-AzDoGitRepository @params Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -Exactly -ParameterFilter { $Key -eq "TestProject\TestRepository" -and $Type -eq "LiveRepositories" @@ -81,7 +81,7 @@ Describe 'Remove-xAzDoGitRepository' { } It 'Calls Export-CacheObject with appropriate parameters' { - Remove-xAzDoGitRepository @params + Remove-AzDoGitRepository @params Assert-MockCalled -CommandName Export-CacheObject -Times 1 -Exactly -ParameterFilter { $CacheType -eq 'LiveRepositories' -and $Content -eq $AzDoLiveRepositories @@ -93,7 +93,7 @@ Describe 'Remove-xAzDoGitRepository' { Mock -CommandName Write-Error -Verifiable Mock -CommandName Get-CacheItem -MockWith { return $null } -ParameterFilter { $Type -eq 'LiveProjects' } - Remove-xAzDoGitRepository @params | Should -BeNullOrEmpty + Remove-AzDoGitRepository @params | Should -BeNullOrEmpty Assert-VerifiableMock } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 index 2e5270e7a..7a4e2b877 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Set-xAzDoGitRepository' -Skip { +Describe 'Set-AzDoGitRepository' -Skip { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -10,7 +10,7 @@ Describe 'Set-xAzDoGitRepository' -Skip { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoGitRepository.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-AzDoGitRepository.tests.ps1' } # Load the functions to test diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 index d3b3454b1..d2c7449b9 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe "Get-xAzDoGroupMember Tests" { +Describe "Get-AzDoGroupMember Tests" { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -12,7 +12,7 @@ Describe "Get-xAzDoGroupMember Tests" { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoGroupMember.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-AzDoGroupMember.tests.ps1' } # Load the functions to test @@ -54,7 +54,7 @@ Describe "Get-xAzDoGroupMember Tests" { Mock -CommandName Get-CacheItem -MockWith { $null } - $result = Get-xAzDoGroupMember @params + $result = Get-AzDoGroupMember @params $result.status | Should -Be ([DSCGetSummaryState]::Unchanged) } @@ -69,7 +69,7 @@ Describe "Get-xAzDoGroupMember Tests" { Mock -CommandName Get-CacheItem -MockWith { $null } - $result = Get-xAzDoGroupMember @params + $result = Get-AzDoGroupMember @params $result.status | Should -Be ([DSCGetSummaryState]::NotFound) } @@ -84,7 +84,7 @@ Describe "Get-xAzDoGroupMember Tests" { Mock -CommandName Get-CacheItem -MockWith { $null } - $result = Get-xAzDoGroupMember @params + $result = Get-AzDoGroupMember @params $result.status | Should -Be ([DSCGetSummaryState]::NotFound) } @@ -97,7 +97,7 @@ Describe "Get-xAzDoGroupMember Tests" { Force = $false } - $result = Get-xAzDoGroupMember @params + $result = Get-AzDoGroupMember @params $result.status | Should -Be ([DSCGetSummaryState]::Missing) } @@ -113,7 +113,7 @@ Describe "Get-xAzDoGroupMember Tests" { Force = $false } - $result = Get-xAzDoGroupMember @params + $result = Get-AzDoGroupMember @params $result.status | Should -Be ([DSCGetSummaryState]::Unchanged) } @@ -128,7 +128,7 @@ Describe "Get-xAzDoGroupMember Tests" { Force = $false } - $result = Get-xAzDoGroupMember @params + $result = Get-AzDoGroupMember @params $result.status | Should -Be ([DSCGetSummaryState]::Changed) $result.propertiesChanged[0].action | Should -Be 'Remove' $result.propertiesChanged[0].value.originId | Should -Be 'MockMember2' @@ -148,7 +148,7 @@ Describe "Get-xAzDoGroupMember Tests" { Force = $true } - $result = Get-xAzDoGroupMember @params + $result = Get-AzDoGroupMember @params $result.status | Should -Be ([DSCGetSummaryState]::Changed) $result.propertiesChanged[0].action | Should -Be 'Remove' $result.propertiesChanged[0].value.originId | Should -Be 'MockMember2' diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 index d2754a81c..d41c9054d 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe "New-xAzDoGroupMember" { +Describe "New-AzDoGroupMember" { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -14,7 +14,7 @@ Describe "New-xAzDoGroupMember" { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoGroupMember.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-AzDoGroupMember.tests.ps1' } # Load the functions to test @@ -60,7 +60,7 @@ Describe "New-xAzDoGroupMember" { $GroupName = 'TestGroup' $GroupMembers = @('User1', 'User2') - New-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers + New-AzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers Assert-MockCalled -CommandName Find-AzDoIdentity -Times 3 -Exactly -Scope It } @@ -69,7 +69,7 @@ Describe "New-xAzDoGroupMember" { $GroupName = 'TestGroup' $GroupMembers = @('User1', 'User2') - New-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers + New-AzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers Assert-MockCalled -CommandName New-DevOpsGroupMember -Times 2 -Exactly -Scope It } @@ -78,7 +78,7 @@ Describe "New-xAzDoGroupMember" { $GroupName = 'TestGroup' $GroupMembers = @('User1', 'User2') - New-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers + New-AzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers Assert-MockCalled -CommandName Add-CacheItem -Times 1 -Exactly -Scope It Assert-MockCalled -CommandName Set-CacheObject -Times 1 -Exactly -Scope It @@ -99,7 +99,7 @@ Describe "New-xAzDoGroupMember" { $GroupName = 'TestGroup' $GroupMembers = @('User1', 'User2') - $result = New-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers + $result = New-AzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers Assert-MockCalled -Times 1 -ParameterFilter { $Message -like "*Unable to find identity for member*" } -CommandName Write-Warning } @@ -109,7 +109,7 @@ Describe "New-xAzDoGroupMember" { $GroupName = 'TestGroup' $GroupMembers = @('User1', 'User2') - New-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers + New-AzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers Assert-MockCalled -CommandName 'Write-Warning' -ParameterFilter { $Message -like "*No group members found*" } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 index cd2f77c41..94733b468 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Remove-xAzDoGroupMember Tests' { +Describe 'Remove-AzDoGroupMember Tests' { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -14,7 +14,7 @@ Describe 'Remove-xAzDoGroupMember Tests' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoGroupMember.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-AzDoGroupMember.tests.ps1' } # Load the functions to test @@ -59,7 +59,7 @@ Describe 'Remove-xAzDoGroupMember Tests' { Context "when functioning correctly" { It 'Should remove group members correctly' { $GroupName = 'TestGroup' - $result = Remove-xAzDoGroupMember -GroupName $GroupName + $result = Remove-AzDoGroupMember -GroupName $GroupName Assert-MockCalled -CommandName Find-AzDoIdentity -Times 1 Assert-MockCalled -CommandName Get-CacheItem -Times 1 @@ -71,7 +71,7 @@ Describe 'Remove-xAzDoGroupMember Tests' { Mock -CommandName Write-Warning $GroupName = 'TestGroup' - $result = Remove-xAzDoGroupMember -GroupName $GroupName + $result = Remove-AzDoGroupMember -GroupName $GroupName Assert-MockCalled -CommandName Write-Warning -ParameterFilter { $Message -like '*No group members found*'} -Exactly 0 Assert-MockCalled -CommandName Remove-CacheItem -Times 1 @@ -86,7 +86,7 @@ Describe 'Remove-xAzDoGroupMember Tests' { Mock -CommandName Write-Warning $GroupName = 'TestGroup' - $result = Remove-xAzDoGroupMember -GroupName $GroupName + $result = Remove-AzDoGroupMember -GroupName $GroupName Assert-MockCalled -CommandName Find-AzDoIdentity -Exactly 1 Assert-MockCalled -CommandName Remove-DevOpsGroupMember -Exactly 0 @@ -105,7 +105,7 @@ Describe 'Remove-xAzDoGroupMember Tests' { Mock -CommandName Write-Warning - $result = Remove-xAzDoGroupMember -GroupName $GroupName + $result = Remove-AzDoGroupMember -GroupName $GroupName Assert-MockCalled -CommandName Write-Warning -ParameterFilter { $Message -like '*Unable to find identity*'} -Exactly 2 Assert-MockCalled -CommandName Remove-DevOpsGroupMember -Exactly 0 @@ -125,7 +125,7 @@ Describe 'Remove-xAzDoGroupMember Tests' { Mock -CommandName Write-Warning $GroupName = 'TestGroup' - $result = Remove-xAzDoGroupMember -GroupName $GroupName + $result = Remove-AzDoGroupMember -GroupName $GroupName Assert-MockCalled -CommandName Write-Warning -ParameterFilter { $Message -like '*Unable to find identity*'} -Exactly 1 Assert-MockCalled -CommandName Remove-DevOpsGroupMember -Exactly 0 diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 index e18444f1c..6d3c4aef4 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Set-xAzDoGroupMember' { +Describe 'Set-AzDoGroupMember' { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -14,7 +14,7 @@ Describe 'Set-xAzDoGroupMember' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoGroupMember.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-AzDoGroupMember.tests.ps1' } # Load the functions to test @@ -69,7 +69,7 @@ Describe 'Set-xAzDoGroupMember' { It 'Should call New-DevOpsGroupMember with correct parameters' { - Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + Set-AzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' $params = @{ GroupIdentity = @{ @@ -89,7 +89,7 @@ Describe 'Set-xAzDoGroupMember' { } It 'Should call Remove-DevOpsGroupMember with correct parameters' { - Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + Set-AzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' $params = @{ GroupIdentity = @{ @@ -109,7 +109,7 @@ Describe 'Set-xAzDoGroupMember' { } it "should add and remove members" { - Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + Set-AzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' $params = @{ GroupIdentity = @{ @@ -160,7 +160,7 @@ Describe 'Set-xAzDoGroupMember' { ) } - Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + Set-AzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' Assert-MockCalled -CommandName 'New-DevOpsGroupMember' -Exactly 0 Assert-MockCalled -CommandName 'Remove-DevOpsGroupMember' -Exactly 0 @@ -179,7 +179,7 @@ Describe 'Set-xAzDoGroupMember' { ) } - Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + Set-AzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' Assert-MockCalled -CommandName 'New-DevOpsGroupMember' -Exactly 0 Assert-MockCalled -CommandName 'Remove-DevOpsGroupMember' -Exactly 0 @@ -196,7 +196,7 @@ Describe 'Set-xAzDoGroupMember' { Mock -CommandName Get-CacheItem -ParameterFilter { $Type -eq 'LiveGroupMembers' } Mock -CommandName Write-Error - Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + Set-AzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' Assert-MockCalled -CommandName 'Write-Error' -Exactly 1 -ParameterFilter { $Message -like '*LiveGroupMembers cache for group*' } Assert-MockCalled -CommandName 'New-DevOpsGroupMember' -Exactly 0 @@ -208,7 +208,7 @@ Describe 'Set-xAzDoGroupMember' { Mock -CommandName New-DevOpsGroupMember -MockWith { return $null } - Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + Set-AzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' Assert-MockCalled -CommandName 'New-DevOpsGroupMember' -Exactly 1 @@ -218,7 +218,7 @@ Describe 'Set-xAzDoGroupMember' { Mock -CommandName Remove-DevOpsGroupMember -MockWith { return $null } - Set-xAzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' + Set-AzDoGroupMember -GroupName 'TestGroup' -LookupResult $LookupResult -Ensure 'Present' Assert-MockCalled -CommandName 'Remove-DevOpsGroupMember' -Exactly 1 diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 index f314f3cd7..c02e5dee8 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Test-xAzDoGroupMember' -skip { +Describe 'Test-AzDoGroupMember' -skip { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -14,7 +14,7 @@ Describe 'Test-xAzDoGroupMember' -skip { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-xAzDoGroupMember.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-AzDoGroupMember.tests.ps1' } # Load the functions to test @@ -38,42 +38,42 @@ Describe 'Test-xAzDoGroupMember' -skip { } It 'Should accept mandatory GroupName parameter' { - { Test-xAzDoGroupMember -GroupName $GroupName } | Should -Not -Throw + { Test-AzDoGroupMember -GroupName $GroupName } | Should -Not -Throw } It 'Should accept optional GroupMembers parameter' { - { Test-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers } | Should -Not -Throw + { Test-AzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers } | Should -Not -Throw } It 'Should accept optional LookupResult parameter' { - { Test-xAzDoGroupMember -GroupName $GroupName -LookupResult $LookupResult } | Should -Not -Throw + { Test-AzDoGroupMember -GroupName $GroupName -LookupResult $LookupResult } | Should -Not -Throw } It 'Should accept optional Ensure parameter' { - { Test-xAzDoGroupMember -GroupName $GroupName -Ensure $Ensure } | Should -Not -Throw + { Test-AzDoGroupMember -GroupName $GroupName -Ensure $Ensure } | Should -Not -Throw } It 'Should accept Force switch parameter' { - { Test-xAzDoGroupMember -GroupName $GroupName -Force } | Should -Not -Throw - { Test-xAzDoGroupMember -GroupName $GroupName -Force:$false } | Should -Not -Throw + { Test-AzDoGroupMember -GroupName $GroupName -Force } | Should -Not -Throw + { Test-AzDoGroupMember -GroupName $GroupName -Force:$false } | Should -Not -Throw } It 'Should fail without mandatory GroupName parameter' { - { Test-xAzDoGroupMember } | Should -Throw + { Test-AzDoGroupMember } | Should -Throw } It 'Should handle null or empty GroupMembers parameter' { - { Test-xAzDoGroupMember -GroupName $GroupName -GroupMembers $null } | Should -Not -Throw - { Test-xAzDoGroupMember -GroupName $GroupName -GroupMembers @() } | Should -Not -Throw + { Test-AzDoGroupMember -GroupName $GroupName -GroupMembers $null } | Should -Not -Throw + { Test-AzDoGroupMember -GroupName $GroupName -GroupMembers @() } | Should -Not -Throw } It 'Should handle null or empty LookupResult parameter' { - { Test-xAzDoGroupMember -GroupName $GroupName -LookupResult $null } | Should -Not -Throw - { Test-xAzDoGroupMember -GroupName $GroupName -LookupResult @{} } | Should -Not -Throw + { Test-AzDoGroupMember -GroupName $GroupName -LookupResult $null } | Should -Not -Throw + { Test-AzDoGroupMember -GroupName $GroupName -LookupResult @{} } | Should -Not -Throw } It 'Should return the expected result' { - $result = Test-xAzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers -LookupResult $LookupResult -Ensure $Ensure -Force + $result = Test-AzDoGroupMember -GroupName $GroupName -GroupMembers $GroupMembers -LookupResult $LookupResult -Ensure $Ensure -Force $result | Should -Be $return } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 index 35f6b3d9a..8b041c844 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 @@ -2,7 +2,7 @@ $currentFile = $MyInvocation.MyCommand.Path # Tests are currently disabled. -Describe 'Get-xAzDoGroupPermission' -skip { +Describe 'Get-AzDoGroupPermission' -skip { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -14,7 +14,7 @@ Describe 'Get-xAzDoGroupPermission' -skip { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoGroupMember.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-AzDoGroupMember.tests.ps1' } # Load the functions to test @@ -106,7 +106,7 @@ Describe 'Get-xAzDoGroupPermission' -skip { It 'Should return group result with correct properties when valid GroupName is provided' { - $result = Get-xAzDoGroupPermission -GroupName 'Project\Group' -isInherited $true + $result = Get-AzDoGroupPermission -GroupName 'Project\Group' -isInherited $true $result | Should -Not -BeNullOrEmpty $result.project | Should -Be 'Project' @@ -117,7 +117,7 @@ Describe 'Get-xAzDoGroupPermission' -skip { } It 'Should not throw an error when GroupName is invalid' { - $result = Get-xAzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true + $result = Get-AzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true $result | Should -BeNullOrEmpty } @@ -131,7 +131,7 @@ Describe 'Get-xAzDoGroupPermission' -skip { } } - $result = Get-xAzDoGroupPermission -GroupName 'Project\Group' -isInherited $true + $result = Get-AzDoGroupPermission -GroupName 'Project\Group' -isInherited $true $result | Should -BeNullOrEmpty } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 index 87e187989..140a162b8 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 @@ -1,7 +1,7 @@ $currentFile = $MyInvocation.MyCommand.Path # Resource is currently disabled -Describe 'New-xAzDoGroupPermission' -skip { +Describe 'New-AzDoGroupPermission' -skip { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -13,7 +13,7 @@ Describe 'New-xAzDoGroupPermission' -skip { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoGroupMember.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-AzDoGroupMember.tests.ps1' } # Load the functions to test @@ -60,7 +60,7 @@ Describe 'New-xAzDoGroupPermission' -skip { } } - Mock -CommandName Set-xAzDoPermission + Mock -CommandName Set-AzDoPermission } @@ -75,32 +75,32 @@ Describe 'New-xAzDoGroupPermission' -skip { } ) - New-xAzDoGroupPermission -GroupName 'Project\Group' -isInherited $true -Permissions $Permissions -LookupResult $LookupResult -Ensure 'Present' -Force:$true + New-AzDoGroupPermission -GroupName 'Project\Group' -isInherited $true -Permissions $Permissions -LookupResult $LookupResult -Ensure 'Present' -Force:$true Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Identity'; Type = 'SecurityNamespaces' } -Times 1 Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Project'; Type = 'LiveProjects' } -Times 1 Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = '[Project]\Group'; Type = 'LiveGroups' } -Times 1 Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'mockNamespaceId'; Type = 'LiveACLList' } -Times 1 Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Times 1 - Assert-MockCalled -CommandName Set-xAzDoPermission -Times 1 + Assert-MockCalled -CommandName Set-AzDoPermission -Times 1 } It 'Should throw a warning when GroupName is invalid' { - { New-xAzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true } | Should -Throw + { New-AzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true } | Should -Throw } It 'Should handle case where no LookupResult is provided' { - $result = New-xAzDoGroupPermission -GroupName 'Project\Group' -isInherited $true -Ensure 'Present' -Force:$true + $result = New-AzDoGroupPermission -GroupName 'Project\Group' -isInherited $true -Ensure 'Present' -Force:$true Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Identity'; Type = 'SecurityNamespaces' } -Times 1 Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Project'; Type = 'LiveProjects' } -Times 1 Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = '[Project]\Group'; Type = 'LiveGroups' } -Times 1 Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'mockNamespaceId'; Type = 'LiveACLList' } -Times 1 Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Times 1 - Assert-MockCalled -CommandName Set-xAzDoPermission -Times 1 + Assert-MockCalled -CommandName Set-AzDoPermission -Times 1 } - It 'Should not call Set-xAzDoPermission if no ACLs are found' { + It 'Should not call Set-AzDoPermission if no ACLs are found' { Mock -CommandName ConvertTo-ACLHashtable -MockWith { return @{ aces = @{ @@ -113,8 +113,8 @@ Describe 'New-xAzDoGroupPermission' -skip { propertiesChanged = @('property1', 'property2') } - New-xAzDoGroupPermission -GroupName 'Project\Group' -isInherited $true -LookupResult $LookupResult -Ensure 'Present' -Force:$true + New-AzDoGroupPermission -GroupName 'Project\Group' -isInherited $true -LookupResult $LookupResult -Ensure 'Present' -Force:$true - Assert-MockCalled -CommandName Set-xAzDoPermission -Times 0 + Assert-MockCalled -CommandName Set-AzDoPermission -Times 0 } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 index 329b1c998..44ed26f54 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 @@ -1,7 +1,7 @@ $currentFile = $MyInvocation.MyCommand.Path # Tests are currently disabled. -Describe 'Remove-xAzDoGroupPermission' -skip { +Describe 'Remove-AzDoGroupPermission' -skip { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -13,7 +13,7 @@ Describe 'Remove-xAzDoGroupPermission' -skip { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoGroupPermission.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-AzDoGroupPermission.tests.ps1' } # Load the functions to test @@ -44,22 +44,22 @@ Describe 'Remove-xAzDoGroupPermission' -skip { } } - Mock -CommandName Remove-xAzDoPermission -MockWith {} + Mock -CommandName Remove-AzDoPermission -MockWith {} } It 'Should remove permissions when valid GroupName is provided' { - Remove-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Ensure 'Present' -Force:$true + Remove-AzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Ensure 'Present' -Force:$true Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Identity'; Type = 'SecurityNamespaces' } -Times 1 Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Project'; Type = 'LiveProjects' } -Times 1 Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'Project\Repository'; Type = 'LiveRepositories' } -Times 1 Assert-MockCalled -CommandName Get-CacheItem -Parameters @{ Key = 'mockNamespaceId'; Type = 'LiveACLList' } -Times 1 - Assert-MockCalled -CommandName Remove-xAzDoPermission -Times 1 + Assert-MockCalled -CommandName Remove-AzDoPermission -Times 1 } It 'Should throw a warning when GroupName is invalid' { - { Remove-xAzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true } | Should -Throw + { Remove-AzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true } | Should -Throw } It 'Should handle case where no matching ACLs are found' { @@ -76,12 +76,12 @@ Describe 'Remove-xAzDoGroupPermission' -skip { } } - Remove-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Ensure 'Present' -Force:$true + Remove-AzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Ensure 'Present' -Force:$true - Assert-MockCalled -CommandName Remove-xAzDoPermission -Times 0 + Assert-MockCalled -CommandName Remove-AzDoPermission -Times 0 } - It 'Should not call Remove-xAzDoPermission if no ACLs are found' { + It 'Should not call Remove-AzDoPermission if no ACLs are found' { Mock -CommandName Get-CacheItem -MockWith { param ($Key, $Type) if ($Type -eq 'LiveACLList') { @@ -93,8 +93,8 @@ Describe 'Remove-xAzDoGroupPermission' -skip { } } - Remove-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Ensure 'Present' -Force:$true + Remove-AzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Ensure 'Present' -Force:$true - Assert-MockCalled -CommandName Remove-xAzDoPermission -Times 0 + Assert-MockCalled -CommandName Remove-AzDoPermission -Times 0 } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 index 6ccad77fd..f0fc5316d 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 @@ -1,7 +1,7 @@ $currentFile = $MyInvocation.MyCommand.Path # Tests are currently disabled. -Describe 'Set-xAzDoGroupPermission' -skip { +Describe 'Set-AzDoGroupPermission' -skip { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -13,7 +13,7 @@ Describe 'Set-xAzDoGroupPermission' -skip { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoGroupPermission.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-AzDoGroupPermission.tests.ps1' } # Load the functions to test @@ -64,12 +64,12 @@ Describe 'Set-xAzDoGroupPermission' -skip { return @{ serializedACLs = 'mockSerializedACLs' } } - Mock -CommandName Set-xAzDoPermission + Mock -CommandName Set-AzDoPermission } It 'Should throw a warning when GroupName is invalid' { - { Set-xAzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true } | Should -Throw + { Set-AzDoGroupPermission -GroupName 'InvalidGroupName' -isInherited $true } | Should -Throw } It 'Should set permissions when valid GroupName is provided' { @@ -77,7 +77,7 @@ Describe 'Set-xAzDoGroupPermission' -skip { propertiesChanged = @{} } - Set-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Permissions @{} -LookupResult $LookupResult -Ensure 'Present' -Force:$true + Set-AzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Permissions @{} -LookupResult $LookupResult -Ensure 'Present' -Force:$true Assert-MockCalled -CommandName Get-CacheItem -Exactly -Times 1 -Scope It -ParameterFilter { $Key -eq 'Identity' -and $Type -eq 'SecurityNamespaces' @@ -86,7 +86,7 @@ Describe 'Set-xAzDoGroupPermission' -skip { $Key -eq $ProjectName -and $Type -eq 'LiveProjects' } Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Exactly -Times 1 -Scope It - Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Set-AzDoPermission -Exactly -Times 1 -Scope It } It 'Should call ConvertTo-ACLHashtable with correct parameters' { @@ -94,7 +94,7 @@ Describe 'Set-xAzDoGroupPermission' -skip { propertiesChanged = @{} } - Set-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Permissions @{} -LookupResult $LookupResult -Ensure 'Present' -Force:$true + Set-AzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Permissions @{} -LookupResult $LookupResult -Ensure 'Present' -Force:$true Assert-MockCalled -CommandName ConvertTo-ACLHashtable -Exactly -Times 1 -Scope It -ParameterFilter { $ReferenceACLs -eq $LookupResult.propertiesChanged -and @@ -103,7 +103,7 @@ Describe 'Set-xAzDoGroupPermission' -skip { } } - It 'Should not call Set-xAzDoPermission if no ACLs are found' { + It 'Should not call Set-AzDoPermission if no ACLs are found' { Mock -CommandName Get-CacheItem -MockWith { param ( [string]$Key, @@ -122,8 +122,8 @@ Describe 'Set-xAzDoGroupPermission' -skip { propertiesChanged = @{} } - Set-xAzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Permissions @{} -LookupResult $LookupResult -Ensure 'Present' -Force:$true + Set-AzDoGroupPermission -GroupName 'Project\Repository' -isInherited $true -Permissions @{} -LookupResult $LookupResult -Ensure 'Present' -Force:$true - Assert-MockCalled -CommandName Set-xAzDoPermission -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-AzDoPermission -Exactly -Times 0 -Scope It } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 index 3022f284b..6215cb1a0 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Get-xAzDoOrganizationGroup' { +Describe 'Get-AzDoOrganizationGroup' { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -12,7 +12,7 @@ Describe 'Get-xAzDoOrganizationGroup' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoOrganizationGroup.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-AzDoOrganizationGroup.tests.ps1' } # Load the functions to test @@ -52,13 +52,13 @@ Describe 'Get-xAzDoOrganizationGroup' { } It 'should return unchanged status if properties match' { - $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' + $result = Get-AzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' $result.status | Should -Be 'Unchanged' $result.Ensure | Should -Be 'Present' } It 'should return changed status if properties differ' { - $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'New Description' + $result = Get-AzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'New Description' $result.status | Should -Be 'Changed' $result.propertiesChanged | Should -Contain 'Description' } @@ -75,7 +75,7 @@ Describe 'Get-xAzDoOrganizationGroup' { } It 'should detect renamed group' { - $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' + $result = Get-AzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' $result.status | Should -Be 'Changed' $result.propertiesChanged[0] | Should -Be 'Name' } @@ -92,7 +92,7 @@ Describe 'Get-xAzDoOrganizationGroup' { } It 'should return not found status' { - $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' + $result = Get-AzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' $result.status | Should -Be 'NotFound' $result.propertiesChanged | Should -Be @('description', 'displayName') } @@ -109,12 +109,12 @@ Describe 'Get-xAzDoOrganizationGroup' { } It 'should return changed' { - $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' + $result = Get-AzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' $result.status | Should -Be 'Changed' } It 'should return changed if properties differ' { - $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'New Description' + $result = Get-AzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'New Description' $result.status | Should -Be 'Changed' $result.propertiesChanged | Should -Contain 'description' } @@ -131,7 +131,7 @@ Describe 'Get-xAzDoOrganizationGroup' { } It 'should return not found status' { - $result = Get-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' + $result = Get-AzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Group' $result.status | Should -Be 'NotFound' $result.propertiesChanged | Should -Be @('description', 'displayName') } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 index f21e740c3..bdb4c46c3 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'New-xAzDoOrganizationGroup' { +Describe 'New-AzDoOrganizationGroup' { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -12,7 +12,7 @@ Describe 'New-xAzDoOrganizationGroup' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoOrganizationGroup.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-AzDoOrganizationGroup.tests.ps1' } # Load the functions to test @@ -27,7 +27,7 @@ Describe 'New-xAzDoOrganizationGroup' { . (Get-ClassFilePath '000.CacheItem') . (Get-ClassFilePath 'Ensure') - # Mock the external functions used within New-xAzDoOrganizationGroup + # Mock the external functions used within New-AzDoOrganizationGroup Mock -CommandName New-DevOpsGroup -MockWith { return @{ principalName = "testPrincipalName" @@ -47,7 +47,7 @@ Describe 'New-xAzDoOrganizationGroup' { GroupDescription = 'Test Description' } - $result = New-xAzDoOrganizationGroup @params + $result = New-AzDoOrganizationGroup @params Assert-MockCalled -CommandName New-DevOpsGroup -Exactly -Times 1 Assert-MockCalled -CommandName Refresh-CacheIdentity -Exactly -Times 1 @@ -67,7 +67,7 @@ Describe 'New-xAzDoOrganizationGroup' { Verbose = $true } - $verboseOutput = New-xAzDoOrganizationGroup @params + $verboseOutput = New-AzDoOrganizationGroup @params Assert-MockCalled -CommandName Write-Verbose -ParameterFilter { $Message -like "*Creating a new DevOps group with GroupName: 'TestGroup', GroupDescription: 'Test Description'*" } Assert-MockCalled -CommandName Write-Verbose -ParameterFilter { $Message -like "*Updated global AzDoGroup cache object.*" } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 index d23c13c32..947eabc1f 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Remove-xAzDoOrganizationGroup' { +Describe 'Remove-AzDoOrganizationGroup' { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -12,7 +12,7 @@ Describe 'Remove-xAzDoOrganizationGroup' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoOrganizationGroup.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-AzDoOrganizationGroup.tests.ps1' } # Load the functions to test @@ -27,7 +27,7 @@ Describe 'Remove-xAzDoOrganizationGroup' { . (Get-ClassFilePath '000.CacheItem') . (Get-ClassFilePath 'Ensure') - # Mock the external functions used within Remove-xAzDoOrganizationGroup + # Mock the external functions used within Remove-AzDoOrganizationGroup Mock -CommandName Remove-DevOpsGroup Mock -CommandName Remove-CacheItem Mock -CommandName Set-CacheObject @@ -48,7 +48,7 @@ Describe 'Remove-xAzDoOrganizationGroup' { localCache = $null } - Remove-xAzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult + Remove-AzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult Assert-MockCalled -CommandName Remove-DevOpsGroup -Times 0 Assert-MockCalled -CommandName Remove-CacheItem -Times 0 @@ -66,7 +66,7 @@ Describe 'Remove-xAzDoOrganizationGroup' { localCache = $null } - Remove-xAzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult + Remove-AzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult Assert-MockCalled -CommandName Remove-DevOpsGroup -Exactly -Times 1 -ParameterFilter { $GroupDescriptor -eq 'LiveDescriptor' -and @@ -88,7 +88,7 @@ Describe 'Remove-xAzDoOrganizationGroup' { } } - Remove-xAzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult + Remove-AzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult Assert-MockCalled -CommandName Remove-DevOpsGroup -Exactly -Times 1 -ParameterFilter { $GroupDescriptor -eq 'LocalDescriptor' -and @@ -113,7 +113,7 @@ Describe 'Remove-xAzDoOrganizationGroup' { } } - Remove-xAzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult + Remove-AzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult Assert-MockCalled -CommandName Remove-DevOpsGroup -Exactly -Times 1 -ParameterFilter { $GroupDescriptor -eq 'LiveDescriptor' -and diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 index 33ce9a8cf..35ab3a1da 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Set-xAzDoOrganizationGroup' { +Describe 'Set-AzDoOrganizationGroup' { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -10,7 +10,7 @@ Describe 'Set-xAzDoOrganizationGroup' { BeforeAll { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoOrganizationGroup.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-AzDoOrganizationGroup.tests.ps1' } # Load the functions to test @@ -25,7 +25,7 @@ Describe 'Set-xAzDoOrganizationGroup' { . (Get-ClassFilePath '000.CacheItem') . (Get-ClassFilePath 'Ensure') - # Mock the external functions used within Set-xAzDoOrganizationGroup + # Mock the external functions used within Set-AzDoOrganizationGroup Mock -CommandName Set-DevOpsGroup -MockWith { return @{ principalName = 'testPrincipalName' @@ -58,7 +58,7 @@ Describe 'Set-xAzDoOrganizationGroup' { } } - $result = Set-xAzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult + $result = Set-AzDoOrganizationGroup -GroupName 'TestGroup' -LookupResult $lookupResult $result | Should -BeNullOrEmpty Assert-MockCalled -CommandName Set-DevOpsGroup -Times 0 -Scope It @@ -81,7 +81,7 @@ Describe 'Set-xAzDoOrganizationGroup' { } } - $result = Set-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Description' -LookupResult $lookupResult + $result = Set-AzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Description' -LookupResult $lookupResult $result | Should -Not -BeNullOrEmpty $result.principalName | Should -Be 'testPrincipalName' @@ -124,7 +124,7 @@ Describe 'Set-xAzDoOrganizationGroup' { localCache = $null } - $result = Set-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Description' -LookupResult $lookupResult + $result = Set-AzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Description' -LookupResult $lookupResult $result | Should -Not -BeNullOrEmpty $result.principalName | Should -Be 'testPrincipalName' @@ -169,7 +169,7 @@ Describe 'Set-xAzDoOrganizationGroup' { localCache = $null } - { Set-xAzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Description' -LookupResult $lookupResult } | Should -Throw 'An error occurred' + { Set-AzDoOrganizationGroup -GroupName 'TestGroup' -GroupDescription 'Test Description' -LookupResult $lookupResult } | Should -Throw 'An error occurred' } } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 index 00734e148..279a4c38e 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 @@ -2,12 +2,12 @@ $currentFile = $MyInvocation.MyCommand.Path # Pester tests # Not required to run in the pipeline -Describe "Test-xAzDoOrganizationGroup" -skip { +Describe "Test-AzDoOrganizationGroup" -skip { BeforeAll { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-xAzDoOrganizationGroup.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-AzDoOrganizationGroup.tests.ps1' } # Load the functions to test @@ -17,15 +17,15 @@ Describe "Test-xAzDoOrganizationGroup" -skip { . $file.FullName } - # Mock the external functions used within Test-xAzDoOrganizationGroup - Mock -CommandName Test-xAzDoOrganizationGroup + # Mock the external functions used within Test-AzDoOrganizationGroup + Mock -CommandName Test-AzDoOrganizationGroup } Context "when the group exists" { It "should return true" { - # Mock Test-xAzDoOrganizationGroup function to simulate group existence - Mock -CommandName Test-xAzDoOrganizationGroup -MockWith { + # Mock Test-AzDoOrganizationGroup function to simulate group existence + Mock -CommandName Test-AzDoOrganizationGroup -MockWith { param ( [string]$GroupName, [string]$Pat, @@ -34,15 +34,15 @@ Describe "Test-xAzDoOrganizationGroup" -skip { return $true } - $result = Test-xAzDoOrganizationGroup -GroupName 'ExistingGroup' -Pat 'dummyPat' -ApiUri 'https://dev.azure.com/myorg' + $result = Test-AzDoOrganizationGroup -GroupName 'ExistingGroup' -Pat 'dummyPat' -ApiUri 'https://dev.azure.com/myorg' $result | Should -Be $true } } Context "when the group does not exist" { It "should return false" { - # Mock Test-xAzDoOrganizationGroup function to simulate group non-existence - Mock -CommandName Test-xAzDoOrganizationGroup -MockWith { + # Mock Test-AzDoOrganizationGroup function to simulate group non-existence + Mock -CommandName Test-AzDoOrganizationGroup -MockWith { param ( [string]$GroupName, [string]$Pat, @@ -51,26 +51,26 @@ Describe "Test-xAzDoOrganizationGroup" -skip { return $false } - $result = Test-xAzDoOrganizationGroup -GroupName 'NonExistentGroup' -Pat 'dummyPat' -ApiUri 'https://dev.azure.com/myorg' + $result = Test-AzDoOrganizationGroup -GroupName 'NonExistentGroup' -Pat 'dummyPat' -ApiUri 'https://dev.azure.com/myorg' $result | Should -Be $false } } Context "when there is an empty GroupName parameter" { It "should throw an error" { - { Test-xAzDoOrganizationGroup -GroupName '' -Pat 'dummyPat' -ApiUri 'https://dev.azure.com/myorg' } | Should -Throw + { Test-AzDoOrganizationGroup -GroupName '' -Pat 'dummyPat' -ApiUri 'https://dev.azure.com/myorg' } | Should -Throw } } Context "when there is an empty Pat parameter" { It "should throw an error" { - { Test-xAzDoOrganizationGroup -GroupName 'ExistingGroup' -Pat '' -ApiUri 'https://dev.azure.com/myorg' } | Should -Throw + { Test-AzDoOrganizationGroup -GroupName 'ExistingGroup' -Pat '' -ApiUri 'https://dev.azure.com/myorg' } | Should -Throw } } Context "when there is an empty ApiUri parameter" { It "should throw an error" { - { Test-xAzDoOrganizationGroup -GroupName 'ExistingGroup' -Pat 'dummyPat' -ApiUri '' } | Should -Throw + { Test-AzDoOrganizationGroup -GroupName 'ExistingGroup' -Pat 'dummyPat' -ApiUri '' } | Should -Throw } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 index 927a4fd26..959342529 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 @@ -1,7 +1,7 @@ $currentFile = $MyInvocation.MyCommand.Path -# Pester tests for Get-xAzDoProject +# Pester tests for Get-AzDoProject -Describe "Get-xAzDoProject" { +Describe "Get-AzDoProject" { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -14,7 +14,7 @@ Describe "Get-xAzDoProject" { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoProject.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-AzDoProject.tests.ps1' } # Load the functions to test @@ -54,20 +54,20 @@ Describe "Get-xAzDoProject" { } It "should return the project details with status unchanged" { - $result = Get-xAzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' + $result = Get-AzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' $result.Status | Should -BeNullOrEmpty $result.ProjectName | Should -Be 'ExistingProject' $result.ProjectDescription | Should -Be 'ExistingDescription' } It "should return status changed when descriptions differ" { - $result = Get-xAzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'NewDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' + $result = Get-AzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'NewDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' $result.Status | Should -Be 'Changed' $result.propertiesChanged | Should -Contain 'Description' } It "should return status changed when visibility differs" { - $result = Get-xAzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Public' + $result = Get-AzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Public' $result.Status | Should -Be 'Changed' $result.propertiesChanged | Should -Contain 'Visibility' } @@ -80,7 +80,7 @@ Describe "Get-xAzDoProject" { } It "should return status NotFound" { - $result = Get-xAzDoProject -ProjectName 'NonExistentProject' -ProjectDescription 'AnyDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' + $result = Get-AzDoProject -ProjectName 'NonExistentProject' -ProjectDescription 'AnyDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' $result.Status | Should -Be 'NotFound' } } @@ -93,7 +93,7 @@ Describe "Get-xAzDoProject" { } It "should throw an error" { - { Get-xAzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'NonExistentTemplate' -Visibility 'Private' } | Should -Throw + { Get-AzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'NonExistentTemplate' -Visibility 'Private' } | Should -Throw } } @@ -108,7 +108,7 @@ Describe "Get-xAzDoProject" { } It "should warn about source control type conflict" { - $result = Get-xAzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' + $result = Get-AzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' $result.Status | Should -BeNullOrEmpty $result.ProjectName | Should -Be 'ExistingProject' $result.SourceControlType | Should -Be 'Git' diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 index a9c7fd4ac..505868649 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 @@ -1,7 +1,7 @@ $currentFile = $MyInvocation.MyCommand.Path -# Pester tests for New-xAzDoProject +# Pester tests for New-AzDoProject -Describe "New-xAzDoProject" { +Describe "New-AzDoProject" { AfterAll { @@ -15,7 +15,7 @@ Describe "New-xAzDoProject" { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoProject.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-AzDoProject.tests.ps1' } # Load the functions to test @@ -61,7 +61,7 @@ Describe "New-xAzDoProject" { } It "should create a new project with specified parameters" { - New-xAzDoProject -ProjectName 'NewProject' -ProjectDescription 'New Project Description' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' + New-AzDoProject -ProjectName 'NewProject' -ProjectDescription 'New Project Description' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' # Validate that Get-CacheItem was called with correct parameters Assert-MockCalled -CommandName Get-CacheItem -Exactly 1 -ParameterFilter { $Key -eq 'Agile' -and $Type -eq 'LiveProcesses' } @@ -96,7 +96,7 @@ Describe "New-xAzDoProject" { } It "should throw an error if process template is not found" { - { New-xAzDoProject -ProjectName 'NewProject' -ProjectDescription 'New Project Description' -SourceControlType 'Git' -ProcessTemplate 'NonExistentTemplate' -Visibility 'Private' } | Should -Throw + { New-AzDoProject -ProjectName 'NewProject' -ProjectDescription 'New Project Description' -SourceControlType 'Git' -ProcessTemplate 'NonExistentTemplate' -Visibility 'Private' } | Should -Throw } } @@ -116,7 +116,7 @@ Describe "New-xAzDoProject" { } It "should create a new project even if it already exists when -Force is used" { - New-xAzDoProject -ProjectName 'NewProject' -ProjectDescription 'New Project Description' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' -Force + New-AzDoProject -ProjectName 'NewProject' -ProjectDescription 'New Project Description' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' -Force # Validate that New-DevOpsProject was called with correct parameters Assert-MockCalled -CommandName New-DevOpsProject -Exactly 1 -ParameterFilter { diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 index e8771863c..6a597bf45 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe "Remove-xAzDoProject" { +Describe "Remove-AzDoProject" { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -13,7 +13,7 @@ Describe "Remove-xAzDoProject" { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoProject.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-AzDoProject.tests.ps1' } # Load the functions to test @@ -44,7 +44,7 @@ Describe "Remove-xAzDoProject" { $projectName = "TestProject" # Act - Remove-xAzDoProject -ProjectName $projectName + Remove-AzDoProject -ProjectName $projectName # Assert Assert-MockCalled -CommandName Get-CacheItem -Exactly -Times 1 -ParameterFilter { @@ -81,7 +81,7 @@ Describe "Remove-xAzDoProject" { $projectName = "NonExistentProject" # Act - Remove-xAzDoProject -ProjectName $projectName + Remove-AzDoProject -ProjectName $projectName # Assert Assert-MockCalled -CommandName Get-CacheItem -Exactly -Times 1 -ParameterFilter { diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 index 934419af3..6d3ba9e0d 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe "Set-xAzDoProject" { +Describe "Set-AzDoProject" { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -13,7 +13,7 @@ Describe "Set-xAzDoProject" { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoProject.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-AzDoProject.tests.ps1' } # Load the functions to test @@ -65,7 +65,7 @@ Describe "Set-xAzDoProject" { $visibility = "Private" # Act - Set-xAzDoProject -ProjectName $projectName -ProjectDescription $projectDescription -SourceControlType $sourceControlType -ProcessTemplate $processTemplate -Visibility $visibility + Set-AzDoProject -ProjectName $projectName -ProjectDescription $projectDescription -SourceControlType $sourceControlType -ProcessTemplate $processTemplate -Visibility $visibility # Assert Assert-MockCalled -CommandName Get-CacheItem -Exactly -Times 1 -ParameterFilter { diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 index 7340850d2..02cd1acbd 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 @@ -9,7 +9,7 @@ Describe "Test-AzDevOpsProject" -Skip { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-xAzDoProject.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-AzDoProject.tests.ps1' } # Load the functions to test diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 index a6af3dbee..28c85070a 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Get-xAzDoProjectGroup' { +Describe 'Get-AzDoProjectGroup' { AfterAll { Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global @@ -13,7 +13,7 @@ Describe 'Get-xAzDoProjectGroup' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-xAzDoProjectGroup.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Get-AzDoProjectGroup.tests.ps1' } # Load the functions to test @@ -43,13 +43,13 @@ Describe 'Get-xAzDoProjectGroup' { Mock -CommandName Remove-CacheItem Mock -CommandName Add-CacheItem Mock -CommandName Format-AzDoGroup -MockWith { - return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + return ('{0}:{1}' -f $mockProjectName, $mockGroupName) } } It 'should call Get-CacheItem for livegroup lookup' { - Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName + Get-AzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName Assert-MockCalled Get-CacheItem -ParameterFilter { $Key -eq "$mockProjectName" -and $Type -eq 'LiveProjects' } -Times 1 @@ -66,10 +66,10 @@ Describe 'Get-xAzDoProjectGroup' { Mock -CommandName Find-CacheItem -MockWith { return @{ originId = 1 } } Mock -CommandName Format-AzDoGroup -MockWith { - return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + return ('{0}:{1}' -f $mockProjectName, $mockGroupName) } - $result = Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName + $result = Get-AzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName $result.status | Should -Be 'Renamed' } @@ -82,10 +82,10 @@ Describe 'Get-xAzDoProjectGroup' { } Mock -CommandName Format-AzDoGroup -MockWith { - return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + return ('{0}:{1}' -f $mockProjectName, $mockGroupName) } - $result = Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName + $result = Get-AzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName $result.status | Should -Be 'NotFound' } @@ -95,10 +95,10 @@ Describe 'Get-xAzDoProjectGroup' { return @{description = 'OldDescription'; name = $mockGroupName; originId = 1} } Mock -CommandName Format-AzDoGroup -MockWith { - return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + return ('{0}:{1}' -f $mockProjectName, $mockGroupName) } - $result = Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName -GroupDescription $mockDescription + $result = Get-AzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName -GroupDescription $mockDescription $result.status | Should -Be 'Changed' $result.propertiesChanged | Should -Contain 'description' @@ -109,10 +109,10 @@ Describe 'Get-xAzDoProjectGroup' { return @{description = $mockDescription; name = $mockGroupName; originId = 1} } Mock -CommandName Format-AzDoGroup -MockWith { - return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + return ('{0}:{1}' -f $mockProjectName, $mockGroupName) } - $result = Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName -GroupDescription $mockDescription + $result = Get-AzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName -GroupDescription $mockDescription $result.status | Should -Be 'Unchanged' } @@ -136,13 +136,13 @@ Describe 'Get-xAzDoProjectGroup' { } Mock -CommandName Format-AzDoGroup -MockWith { - return ("{0}:{1}" -f $mockProjectName, $mockGroupName) + return ('{0}:{1}' -f $mockProjectName, $mockGroupName) } - Get-xAzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName -GroupDescription $mockDescription + Get-AzDoProjectGroup -ProjectName $mockProjectName -GroupName $mockGroupName -GroupDescription $mockDescription Assert-MockCalled -CommandName Add-CacheItem -ParameterFilter { - ($Key -eq ("{0}:{1}" -f $mockProjectName, $mockGroupName)) -and ($Type -eq 'Group') + ($Key -eq ('{0}:{1}' -f $mockProjectName, $mockGroupName)) -and ($Type -eq 'Group') } -Times 1 } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 index 11acf4d7b..021dca614 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 @@ -1,8 +1,8 @@ -# Save this script as New-xAzDoProjectGroup.Tests.ps1 +# Save this script as New-AzDoProjectGroup.Tests.ps1 $currentFile = $MyInvocation.MyCommand.Path -Describe 'New-xAzDoProjectGroup' { +Describe 'New-AzDoProjectGroup' { BeforeAll { @@ -11,7 +11,7 @@ Describe 'New-xAzDoProjectGroup' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-xAzDoProjectGroup.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-AzDoProjectGroup.tests.ps1' } # Load the functions to test @@ -57,7 +57,7 @@ Describe 'New-xAzDoProjectGroup' { ProjectName = 'TestProject' } - $result = New-xAzDoProjectGroup @params + $result = New-AzDoProjectGroup @params Assert-MockCalled Get-CacheItem -Exactly 1 Assert-MockCalled New-DevOpsGroup -Exactly 1 @@ -72,7 +72,7 @@ Describe 'New-xAzDoProjectGroup' { ProjectName = 'TestProject' } - $result = New-xAzDoProjectGroup @params + $result = New-AzDoProjectGroup @params Assert-MockCalled Add-CacheItem -Exactly 1 Assert-MockCalled Set-CacheObject -Exactly 1 @@ -93,7 +93,7 @@ Describe 'New-xAzDoProjectGroup' { ProjectName = 'TestProject' } - $result = New-xAzDoProjectGroup @params + $result = New-AzDoProjectGroup @params Assert-MockCalled Get-CacheItem -Exactly 1 Assert-MockCalled Write-Warning -Exactly 1 diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 index 5932bee84..f70c6e52d 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 @@ -1,6 +1,6 @@ $currentFile = $MyInvocation.MyCommand.Path -Describe 'Remove-xAzDoProjectGroup' { +Describe 'Remove-AzDoProjectGroup' { AfterAll { # Clean up @@ -14,7 +14,7 @@ Describe 'Remove-xAzDoProjectGroup' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-xAzDoProjectGroup.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-AzDoProjectGroup.tests.ps1' } # Load the functions to test @@ -47,7 +47,7 @@ Describe 'Remove-xAzDoProjectGroup' { localCache = $null } - $result = Remove-xAzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult + $result = Remove-AzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult Assert-MockCalled -CommandName Remove-DevOpsGroup -Times 0 -Exactly Assert-MockCalled -CommandName Remove-CacheItem -Times 0 -Exactly @@ -66,7 +66,7 @@ Describe 'Remove-xAzDoProjectGroup' { localCache = $null } - $result = Remove-xAzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult + $result = Remove-AzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult Assert-MockCalled -CommandName Remove-DevOpsGroup -Times 1 -Exactly Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -ParameterFilter { $type -eq 'LiveGroups' } @@ -89,7 +89,7 @@ Describe 'Remove-xAzDoProjectGroup' { } } - $result = Remove-xAzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult + $result = Remove-AzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult Assert-MockCalled -CommandName Remove-DevOpsGroup -Times 1 Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -ParameterFilter { $type -eq 'LiveGroups' } @@ -109,7 +109,7 @@ Describe 'Remove-xAzDoProjectGroup' { } } - $result = Remove-xAzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult + $result = Remove-AzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult Assert-MockCalled -CommandName Remove-DevOpsGroup -Times 1 Assert-MockCalled -CommandName Remove-CacheItem -Times 1 -ParameterFilter { $type -eq 'LiveGroups' } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 index 36f1d2e75..843a78a70 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 @@ -1,9 +1,9 @@ $currentFile = $MyInvocation.MyCommand.Path -# Import the module containing Set-xAzDoProjectGroup if it's in a different file. +# Import the module containing Set-AzDoProjectGroup if it's in a different file. # . .\path\to\your\module.psm1 -Describe 'Set-xAzDoProjectGroup' { +Describe 'Set-AzDoProjectGroup' { AfterAll { # Clean up @@ -17,7 +17,7 @@ Describe 'Set-xAzDoProjectGroup' { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-xAzDoProjectGroup.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Set-AzDoProjectGroup.tests.ps1' } # Load the functions to test @@ -55,7 +55,7 @@ Describe 'Set-xAzDoProjectGroup' { } } - $result = Set-xAzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult + $result = Set-AzDoProjectGroup -GroupName 'TestGroup' -ProjectName 'TestProject' -LookupResult $LookupResult Assert-MockCalled -CommandName Set-DevOpsGroup -Times 0 -Exactly Assert-MockCalled -CommandName Refresh-CacheIdentity -Times 0 -Exactly @@ -79,7 +79,7 @@ Describe 'Set-xAzDoProjectGroup' { $Global:DSCAZDO_OrganizationName = 'TestOrg' - $result = Set-xAzDoProjectGroup -GroupName 'TestGroup' -GroupDescription 'TestDescription' -ProjectName 'TestProject' -LookupResult $LookupResult + $result = Set-AzDoProjectGroup -GroupName 'TestGroup' -GroupDescription 'TestDescription' -ProjectName 'TestProject' -LookupResult $LookupResult Assert-MockCalled -CommandName Set-DevOpsGroup -Times 1 -Exactly -ParameterFilter { ($ApiUri -eq "https://vssps.dev.azure.com/TestOrg") -and @@ -121,7 +121,7 @@ Describe 'Set-xAzDoProjectGroup' { $Global:DSCAZDO_OrganizationName = 'TestOrg' - $result = Set-xAzDoProjectGroup -GroupName 'TestGroup' -GroupDescription 'TestDescription' -ProjectName 'TestProject' -LookupResult $LookupResult + $result = Set-AzDoProjectGroup -GroupName 'TestGroup' -GroupDescription 'TestDescription' -ProjectName 'TestProject' -LookupResult $LookupResult Assert-MockCalled -CommandName Remove-CacheItem -Times 0 -Exactly } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 index 071759b37..f9c2f5860 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 @@ -1,7 +1,7 @@ $currentFile = $MyInvocation.MyCommand.Path # Not used -Describe 'Test-xAzDoProjectGroup' -skip { +Describe 'Test-AzDoProjectGroup' -skip { AfterAll { # Clean up @@ -15,7 +15,7 @@ Describe 'Test-xAzDoProjectGroup' -skip { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-xAzDoProjectGroup.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Test-AzDoProjectGroup.tests.ps1' } # Load the functions to test @@ -49,7 +49,7 @@ Describe 'Test-xAzDoProjectGroup' -skip { Current = @{ description = 'Group Description' } } - $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result = Test-AzDoProjectGroup -GroupName $GroupName -GetResult $GetResult $result | Should -BeTrue } @@ -61,7 +61,7 @@ Describe 'Test-xAzDoProjectGroup' -skip { Current = @{ description = $GroupDescription } } - $result = Test-xAzDoProjectGroup -GroupName $GroupName -GroupDescription $GroupDescription -GetResult $GetResult + $result = Test-AzDoProjectGroup -GroupName $GroupName -GroupDescription $GroupDescription -GetResult $GetResult $result | Should -BeFalse } @@ -73,7 +73,7 @@ Describe 'Test-xAzDoProjectGroup' -skip { Cache = @{} } - $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result = Test-AzDoProjectGroup -GroupName $GroupName -GetResult $GetResult $result | Should -BeTrue } @@ -85,7 +85,7 @@ Describe 'Test-xAzDoProjectGroup' -skip { Cache = $null } - $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result = Test-AzDoProjectGroup -GroupName $GroupName -GetResult $GetResult $result | Should -BeTrue } @@ -97,7 +97,7 @@ Describe 'Test-xAzDoProjectGroup' -skip { Cache = @{} } - $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result = Test-AzDoProjectGroup -GroupName $GroupName -GetResult $GetResult $result | Should -BeTrue } @@ -107,7 +107,7 @@ Describe 'Test-xAzDoProjectGroup' -skip { Status = [DSCGetSummaryState]::Renamed } - $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result = Test-AzDoProjectGroup -GroupName $GroupName -GetResult $GetResult $result | Should -BeFalse } @@ -117,7 +117,7 @@ Describe 'Test-xAzDoProjectGroup' -skip { Status = [DSCGetSummaryState]::Missing } - $result = Test-xAzDoProjectGroup -GroupName $GroupName -GetResult $GetResult + $result = Test-AzDoProjectGroup -GroupName $GroupName -GetResult $GetResult $result | Should -BeTrue } } diff --git a/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1 b/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1 index a07d32939..eaf0a0735 100644 --- a/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1 +++ b/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1 @@ -264,7 +264,7 @@ function Set-OutputDirAsModulePath if ($ENV:PSModulePath -like "*$($RepositoryRoot)*") { return } $ModulePath = '{0}{1}\' -f (($IsLinux) ? ':' : ';'), $RepositoryRoot - $ENV:PSModulePath = "{0}{1}\output" -f $ENV:PSModulePath, $ModulePath - $ENV:PSModulePath = "{0}{1}\output\AzureDevOpsDsc\0.0.0\Modules" -f $ENV:PSModulePath, $ModulePath + $ENV:PSModulePath = '{0}{1}\output' -f $ENV:PSModulePath, $ModulePath + $ENV:PSModulePath = '{0}{1}\output\AzureDevOpsDsc\0.0.0\Modules' -f $ENV:PSModulePath, $ModulePath } From ef092a1d9410361ac4644a09ca84e0180b7ecdb9 Mon Sep 17 00:00:00 2001 From: Michael Zanatta Date: Mon, 4 Nov 2024 08:05:03 +1000 Subject: [PATCH 5/7] Unit testing rework (#5) * Remove 'x' from Files and Directories * Added Common Testing to root of the repository Updating Project Tasks * Bug Fixes * Bug Fixes within Tests Updating Tests to be in-line with changes * Code Cleanup of commontests script Add New-AzDoAuthenticationProvider tests * Big Fixes * Bug Fixes with Tests Adding Azure DevOps DSC Tests * Adding Basline Mocking and Classes * Adding Test Initalization Fixes Fixes to Tests More Work is required. * Adding Tests Renamed: Invoke-BeforeEachFunctions to: Get-FunctionItem * Renamed: Find-Functions to Find-MockedFunctions * Finishing Initial Testing Removing Un-used Tests * Bug Fixes to Bring in tests * Fixed: AzDevOpsDscResourceBase tests * Refactoring Tests * Bug Fixes * Bug Fixes with Tests * Adding Tests * Adding More tests Bug Fixes * Removing Archived Tests * Bug Fixes Updating Documentation * Adding Documentation * Adding excludedProjectsFromTeardown * Running Intergration Tests Fixing Bug with Test-isWindowsAdmin function Fixing Intergration Supporting function bugs * Removing Unsed Paths. Fixing Tests * Fixed Bug with Regex Matching Added Unit Testing to provide code coverage * Bug Fixes * Bug Fixes with Intergration Tests Fixed Bug with ResourceBase class Fixed Bug with GetMembers using the wrong type * Fixed Bug with Test-AzDoProject being the incorrect type Fixed bug in Get-AzDoProject typecasting lookupresult Fixed unchanged bug with Get-AzDoProject * Bug Fixing Tests --------- Co-authored-by: Michael Zanatta --- .gitattributes | 1 + CHANGELOG.md | 33 +- CONTRIBUTING.md | 4 +- azuredevopsdsc.common.tests.ps1 | 43 ++ azuredevopsdsc.tests.ps1 | 87 +++++ build.yaml | 3 +- source/AzureDevOpsDsc.psd1 | 2 +- source/Classes/004.DscResourceBase.ps1 | 6 +- .../Classes/006.AzDevOpsDscResourceBase.ps1 | 68 ++-- source/Classes/009.AzDoGroupPermission.ps1 | 4 + .../Authentication/1-NewAuthenticationPAT.ps1 | 10 + .../2-NewAuthenticationManagedIdentity.ps1 | 8 + .../AzDevOpsProject/1-AddProject.ps1 | 18 +- .../AzDevOpsProject/2-UpdateProject.ps1 | 19 +- .../AzDevOpsProject/3-DeleteProject.ps1 | 17 +- .../4-AddProject-UsingManaged-Identity.ps1 | 43 -- .../Examples/Resources/AzDoGitPermission.md | 195 ++++++++++ .../AzDoGitPermission/1-AddGitPermission.ps1 | 39 ++ .../2-UpdateGitPermission.ps1 | 39 ++ .../3-DeleteAzDoGitPermission.ps1 | 27 ++ .../Examples/Resources/AzDoGitRepository.md | 83 ++++ .../1-AddAzDoGitRepository.ps1 | 22 ++ .../2-UpdateAzDoGitRepository.ps1 | 6 + .../3-DeleteAzDoGitRepository.ps1 | 22 ++ source/Examples/Resources/AzDoGroupMember.md | 127 ++++++ .../AzDoGroupMember/1-AddAzDoGroupMember.ps1 | 25 ++ .../2-UpdateAzDoGroupMember.ps1 | 25 ++ .../3-DeleteAzDoGroupMember.ps1 | 23 ++ .../Examples/Resources/AzDoGroupPermission.md | 147 +++++++ .../Resources/AzDoOrganizationGroup.md | 96 +++++ .../1-AddAzDoOrganizationGroup.ps1 | 22 ++ .../2-UpdateAzDoOrganizationGroup.ps1 | 22 ++ .../3-DeleteAzDoOrganizationGroup.ps1 | 21 + source/Examples/Resources/AzDoProject.md | 123 ++++++ source/Examples/Resources/AzDoProjectGroup.md | 102 +++++ .../1-AddAzDoProjectGroup.ps1 | 22 ++ .../2-UpdateAzDoProjectGroup.ps1 | 22 ++ .../3-DeleteAzDoProjectGroup.ps1 | 21 + .../Examples/Resources/AzDoProjectServices.md | 111 ++++++ .../1-AddAzDoProjectServices.ps1 | 24 ++ .../2-UpdateAzDoProjectServices.ps1 | 24 ++ .../3-DeleteAzDoProjectServices.ps1 | 6 + .../Get-AzManagedIdentityToken.ps1 | 23 +- .../Get-OperatingSystemInfo.ps1 | 38 ++ .../ManagedIdentity/Test-isWindowsAdmin.ps1 | 29 ++ .../Update-AzManagedIdentity.ps1 | 2 +- .../Helper/Invoke-AzDevOpsApiRestMethod.ps1 | 2 +- .../Private/Helper/System/New-Thread.ps1 | 43 -- .../AzureDevOpsDsc.Common.psm1 | 2 +- .../000.LocalizedDataAzACLTokenPatten.ps1 | 8 +- .../AzDoGroupMember/New-AzDoGroupMember.ps1 | 2 +- .../Public/AzDoProject/Get-AzDoProject.ps1 | 12 +- .../Public/AzDoProject/Test-AzDoProject.ps1 | 2 +- .../Get-AzDoProjectServices.ps1 | 4 +- .../Public/New-AzDoAuthenticationProvider.ps1 | 7 +- .../WikiSource/Resources/AzDoGitPermission.md | 2 +- .../WikiSource/Resources/AzDoGroupMember.md | 2 +- .../Resources/AzDoOrganizationGroup.md | 2 +- source/WikiSource/Resources/AzDoProject.md | 2 +- .../WikiSource/Resources/AzDoProjectGroup.md | 2 +- .../Resources/AzDoProjectServices.md | 2 +- tests/DSC/InModuleScope.ps1 | 8 - tests/Integration/Invoke-Tests.ps1 | 2 +- ....tests.ps1 => AzDoGitPermission.tests.ps1} | 1 - ....tests.ps1 => AzDoGitRepository.tests.ps1} | 0 ...er.tests.ps1 => AzDoGroupMember.tests.ps1} | 0 ...ests.ps1 => AzDoGroupPermission.tests.ps1} | 0 ...DoOrganizationGroup.Description.tests.ps1} | 0 ...OrganizationGroup.NoDescription.tests.ps1} | 0 ....ps1 => AzDoProject.Description.tests.ps1} | 0 ...s1 => AzDoProject.NoDescription.tests.ps1} | 0 ...=> AzDoProjectGroup.Description.tests.ps1} | 0 ...ests.ps1 => AzDoProjectServices.tests.ps1} | 0 .../Supporting/API/Get-MIToken.ps1 | 43 +- .../API/Get-OperatingSystemInfo.ps1 | 38 ++ .../Supporting/API/Invoke-APIRestMethod.ps1 | 137 ++----- .../Supporting/API/Test-isWindowsAdmin.ps1 | 10 + tests/Integration/Supporting/Teardown.ps1 | 8 +- .../TestFrameworkConfiguration.json | 5 +- .../Classes/API/007.APIRateLimit.tests.ps1 | 12 +- .../001.AuthenticationToken.tests.ps1 | 7 +- .../002.PersonalAccessToken.tests.ps1 | 7 +- .../003.ManagedIdentityToken.tests.ps1 | 18 +- .../AzDevOpsApiDscResourceBase/BeforeAll.ps1 | 8 + .../GetResourceFunctionName.Tests.ps1 | 172 ++++---- .../GetResourceId.Tests.ps1 | 55 +-- .../GetResourceIdPropertyName.Tests.ps1 | 54 +-- .../GetResourceKey.Tests.ps1 | 56 +-- .../GetResourceKeyPropertyName.Tests.ps1 | 56 +-- .../GetResourceName.Tests.ps1 | 88 ++--- ...psDscResourceBase.Initialization.Tests.ps1 | 14 - .../AzDevOpsDscResourceBase/BeforeAll.ps1 | 8 + .../GetDscCurrentStateObject.Tests.ps1 | 133 +++---- ...cCurrentStateObjectGetParameters.Tests.ps1 | 250 ++++++------ ...GetDscCurrentStateResourceObject.Tests.ps1 | 152 ++++---- .../GetPostSetWaitTimeSeconds.Tests.ps1 | 74 ++-- .../AzDevOpsDscResourceBase/Set.Tests.ps1 | 81 ++-- .../SetToDesiredState.Tests.ps1 | 187 ++++----- .../AzDevOpsDscResourceBase/Test.Tests.ps1 | 108 +++--- .../TestDesiredState.Tests.ps1 | 157 ++++---- tests/Unit/Classes/Classes.BeforeAll.ps1 | 42 -- .../Classes/DscResourceBase/BeforeAll.ps1 | 8 + .../DscResourceBase.Initialization.Tests.ps1 | 12 - .../GetDscResourceKey.Tests.ps1 | 91 ++--- .../GetDscResourceKeyPropertyName.Tests.ps1 | 76 ++-- .../GetDscResourcePropertyNames.Tests.ps1 | 64 ++- ...rcePropertyNamesWithNoSetSupport.Tests.ps1 | 76 ++-- .../Unit/Classes/Resources/000.BeforeAll.ps1 | 11 + .../009.AzDoGroupPermission.tests.ps1 | 232 +++++++++++ .../009.xAzDoGroupPermission.tests.ps1 | 59 --- ...s1 => 011.AzDoOrganizationGroup.tests.ps1} | 37 +- .../Resources/020.AzDoProject.tests.ps1 | 120 ++++++ .../Resources/020.xAzDevOpsProject.tests.ps1 | 71 ---- .../Resources/022.AzDoProjectGroup.tests.ps1 | 73 ++++ .../Resources/022.xAzDoProjectGroup.tests.ps1 | 89 ----- .../Resources/031.AzDoGroupMember.tests.ps1 | 70 ++++ .../Resources/031.xAzDoGroupMember.tests.ps1 | 82 ---- .../Resources/040.AzDoGitRepository.tests.ps1 | 77 ++++ .../040.xAzDoGitRepository.tests.ps1 | 87 ----- .../Resources/041.AzDoGitPermission.tests.ps1 | 76 ++++ .../041.xAzDoGitPermission.tests.ps1 | 91 ----- .../AzDevOpsProject/AzDevOpsProject.Tests.ps1 | 301 -------------- .../AzDevOpsProject/Get.Tests.ps1 | 262 ------------- .../GetDscCurrentStateProperties.Tests.ps1 | 366 ------------------ ...rcePropertyNamesWithNoSetSupport.Tests.ps1 | 259 ------------- .../DSCClassResources.TestInitialization.ps1 | 22 -- .../Private/Api/ACL/Get-DevOpsACL.tests.ps1 | 2 +- .../Get-DevOpsDescriptorIdentity.tests.ps1 | 2 +- ...ts.ps1 => Remove-AzDoPermission.tests.ps1} | 2 +- ...tests.ps1 => Set-AzDoPermission.tests.ps1} | 2 +- .../Cache/List-DevOpsGitRepository.tests.ps1 | 2 +- .../Cache/List-DevOpsGroupMembers.tests.ps1 | 2 +- .../Api/Cache/List-DevOpsGroups.tests.ps1 | 2 +- .../Api/Cache/List-DevOpsProcess.tests.ps1 | 2 +- .../Api/Cache/List-DevOpsProjects.tests.ps1 | 2 +- .../List-DevOpsSecurityNamespaces.tests.ps1 | 2 +- .../List-DevOpsServicePrinciples.tests.ps1 | 2 +- .../Api/Cache/List-UserCache.tests.ps1 | 2 +- .../GitRepository/New-GitRepository.tests.ps1 | 2 +- .../Remove-GitRepository.tests.ps1 | 2 +- .../Api/Group/New-DevOpsGroup.tests.ps1 | 2 +- .../Api/Group/Remove-DevOpsGroup.tests.ps1 | 2 +- .../Api/Group/Set-DevOpsGroup.tests.ps1 | 2 +- .../New-DevOpsGroupMember.tests.ps1 | 2 +- .../Remove-DevOpsGroupMember.tests.ps1 | 2 +- .../Api/Project/New-DevOpsProject.tests.ps1 | 2 +- .../Project/Remove-DevOpsProject.tests.ps1 | 2 +- .../Project/Update-DevOpsProject.tests.ps1 | 2 +- .../Api/Project/Wait-DevOpsProject.tests.ps1 | 2 +- .../Get-ProjectServiceStatus.tests.ps1 | 2 +- .../Set-ProjectServiceStatus.tests.ps1 | 2 +- .../Get-DevOpsSecurityDescriptor.tests.ps1 | 2 +- .../Add-AuthenticationHTTPHeader.tests.ps1 | 2 +- .../Get-AzManagedIdentityToken.tests.ps1 | 124 +++++- .../Update-AzManagedIdentity.tests.ps1 | 2 +- .../Set-AzPersonalAccessToken.tests.ps1 | 2 +- .../Authentication/Test-AzToken.tests.ps1 | 2 +- .../Private/Cache/Add-CacheItem.tests.ps1 | 2 +- .../0.ProjectCache.tests.ps1 | 2 +- .../1.GroupCache.tests.ps1 | 2 +- .../Cache Initalization/2.UserCache.tests.ps1 | 2 +- .../3.GroupMemberCache.tests.ps1 | 2 +- .../4.GitRepositoryCache.tests.ps1 | 2 +- .../5.PermissionsCache.tests.ps1 | 2 +- .../6.ServicePrinciple.tests.ps1 | 2 +- .../7.IdentitySubjectDescriptors.tests.ps1 | 2 +- .../8.ProcessTemplates.tests.ps1 | 2 +- .../Cache/Export-CacheObject.tests.ps1 | 2 +- .../Private/Cache/Find-CacheItem.tests.ps1 | 2 +- .../Private/Cache/Get-CacheItem.tests.ps1 | 2 +- .../Private/Cache/Get-CacheObject.tests.ps1 | 2 +- .../Cache/Import-CacheObject.tests.ps1 | 2 +- .../Cache/Initialize-CacheObject.tests.ps1 | 2 +- .../Private/Cache/Refresh-AzDoCache.tests.ps1 | 3 +- .../Cache/Refresh-CacheIdentity.tests.ps1 | 2 +- .../Cache/Refresh-CacheObject.tests.ps1 | 4 +- .../Private/Cache/Remove-CacheItem.tests.ps1 | 2 +- .../Private/Cache/Set-CacheObject.tests.ps1 | 2 +- .../Helper/ACL/ConvertTo-ACEList.tests.ps1 | 2 +- .../ACL/ConvertTo-ACETokenList.tests.ps1 | 2 +- .../Helper/ACL/ConvertTo-ACL.tests.ps1 | 4 +- .../ACL/ConvertTo-ACLHashtable.tests.ps1 | 2 +- .../ACL/ConvertTo-FormattedACL.tests.ps1 | 2 +- .../ACL/ConvertTo-FormattedToken.tests.ps1 | 12 +- .../Private/Helper/ACL/Format-ACEs.tests.ps1 | 2 +- .../Helper/ACL/Get-BitwiseOrResult.tests.ps1 | 2 +- .../Private/Helper/ACL/Group-ACEs.tests.ps1 | 32 +- .../Private/Helper/ACL/New-ACLToken.tests.ps1 | 2 +- .../Helper/ACL/Parse-ACLToken.tests.ps1 | 2 +- .../Helper/ACL/Resolve-ACLToken.tests.ps1 | 2 +- .../ACL/Test-ACLListforChanges.tests.ps1 | 2 +- ...est-AzDevOpsApiHttpRequestHeader.tests.ps1 | 2 +- .../API/Test-AzDevOpsApiResourceId.tests.ps1 | 2 +- .../Test-AzDevOpsApiTimeoutExceeded.tests.ps1 | 2 +- .../Helper/API/Test-AzDevOpsApiUri.tests.ps1 | 2 +- .../API/Test-AzDevOpsApiVersion.tests.ps1 | 2 +- .../API/Wait-AzDevOpsApiResource.tests.ps1 | 2 +- .../Helper/ConvertTo-Base64String.tests.ps1 | 2 +- .../Helper/Find-AzDoIdentity.tests.ps1 | 2 +- .../Private/Helper/Find-Identity.tests.ps1 | 44 ++- .../Private/Helper/Format-AzDoGroup.tests.ps1 | 2 +- .../Helper/Format-AzDoProjectName.tests.ps1 | 2 +- .../Helper/Format-DescriptorType.tests.ps1 | 2 +- ...Get-AzDevOpsApiHttpRequestHeader.tests.ps1 | 2 +- .../Get-AzDevOpsApiResourceName.tests.ps1 | 2 +- .../Get-AzDevOpsApiUriAreaName.tests.ps1 | 2 +- .../Helper/Get-AzDevOpsApiVersion.tests.ps1 | 2 +- .../Get-AzDevOpsApiWaitIntervalMs.tests.ps1 | 2 +- .../Get-AzDevOpsApiWaitTimeoutMs.tests.ps1 | 2 +- .../Helper/Get-AzDoCacheObjects.tests.ps1 | 2 +- .../Invoke-AzDevOpsApiRestMethod.tests.ps1 | 2 +- .../Helper/New-AzDevOpsACLToken.tests.ps1 | 2 +- .../AzureDevOpsDsc.Common.InvokeTests.ps1 | 37 -- .../Archive/Get-AzDevOpsCache.ps1.disabled | 87 ----- ...-AzManagedIdentityToken.Tests.ps1.disabled | 72 ---- .../New-AzDevOpsACLToken.Tests.ps1.disabled | 68 ---- .../New-AzDevOpsApiCache.Tests.ps1.disabled | 68 ---- ...New-AzDevOpsApiResource.Tests.ps1.disabled | 9 - .../New-AzManagedIdentity.Tests.ps1.disabled | 53 --- ...t-AzDevOpsPatCredential.Tests.ps1.disabled | 107 ----- ...-AzManagedIdentityToken.Tests.ps1.disabled | 59 --- ...pdate-AzManagedIdentity.Tests.ps1.disabled | 28 -- ...00.LocalizedDataAzACLTokenPatten.tests.ps1 | 61 +++ .../Get-AzDoGitPermission.tests.ps1} | 2 +- .../New-AzDoGitPermission.tests.ps1} | 2 +- .../Remove-AzDoGitPermission.tests.ps1} | 2 +- .../Set-AzDoGitPermission.tests.ps1} | 2 +- .../Get-AzDoGitRepository.tests.ps1} | 2 +- .../New-AzDoGitRepository.tests.ps1} | 2 +- .../Remove-AzDoGitRepository.tests.ps1} | 2 +- .../Set-AzDoGitRepository.tests.ps1} | 2 +- .../Get-AzDoGroupMember.tests.ps1} | 2 +- .../New-AzDoGroupMember.tests.ps1} | 2 +- .../Remove-AzDoGroupMember.tests.ps1} | 2 +- .../Set-AzDoGroupMember.tests.ps1} | 2 +- .../Test-AzDoGroupMember.tests.ps1} | 2 +- .../Get-AzDoGroupPermission.tests.ps1} | 2 +- .../New-AzDoGroupPermission.tests.ps1} | 2 +- .../Remove-AzDoGroupPermission.tests.ps1} | 2 +- .../Set-AzDoGroupPermission.tests.ps1} | 2 +- .../Get-AzDoOrganizationGroup.tests.ps1} | 2 +- .../New-AzDoOrganizationGroup.tests.ps1} | 2 +- .../Remove-AzDoOrganizationGroup.tests.ps1} | 2 +- .../Set-AzDoOrganizationGroup.tests.ps1} | 2 +- .../Test-AzDoOrganizationGroup.tests.ps1} | 2 +- .../Get-AzDoProject.tests.ps1} | 6 +- .../New-AzDoProject.tests.ps1} | 19 +- .../Remove-AzDoProject.tests.ps1} | 2 +- .../Set-AzDoProject.tests.ps1} | 6 +- .../Test-AzDoProject.tests.ps1} | 2 +- .../Get-AzDoProjectGroup.tests.ps1} | 2 +- .../New-AzDoProjectGroup.tests.ps1} | 2 +- .../Remove-AzDoProjectGroup.tests.ps1} | 2 +- .../Set-AzDoProjectGroup.tests.ps1} | 2 +- .../Test-AzDoProjectGroup.tests.ps1} | 2 +- .../New-AzDoAuthenticationProvider.tests.ps1 | 130 +++++++ ...AzureDevOpsDsc.DscClassResources.Tests.ps1 | 30 -- .../TestHelpers/CommonTestFunctions.psm1 | 6 +- 258 files changed, 3914 insertions(+), 4022 deletions(-) create mode 100644 azuredevopsdsc.common.tests.ps1 create mode 100644 azuredevopsdsc.tests.ps1 create mode 100644 source/Examples/Authentication/1-NewAuthenticationPAT.ps1 create mode 100644 source/Examples/Authentication/2-NewAuthenticationManagedIdentity.ps1 delete mode 100644 source/Examples/Resources/AzDevOpsProject/4-AddProject-UsingManaged-Identity.ps1 create mode 100644 source/Examples/Resources/AzDoGitPermission.md create mode 100644 source/Examples/Resources/AzDoGitPermission/1-AddGitPermission.ps1 create mode 100644 source/Examples/Resources/AzDoGitPermission/2-UpdateGitPermission.ps1 create mode 100644 source/Examples/Resources/AzDoGitPermission/3-DeleteAzDoGitPermission.ps1 create mode 100644 source/Examples/Resources/AzDoGitRepository.md create mode 100644 source/Examples/Resources/AzDoGitRepository/1-AddAzDoGitRepository.ps1 create mode 100644 source/Examples/Resources/AzDoGitRepository/2-UpdateAzDoGitRepository.ps1 create mode 100644 source/Examples/Resources/AzDoGitRepository/3-DeleteAzDoGitRepository.ps1 create mode 100644 source/Examples/Resources/AzDoGroupMember.md create mode 100644 source/Examples/Resources/AzDoGroupMember/1-AddAzDoGroupMember.ps1 create mode 100644 source/Examples/Resources/AzDoGroupMember/2-UpdateAzDoGroupMember.ps1 create mode 100644 source/Examples/Resources/AzDoGroupMember/3-DeleteAzDoGroupMember.ps1 create mode 100644 source/Examples/Resources/AzDoGroupPermission.md create mode 100644 source/Examples/Resources/AzDoOrganizationGroup.md create mode 100644 source/Examples/Resources/AzDoOrganizationGroup/1-AddAzDoOrganizationGroup.ps1 create mode 100644 source/Examples/Resources/AzDoOrganizationGroup/2-UpdateAzDoOrganizationGroup.ps1 create mode 100644 source/Examples/Resources/AzDoOrganizationGroup/3-DeleteAzDoOrganizationGroup.ps1 create mode 100644 source/Examples/Resources/AzDoProject.md create mode 100644 source/Examples/Resources/AzDoProjectGroup.md create mode 100644 source/Examples/Resources/AzDoProjectGroup/1-AddAzDoProjectGroup.ps1 create mode 100644 source/Examples/Resources/AzDoProjectGroup/2-UpdateAzDoProjectGroup.ps1 create mode 100644 source/Examples/Resources/AzDoProjectGroup/3-DeleteAzDoProjectGroup.ps1 create mode 100644 source/Examples/Resources/AzDoProjectServices.md create mode 100644 source/Examples/Resources/AzDoProjectServices/1-AddAzDoProjectServices.ps1 create mode 100644 source/Examples/Resources/AzDoProjectServices/2-UpdateAzDoProjectServices.ps1 create mode 100644 source/Examples/Resources/AzDoProjectServices/3-DeleteAzDoProjectServices.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-OperatingSystemInfo.ps1 create mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Test-isWindowsAdmin.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 rename tests/Integration/Resources/{xAzDoGitPermission.tests.ps1 => AzDoGitPermission.tests.ps1} (99%) rename tests/Integration/Resources/{xAzDoGitRepository.tests.ps1 => AzDoGitRepository.tests.ps1} (100%) rename tests/Integration/Resources/{xAzDoGroupMember.tests.ps1 => AzDoGroupMember.tests.ps1} (100%) rename tests/Integration/Resources/{xAzDoGroupPermission.tests.ps1 => AzDoGroupPermission.tests.ps1} (100%) rename tests/Integration/Resources/{xAzDoOrganizationGroup.Description.tests.ps1 => AzDoOrganizationGroup.Description.tests.ps1} (100%) rename tests/Integration/Resources/{xAzDoOrganizationGroup.NoDescription.tests.ps1 => AzDoOrganizationGroup.NoDescription.tests.ps1} (100%) rename tests/Integration/Resources/{xAzDoProject.Description.tests.ps1 => AzDoProject.Description.tests.ps1} (100%) rename tests/Integration/Resources/{xAzDoProject.NoDescription.tests.ps1 => AzDoProject.NoDescription.tests.ps1} (100%) rename tests/Integration/Resources/{xAzDoProjectGroup.Description.tests.ps1 => AzDoProjectGroup.Description.tests.ps1} (100%) rename tests/Integration/Resources/{xAzDoProjectServices.tests.ps1 => AzDoProjectServices.tests.ps1} (100%) create mode 100644 tests/Integration/Supporting/API/Get-OperatingSystemInfo.ps1 create mode 100644 tests/Integration/Supporting/API/Test-isWindowsAdmin.ps1 create mode 100644 tests/Unit/Classes/AzDevOpsApiDscResourceBase/BeforeAll.ps1 delete mode 100644 tests/Unit/Classes/AzDevOpsDscResourceBase/AzDevOpsDscResourceBase.Initialization.Tests.ps1 create mode 100644 tests/Unit/Classes/AzDevOpsDscResourceBase/BeforeAll.ps1 delete mode 100644 tests/Unit/Classes/Classes.BeforeAll.ps1 create mode 100644 tests/Unit/Classes/DscResourceBase/BeforeAll.ps1 delete mode 100644 tests/Unit/Classes/DscResourceBase/DscResourceBase.Initialization.Tests.ps1 create mode 100644 tests/Unit/Classes/Resources/000.BeforeAll.ps1 create mode 100644 tests/Unit/Classes/Resources/009.AzDoGroupPermission.tests.ps1 delete mode 100644 tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 rename tests/Unit/Classes/Resources/{011.xAzDoOrganizationGroup.tests.ps1 => 011.AzDoOrganizationGroup.tests.ps1} (70%) create mode 100644 tests/Unit/Classes/Resources/020.AzDoProject.tests.ps1 delete mode 100644 tests/Unit/Classes/Resources/020.xAzDevOpsProject.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/022.AzDoProjectGroup.tests.ps1 delete mode 100644 tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/031.AzDoGroupMember.tests.ps1 delete mode 100644 tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/040.AzDoGitRepository.tests.ps1 delete mode 100644 tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 create mode 100644 tests/Unit/Classes/Resources/041.AzDoGitPermission.tests.ps1 delete mode 100644 tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 delete mode 100644 tests/Unit/DSCClassResources/AzDevOpsProject/AzDevOpsProject.Tests.ps1 delete mode 100644 tests/Unit/DSCClassResources/AzDevOpsProject/Get.Tests.ps1 delete mode 100644 tests/Unit/DSCClassResources/AzDevOpsProject/GetDscCurrentStateProperties.Tests.ps1 delete mode 100644 tests/Unit/DSCClassResources/AzDevOpsProject/GetDscResourcePropertyNamesWithNoSetSupport.Tests.ps1 delete mode 100644 tests/Unit/DSCClassResources/DSCClassResources.TestInitialization.ps1 rename tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/{Remove-xAzDoPermission.tests.ps1 => Remove-AzDoPermission.tests.ps1} (96%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/{Set-xAzDoPermission.tests.ps1 => Set-AzDoPermission.tests.ps1} (96%) delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.InvokeTests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzDevOpsCache.ps1.disabled delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzManagedIdentityToken.Tests.ps1.disabled delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsACLToken.Tests.ps1.disabled delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiCache.Tests.ps1.disabled delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiResource.Tests.ps1.disabled delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzManagedIdentity.Tests.ps1.disabled delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzDevOpsPatCredential.Tests.ps1.disabled delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzManagedIdentityToken.Tests.ps1.disabled delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Update-AzManagedIdentity.Tests.ps1.disabled create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.tests.ps1 rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 => AzDoGitPermission/Get-AzDoGitPermission.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 => AzDoGitPermission/New-AzDoGitPermission.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 => AzDoGitPermission/Remove-AzDoGitPermission.tests.ps1} (97%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 => AzDoGitPermission/Set-AzDoGitPermission.tests.ps1} (97%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 => AzDoGitRepository/Get-AzDoGitRepository.tests.ps1} (96%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 => AzDoGitRepository/New-AzDoGitRepository.tests.ps1} (97%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 => AzDoGitRepository/Remove-AzDoGitRepository.tests.ps1} (97%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 => AzDoGitRepository/Set-AzDoGitRepository.tests.ps1} (85%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 => AzDoGroupMember/Get-AzDoGroupMember.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 => AzDoGroupMember/New-AzDoGroupMember.tests.ps1} (97%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 => AzDoGroupMember/Remove-AzDoGroupMember.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 => AzDoGroupMember/Set-AzDoGroupMember.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 => AzDoGroupMember/Test-AzDoGroupMember.tests.ps1} (96%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 => AzDoGroupPermission/Get-AzDoGroupPermission.tests.ps1} (97%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 => AzDoGroupPermission/New-AzDoGroupPermission.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 => AzDoGroupPermission/Remove-AzDoGroupPermission.tests.ps1} (97%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 => AzDoGroupPermission/Set-AzDoGroupPermission.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 => AzDoOrganizationGroup/Get-AzDoOrganizationGroup.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 => AzDoOrganizationGroup/New-AzDoOrganizationGroup.tests.ps1} (96%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 => AzDoOrganizationGroup/Remove-AzDoOrganizationGroup.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 => AzDoOrganizationGroup/Set-AzDoOrganizationGroup.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 => AzDoOrganizationGroup/Test-AzDoOrganizationGroup.tests.ps1} (96%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProject/Get-xAzDoProject.tests.ps1 => AzDoProject/Get-AzDoProject.tests.ps1} (96%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProject/New-xAzDoProject.tests.ps1 => AzDoProject/New-AzDoProject.tests.ps1} (88%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProject/Remove-xAzDoProject.tests.ps1 => AzDoProject/Remove-AzDoProject.tests.ps1} (97%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProject/Set-xAzDoProject.tests.ps1 => AzDoProject/Set-AzDoProject.tests.ps1} (92%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProject/Test-xAzDoProject.tests.ps1 => AzDoProject/Test-AzDoProject.tests.ps1} (96%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 => AzDoProjectGroup/Get-AzDoProjectGroup.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 => AzDoProjectGroup/New-AzDoProjectGroup.tests.ps1} (97%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 => AzDoProjectGroup/Remove-AzDoProjectGroup.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 => AzDoProjectGroup/Set-AzDoProjectGroup.tests.ps1} (98%) rename tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/{xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 => AzDoProjectGroup/Test-AzDoProjectGroup.tests.ps1} (97%) create mode 100644 tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.tests.ps1 delete mode 100644 tests/Unit/Modules/AzureDevOpsDsc/AzureDevOpsDsc.DscClassResources.Tests.ps1 diff --git a/.gitattributes b/.gitattributes index d49b050c9..9f0fb1f3b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,4 @@ *.jpg binary *.xl* binary *.pfx binary +coverage.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index d5fb5979e..d615419d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - AzureDevOpsDsc - - Azure Managed Identity supporting classes. These classes are used by 'AzureDevOpsDsc.Common'. - Updated pipeline files to support change of default branch to main. - Added GitHub issue templates and pull request template ([issue #1](https://github.com/dsccommunity/AzureDevOpsDsc/issues/1)) @@ -23,16 +22,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 'prefix.ps1' ([issue #12](https://github.com/dsccommunity/AzureDevOpsDsc/issues/12)). - Added pipeline support for publish markdown content to the GitHub repository wiki ([issue #15](https://github.com/dsccommunity/AzureDevOpsDsc/issues/15)). - This will publish the markdown documentation that is generated bu the - build pipeline. + This will publish the markdown documentation that is generated by the build pipeline. - Added new source folder `WikiSource`. Every markdown file in the folder `WikiSource` will be published to the GitHub repository wiki. The markdown file `Home.md` will be updated with the correct module version on each publish to gallery (including preview). +- Added Resources: + - AzDoGroupPermission + - AzDoOrganizationGroup + - AzDoProjectGroup + - AzDoGroupMember + - AzDoGitRepository + - AzDoGitPermission - AzureDevOpsDsc.Common - - Managed Identity has been added to the system. This feature can be used before invoking Invoke-DSCResource. With New-AzManagedIdentity, the bearer token is automatically authenticated, retrieved, and managed. It's worth noting that bearer tokens take precedence over basic tokens. When using Invoke-AzDevOpsApiRestMethod, the token is automatically interpolated as required. + - Added New-AzDoAuthenticationProvider. This is invoked prior to the resource invocation. - Added 'wrapper' functionality around the [Azure DevOps REST API](https://docs.microsoft.com/en-us/rest/api/azure/devops/) - Added Supporting Functions for Azure Managed Identity. +- Added Unit Testing to AzureDevOpsDsc.Common + ### Changed @@ -42,12 +49,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 for more information). - Updated pipeline file `RequiredModules.ps1` to latest pipeline pattern. - Updated pipeline file `build.yaml` to latest pipeline pattern. + - Enhanced Authentication Mechanisms. + The classes have been refactored to accommodate a variety of authentication methods. + This refactoring allows the system to support multiple authentication + protocols, enhancing security and providing flexibility in integrating with + different identity providers. + - Added LookupResult Property to classes. A new property, LookupResult, + has been introduced to the classes. This addition enables the classes to + efficiently store and retrieve lookup results, improving data handling + capabilities and streamlining processes that depend on quick access + to these results. + - Added [DSCGetSummaryState] class. : Introduced an additional class, + [DSCGetSummaryState], which serves to represent the changes that have been detected. + - The Get() and Test() methods have undergone a redesign. + The Get-* commands now efficiently retrieve and identify complex changes, + which are then depicted within the [DSCGetSummaryState] class. - AzDevOpsProject - Added a validate set to the parameter `SourceControlType` to (for now) limit the parameter to the values `Git` and `Tfvc`. - Update comment-based help to remove text which the valid values are since that is now add automatically to the documentation (conceptual help and wiki documentation). +- Update build.yaml tests reference: + - Added: ./azuredevopsdsc.common.tests.ps1 + - Added: ./azuredevopsdsc.tests.ps1 ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2697e8361..25ce14ea7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -488,14 +488,14 @@ Function Get-AzDoProjectServices { # # Attempt to retrive the Project from the Live Cache. - Write-Verbose "[Get-xAzDevOpsProjectServices] Retriving the Project from the Live Cache." + Write-Verbose "[Get-AzDevOpsProjectServices] Retriving the Project from the Live Cache." # Retrive the Repositories from the Live Cache. $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' # If the Project does not exist in the Live Cache, return the Project object. if ($null -eq $Project) { - Write-Warning "[Get-xAzDevOpsProjectServices] The Project '$ProjectName' was not found in the Live Cache." + Write-Warning "[Get-AzDevOpsProjectServices] The Project '$ProjectName' was not found in the Live Cache." $Result.Status = [DSCGetSummaryState]::NotFound return $Result } diff --git a/azuredevopsdsc.common.tests.ps1 b/azuredevopsdsc.common.tests.ps1 new file mode 100644 index 000000000..782a3db3c --- /dev/null +++ b/azuredevopsdsc.common.tests.ps1 @@ -0,0 +1,43 @@ +[CmdletBinding()] +param ( + [Parameter()] + [switch] + $LoadModulesOnly +) + +# Unload the $Global:RepositoryRoot and $Global:TestPaths variables +Remove-Variable -Name RepositoryRoot -Scope Global -ErrorAction SilentlyContinue + +# Set the $Global:RepositoryRoot and $Global:TestPaths variables +$Global:RepositoryRoot = $PSScriptRoot + +Import-Module -Name (Join-Path -Path $Global:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestCases.psm1') +Import-Module -Name (Join-Path -Path $Global:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1') +Import-Module -Name (Join-Path -Path $Global:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1') + +if ($LoadModulesOnly.IsPresent) +{ + return +} + +$config = New-PesterConfiguration + +$config.Run.Path = ".\tests\Unit\Modules\AzureDevOpsDsc.Common" +$config.Output.CIFormat = "GitHubActions" +#$config.Output.Verbosity = "Detailed" +$config.CodeCoverage.Enabled = $true +$config.CodeCoverage.Path = @( + '.\source\Modules\AzureDevOpsDsc.Common\Api', + '.\source\Modules\AzureDevOpsDsc.Common\Connection', + '.\source\Modules\AzureDevOpsDsc.Common\en-US', + '.\source\Modules\AzureDevOpsDsc.Common\LocalizedData', + '.\source\Modules\AzureDevOpsDsc.Common\Resources', + '.\source\Modules\AzureDevOpsDsc.Common\Services' + ) +$config.CodeCoverage.OutputFormat = 'CoverageGutters' +$config.CodeCoverage.OutputPath = ".\output\AzureDevOpsDsc.Common.codeCoverage.xml" +$config.CodeCoverage.OutputEncoding = 'utf8' + +# Get the path to the function being tested + +Invoke-Pester -Configuration $config diff --git a/azuredevopsdsc.tests.ps1 b/azuredevopsdsc.tests.ps1 new file mode 100644 index 000000000..2c85f574b --- /dev/null +++ b/azuredevopsdsc.tests.ps1 @@ -0,0 +1,87 @@ +[CmdletBinding()] +param ( + [Parameter()] + [switch] + $LoadModulesOnly +) + +# Unload the $Global:RepositoryRoot and $Global:TestPaths variables +Remove-Variable -Name RepositoryRoot -Scope Global -ErrorAction SilentlyContinue + +# Set the $Global:RepositoryRoot and $Global:TestPaths variables +$Global:RepositoryRoot = $PSScriptRoot +$ClassesDirectory = "$Global:RepositoryRoot\source\Classes" +$EnumsDirectory = "$Global:RepositoryRoot\source\Enum" +$PublicDirectory = "$Global:RepositoryRoot\source\Modules\AzureDevOpsDsc.Common\Resources\Functions\Public" +$Global:ClassesLoaded = $true + +# +# Load the Helper Modules +Import-Module -Name (Join-Path -Path $Global:RepositoryRoot -ChildPath 'tests\Unit\Modules\TestHelpers\CommonTestFunctions.psm1') + + +# +# Load all the Enums + +Get-ChildItem -LiteralPath $EnumsDirectory -File | ForEach-Object { + Write-Verbose "Dot Sourcing $($_.FullName)" + . $_.FullName +} + +# +# Load all the Classes + +Get-ChildItem -LiteralPath $ClassesDirectory -File | ForEach-Object { + + Write-Verbose "Dot Sourcing $($_.FullName)" + # Read the file and remove [DscResource()] attribute + $file = Get-Command $_.FullName + # Remove [DscResource()] attribute + $content = $file.ScriptContents -replace '\[DscResource\(\)\]', '' + # Convert the string array into ScriptBlock + $scriptBlock = [ScriptBlock]::Create($content) + # Dot source the script block + . $scriptBlock + +} + +# Load all the Helper Functions from the AzureDevOpsDsc.Common Module into Memory +Get-ChildItem -LiteralPath "$($Global:RepositoryRoot)\source\Modules\AzureDevOpsDsc.Common\Api\Functions\Private\Helper" -File -Recurse -Filter *.ps1 | ForEach-Object { + Write-Verbose "Dot Sourcing $($_.FullName)" + . $_.FullName +} + +# Load all the Public Functions from the AzureDevOpsDsc.Common Module into Memory +Get-ChildItem -LiteralPath $PublicDirectory -File -Recurse -Filter *.ps1 | ForEach-Object { + Write-Verbose "Dot Sourcing $($_.FullName)" + . $_.FullName +} + + +if ($LoadModulesOnly.IsPresent) +{ + return +} + +$config = New-PesterConfiguration + +$config.Run.Path = ".\tests\Unit\Classes" +$config.Output.CIFormat = "GitHubActions" +#$config.Output.Verbosity = "Detailed" +$config.CodeCoverage.Enabled = $true +$config.CodeCoverage.Path = @( + '.\source\Classes\' + ) +$config.CodeCoverage.OutputFormat = 'CoverageGutters' +$config.CodeCoverage.OutputPath = ".\output\AzureDevOpsDsc.codeCoverage.xml" +$config.CodeCoverage.OutputEncoding = 'utf8' + +# Get the path to the function being tested + +Invoke-Pester -Configuration $config + + + + + + diff --git a/build.yaml b/build.yaml index 46731b217..d725e206f 100644 --- a/build.yaml +++ b/build.yaml @@ -64,7 +64,8 @@ Pester: - Modules/DscResource.Common Script: # Only run on unit test on './build.ps1 -Task test' - - tests/Unit + - ./azuredevopsdsc.common.tests.ps1 + - ./azuredevopsdsc.tests.ps1 ExcludeTag: Tag: CodeCoverageThreshold: 40 diff --git a/source/AzureDevOpsDsc.psd1 b/source/AzureDevOpsDsc.psd1 index bbd9f6392..ed1639871 100644 --- a/source/AzureDevOpsDsc.psd1 +++ b/source/AzureDevOpsDsc.psd1 @@ -41,7 +41,7 @@ NestedModules = @() DscResourcesToExport = @( - 'xAzDevOpsProject', + 'AzDevOpsProject', 'AzDoOrganizationGroup', 'AzDoProjectGroup' ) diff --git a/source/Classes/004.DscResourceBase.ps1 b/source/Classes/004.DscResourceBase.ps1 index 0da01e030..ad34d3ffe 100644 --- a/source/Classes/004.DscResourceBase.ps1 +++ b/source/Classes/004.DscResourceBase.ps1 @@ -11,7 +11,7 @@ class DscResourceBase if ([String]::IsNullOrWhiteSpace($dscResourceKeyPropertyName)) { $errorMessage = "Cannot obtain a 'DscResourceKey' value for the '$($this.GetType().Name)' instance." - New-InvalidOperationException -Message $errorMessage + throw (New-InvalidOperationException -Message $errorMessage) } return $this."$dscResourceKeyPropertyName" @@ -42,13 +42,13 @@ class DscResourceBase if ($null -eq $dscResourceKeyPropertyNames -or $dscResourceKeyPropertyNames.Count -eq 0) { $errorMessage = "Could not obtain a 'DscResourceDscKey' property for type '$($this.GetType().Name)'." - New-InvalidOperationException -Message $errorMessage + throw (New-InvalidOperationException -Message $errorMessage) } elseif ($dscResourceKeyPropertyNames.Count -gt 1) { $errorMessage = "Obtained more than 1 property for type '$($this.GetType().Name)' that was marked as a 'Key'. There must only be 1 property on the class set as the 'Key' for DSC." - New-InvalidOperationException -Message $errorMessage + throw (New-InvalidOperationException -Message $errorMessage) } return $dscResourceKeyPropertyNames[0] diff --git a/source/Classes/006.AzDevOpsDscResourceBase.ps1 b/source/Classes/006.AzDevOpsDscResourceBase.ps1 index d6ce90676..665b86b09 100644 --- a/source/Classes/006.AzDevOpsDscResourceBase.ps1 +++ b/source/Classes/006.AzDevOpsDscResourceBase.ps1 @@ -85,13 +85,12 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase # # Initialize the cache objects. Don't delete the cache objects since they are used by other resources. - Get-AzDoCacheObjects | ForEach-Object - { + + Get-AzDoCacheObjects | ForEach-Object { Initialize-CacheObject -CacheType $_ -BypassFileCheck -Debug Write-Verbose "[AzDevOpsDscResourceBase] Initialized cache object of type: $_" } - } hidden [Hashtable]GetDscCurrentStateObjectGetParameters() @@ -135,7 +134,11 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase } $props.LookupResult = $this.GetDscCurrentStateResourceObject($getParameters) - $props.Ensure = $props.LookupResult.Ensure + $props.Ensure = $( + if ($null -eq $props.LookupResult.Ensure) { [Ensure]::Absent } + else { $props.LookupResult.Ensure } + ) + $props.LookupResult.Ensure return $props @@ -205,40 +208,41 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase [System.String[]]$dscPropertyNamesWithNoSetSupport = $this.GetDscResourcePropertyNamesWithNoSetSupport() [System.String[]]$dscPropertyNamesToCompare = $this.GetDscResourcePropertyNames() + switch ($desiredProperties.Ensure) { ([Ensure]::Present) { Write-Verbose "Desired state is Present." - if ($currentProperties.Ensure -eq [Ensure]::Absent) + switch ($currentProperties.LookupResult.Status) { - Write-Verbose "Current state is Absent." - - switch ($currentProperties.LookupResult.Status) - { - ([DSCGetSummaryState]::NotFound) { - $dscRequiredAction = [RequiredAction]::New - Write-Verbose "Resource not found. Setting action to New." - }([DSCGetSummaryState]::Changed) { - $dscRequiredAction = [RequiredAction]::Set - Write-Verbose "Resource Changed. Setting action to Set." - }([DSCGetSummaryState]::Renamed) { - $dscRequiredAction = [RequiredAction]::Set - Write-Verbose "Resource Renamed. Setting action to Set." - } - ([DSCGetSummaryState]::Missing) { - $dscRequiredAction = [RequiredAction]::Remove - Write-Verbose "Resource missing. Setting action to Remove." - } + ([DSCGetSummaryState]::NotFound) { + $dscRequiredAction = [RequiredAction]::New + Write-Verbose "Resource not found. Setting action to New." + }([DSCGetSummaryState]::Changed) { + $dscRequiredAction = [RequiredAction]::Set + Write-Verbose "Resource Changed. Setting action to Set." + }([DSCGetSummaryState]::Renamed) { + $dscRequiredAction = [RequiredAction]::Set + Write-Verbose "Resource Renamed. Setting action to Set." + }([DSCGetSummaryState]::Missing) { + $dscRequiredAction = [RequiredAction]::Remove + Write-Verbose "Resource missing. Setting action to Remove." + }([DSCGetSummaryState]::Unchanged) { + $dscRequiredAction = [RequiredAction]::None + Write-Verbose "Resource Not Changed. Setting action to Remove." + } + default { + $errorMessage = "Could not obtain a valid 'LookupResult.Status' value within '$($this.GetResourceName())' Test() function. Value was '$($currentProperties.LookupResult.Status)'" + Write-Verbose $errorMessage + throw (New-InvalidOperationException -Message $errorMessage) } - - Write-Verbose "DscActionRequired='$dscRequiredAction'" - return $dscRequiredAction } - Write-Verbose "No changes required. Desired state already achieved." + Write-Verbose "DscActionRequired='$dscRequiredAction'" return $dscRequiredAction + } ([Ensure]::Absent) { @@ -254,7 +258,7 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase default { $errorMessage = "Could not obtain a valid 'Ensure' value within '$($this.GetResourceName())' Test() function. Value was '$($desiredProperties.Ensure)'." Write-Verbose $errorMessage - New-InvalidOperationException -Message $errorMessage + throw (New-InvalidOperationException -Message $errorMessage) } } @@ -277,14 +281,6 @@ class AzDevOpsDscResourceBase : AzDevOpsApiDscResourceBase return $desiredStateParameters - return @{ - ApiUri = $DesiredStateProperties.ApiUri - Pat = $DesiredStateProperties.Pat - Force = $true - - # Set this from the 'Current' state as we would expect this to have an existing key/ID value to use - "$IdPropertyName" = $CurrentStateProperties."$IdPropertyName" - } } # If the desired state/action is to add/new or update/set the resource, start with the values in the $DesiredStateProperties variable, and amend elseif ($RequiredAction -in @([RequiredAction]::New, [RequiredAction]::Set)) diff --git a/source/Classes/009.AzDoGroupPermission.ps1 b/source/Classes/009.AzDoGroupPermission.ps1 index e06eba722..a3b2f6a75 100644 --- a/source/Classes/009.AzDoGroupPermission.ps1 +++ b/source/Classes/009.AzDoGroupPermission.ps1 @@ -71,6 +71,10 @@ class AzDoGroupPermission : AzDevOpsDscResourceBase $this.Construct() } + AzDoGroupPermission([bool]$isTest) + { + } + [AzDoGroupPermission] Get() { return [AzDoGroupPermission]$($this.GetDscCurrentStateProperties()) diff --git a/source/Examples/Authentication/1-NewAuthenticationPAT.ps1 b/source/Examples/Authentication/1-NewAuthenticationPAT.ps1 new file mode 100644 index 000000000..e30d5228c --- /dev/null +++ b/source/Examples/Authentication/1-NewAuthenticationPAT.ps1 @@ -0,0 +1,10 @@ +<# + .DESCRIPTION + This example shows how to authenticate with Azure DevOps using a Personal Access Token (PAT). +#> + +# Using New-AzDoAuthenticationProvider +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +# Using New-AzDoAuthenticationProvider +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -SecureStringPersonalAccessToken $SecureStringPAT diff --git a/source/Examples/Authentication/2-NewAuthenticationManagedIdentity.ps1 b/source/Examples/Authentication/2-NewAuthenticationManagedIdentity.ps1 new file mode 100644 index 000000000..b5cc0f777 --- /dev/null +++ b/source/Examples/Authentication/2-NewAuthenticationManagedIdentity.ps1 @@ -0,0 +1,8 @@ +<# + .DESCRIPTION + This example shows how to authenticate with Azure DevOps using a Managed Identity. +#> + +# Using New-AzDoAuthenticationProvider +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + diff --git a/source/Examples/Resources/AzDevOpsProject/1-AddProject.ps1 b/source/Examples/Resources/AzDevOpsProject/1-AddProject.ps1 index 2e7bc7eba..db2ce88e1 100644 --- a/source/Examples/Resources/AzDevOpsProject/1-AddProject.ps1 +++ b/source/Examples/Resources/AzDevOpsProject/1-AddProject.ps1 @@ -5,18 +5,11 @@ called 'Test Project' exists (or is added if it does not exist). #> +# Refer to Authentication\1-NewAuthenticationPAT.ps1 for the New-AzDoAuthenticationProvider command +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + Configuration Example { - param - ( - [Parameter(Mandatory = $true)] - [string] - $ApiUri, - - [Parameter(Mandatory = $true)] - [string] - $Pat - ) Import-DscResource -ModuleName 'AzureDevOpsDsc' @@ -25,13 +18,8 @@ Configuration Example AzDevOpsProject 'AddProject' { Ensure = 'Present' - - ApiUri = $ApiUri - Pat = $Pat - ProjectName = 'Test Project' ProjectDescription = 'A Test Project' - SourceControlType = 'Git' } diff --git a/source/Examples/Resources/AzDevOpsProject/2-UpdateProject.ps1 b/source/Examples/Resources/AzDevOpsProject/2-UpdateProject.ps1 index 41b201032..4fd713aea 100644 --- a/source/Examples/Resources/AzDevOpsProject/2-UpdateProject.ps1 +++ b/source/Examples/Resources/AzDevOpsProject/2-UpdateProject.ps1 @@ -6,18 +6,11 @@ updated to 'A Test Project with a new description'. #> +# Refer to Authentication\1-NewAuthenticationPAT.ps1 for the New-AzDoAuthenticationProvider command +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + Configuration Example { - param - ( - [Parameter(Mandatory = $true)] - [string] - $ApiUri, - - [Parameter(Mandatory = $true)] - [string] - $Pat - ) Import-DscResource -ModuleName 'AzureDevOpsDsc' @@ -26,14 +19,8 @@ Configuration Example AzDevOpsProject 'UpdateProject' { Ensure = 'Present' - - ApiUri = $ApiUri - Pat = $Pat - ProjectName = 'Test Project' ProjectDescription = 'A Test Project with a new description' # Updated property - - #SourceControlType = 'Git' # Note: Update of this property is not supported } diff --git a/source/Examples/Resources/AzDevOpsProject/3-DeleteProject.ps1 b/source/Examples/Resources/AzDevOpsProject/3-DeleteProject.ps1 index 890bbdc2c..83553347a 100644 --- a/source/Examples/Resources/AzDevOpsProject/3-DeleteProject.ps1 +++ b/source/Examples/Resources/AzDevOpsProject/3-DeleteProject.ps1 @@ -4,18 +4,11 @@ This example shows how to delete a project called 'Test Project'. #> +# Refer to Authentication\1-NewAuthenticationPAT.ps1 for the New-AzDoAuthenticationProvider command +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + Configuration Example { - param - ( - [Parameter(Mandatory = $true)] - [string] - $ApiUri, - - [Parameter(Mandatory = $true)] - [string] - $Pat - ) Import-DscResource -ModuleName 'AzureDevOpsDsc' @@ -25,10 +18,6 @@ Configuration Example AzDevOpsProject 'DeleteProject' { Ensure = 'Absent' # 'Absent' ensures this will be removed/deleted - - ApiUri = $ApiUri - Pat = $Pat - ProjectName = 'Test Project' # Identifies the name of the project to be deleted } diff --git a/source/Examples/Resources/AzDevOpsProject/4-AddProject-UsingManaged-Identity.ps1 b/source/Examples/Resources/AzDevOpsProject/4-AddProject-UsingManaged-Identity.ps1 deleted file mode 100644 index 3e140392d..000000000 --- a/source/Examples/Resources/AzDevOpsProject/4-AddProject-UsingManaged-Identity.ps1 +++ /dev/null @@ -1,43 +0,0 @@ - -<# - .DESCRIPTION - This example shows how to ensure that the Azure DevOps project - called 'Test Project' exists (or is added if it does not exist). - This example uses Invoke-DSCResource to authenticate to Azure DevOps using a Managed Identity. -#> - -Configuration Example -{ - param - ( - [Parameter(Mandatory = $true)] - [string] - $ApiUri - ) - - Import-DscResource -ModuleName 'AzureDevOpsDsc' - - node localhost - { - AzDevOpsProject 'AddProject' - { - Ensure = 'Present' - - ApiUri = $ApiUri - Pat = $Pat - - ProjectName = 'Test Project' - ProjectDescription = 'A Test Project' - - SourceControlType = 'Git' - } - - } -} - -# Create a new Azure Managed Identity and store the token in a global variable -New-AzManagedIdentity -OrganizationName "Contoso" - -# Using Invoke-DSCResource, invoke the 'Test' method of the 'AzDevOpsProject' resource. -# The global variable will be used to authenticate to Azure DevOps. -Invoke-DscResource -Name Example -Method Test -Verbose diff --git a/source/Examples/Resources/AzDoGitPermission.md b/source/Examples/Resources/AzDoGitPermission.md new file mode 100644 index 000000000..a87b621de --- /dev/null +++ b/source/Examples/Resources/AzDoGitPermission.md @@ -0,0 +1,195 @@ +# DSC AzDoGitPermission Resource + +# Syntax + +``` PowerShell +AzDoGitPermission [string] #ResourceName +{ + ProjectName = [String]$ProjectName + RepositoryName = [String]$RepositoryName + Permissions = [HashTable]$Permissions # See Permissions Syntax + [ Ensure = [String] {'Present', 'Absent'}] +} +``` + +## Permissions Syntax + +``` PowerShell +AzDoGitPermission/Permissions +{ + Identity = [String]$Identity # Syntax + # SYNTAX: '[ProjectName | OrganizationName]\ServicePrincipalName, UserPrincipalName, UserDisplayName, GroupDisplayName' + # EXAMPLE: '[TestProject]\UserName@email.com' + # EXAMPLE: '[SampleOrganizationName]\Project Collection Administrators' + Permission = [Hashtable[]]$Permissions # See 'Permission List" +} +``` + +## Permission Usage + +``` PowerShell +AzDoGitPermission/Permissions/Permission +{ + PermissionName|PermissionDisplayName = [String]$Name { 'Allow, Deny' } +} + +``` + +## Permission List + +> Either 'Name' or 'DisplayName' can be used + +| Name | DisplayName | Values | Note | +| ------------- | ------------- | - | - | +|Administer | Administer | [ allow, deny ] | Not recommended. | +|GenericRead | Read | [ allow, deny ] | | +|GenericContribute | Contribute | [ allow, deny ] | | +|ForcePush | Force push (rewrite history, delete branches and tags) | [ allow, deny ] | | +|CreateBranch | Create branch |[ allow, deny ] | | +|CreateTag | Create tag | [ allow, deny ] | | +|ManageNote | Manage notes | [ allow, deny ] | | +|PolicyExempt | Bypass policies when pushing | [ allow, deny ] | | +|CreateRepository | Create repository | [ allow, deny ] | | +|DeleteRepository | Delete or disable repository | [ allow, deny ] | | +|RenameRepository | Rename repository | [ allow, deny ] | | +|EditPolicies | Edit policies | [ allow, deny ] | | +|RemoveOthersLocks | Remove others' locks | [ allow, deny ] | | +|ManagePermissions | Manage permissions | [ allow, deny ] | | +|PullRequestContribute | Contribute to pull requests | [ allow, deny ] | | +|PullRequestBypassPolicy | Bypass policies when completing pull requests | [ allow, deny ] | | +|ViewAdvSecAlerts | Advanced Security: view alerts | [ allow, deny ] | | +|DismissAdvSecAlerts | Advanced Security: manage and dismiss alerts | [ allow, deny ] | | +|ManageAdvSecScanning | Advanced Security: manage settings | [ allow, deny ] | | + +# Common Properties + +- __ProjectName__: The name of the Azure DevOps project. +- __RepositoryName__: The name of the Git repository within the project. +- __Permissions__: A HashTable that specifies the permissions to be set. Refer to: 'Permissions Syntax'. +- __Ensure__: Specifies whether the repository should exist. Defaults to 'Absent'. + +# Additional Information + +This resource allows you to manage Azure DevOps projects using Desired State Configuration (DSC). +It includes properties for specifying the project name, description, source control type, process template, and visibility. + +# Examples + +## Example 1: Sample Configuration using AzDoGitPermission Resource + +``` PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + AzDoGitPermission GitPermission { + Ensure = 'Present' + ProjectName = 'SampleProject' + RepositoryName = 'SampleGitRepository' + isInherited = $true + Permissions = @( + @{ + Identity = '[ProjectName]\GroupName' + Permissions = @{ + Read = 'Allow' + "Manage Notes" = 'Allow' + "Contribute" = 'Deny' + } + } + ) + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose + +``` + +## Example 2: Sample Configuration using Invoke-DSCResource + +``` PowerShell +# Return the current configuration for AzDoGitPermission +# Ensure is not required +$properties = @{ + ProjectName = 'SampleProject' + RepositoryName = 'SampleGitRepository' + isInherited = $true + Permissions = @( + @{ + Identity = '[ProjectName]\GroupName' + Permissions = @{ + Read = 'Allow' + "Manage Notes" = 'Allow' + "Contribute" = 'Deny' + } + } + ) +} + +Invoke-DSCResource -Name 'AzDoGitPermission' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Example 3: Sample Configuration to clear permissions for an identity within a group + +``` PowerShell +# Remove all group members from the group. +$properties = @{ + ProjectName = 'SampleProject' + RepositoryName = 'SampleGitRepository' + isInherited = $true + Permissions = @( + @{ + Identity = '[ProjectName]\GroupName' + Permissions = @{} + } + ) +} + +Invoke-DSCResource -Name 'AzDoGitPermission' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Example 4: Sample Configuration using AzDO-DSC-LCM + +``` YAML +parameters: {} + +variables: { + ProjectName: SampleProject, + RepositoryName: SampleRepository +} + +resources: + + - name: SampleGroup Permissions + type: AzureDevOpsDsc/AzDoGitPermission + dependsOn: + - AzureDevOpsDsc/AzDoProjectGroup/SampleGroupReadAccess + properties: + projectName: $ProjectName + RepositoryName: $RepositoryName + isInherited: false + Permissions: + - Identity: '[$ProjectName]\SampleGroupReadAccess' + Permission: + Read: "Allow" + "Manage notes": "Allow" +``` + +LCM Initialization: + +``` PowerShell + +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +Invoke-AzDoLCM @params + +``` diff --git a/source/Examples/Resources/AzDoGitPermission/1-AddGitPermission.ps1 b/source/Examples/Resources/AzDoGitPermission/1-AddGitPermission.ps1 new file mode 100644 index 000000000..6ef82d744 --- /dev/null +++ b/source/Examples/Resources/AzDoGitPermission/1-AddGitPermission.ps1 @@ -0,0 +1,39 @@ +<# + .DESCRIPTION + This example shows how to ensure that the Git repository permissions. +#> + +# Refer to Authentication\1-NewAuthenticationPAT.ps1 for the New-AzDoAuthenticationProvider command +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoGitPermission 'AddGitPermission' + { + Ensure = 'Present' + ProjectName = 'Test Project' + RepositoryName = 'Test Repository' + isInherited = $true + Permissions = @( + @{ + Identity = '[Project]\Contributors' + Permission = @{ + read = 'allow' + contribute = 'allow' + } + }, + @{ + Identity = '[Project]\Readers' + Permission = @{ + read = 'allow' + } + } + ) + } + } +} diff --git a/source/Examples/Resources/AzDoGitPermission/2-UpdateGitPermission.ps1 b/source/Examples/Resources/AzDoGitPermission/2-UpdateGitPermission.ps1 new file mode 100644 index 000000000..d8e4771ef --- /dev/null +++ b/source/Examples/Resources/AzDoGitPermission/2-UpdateGitPermission.ps1 @@ -0,0 +1,39 @@ +<# + .DESCRIPTION + This example shows how to update the Git repository permissions. +#> + +# Refer to Authentication\1-NewAuthenticationPAT.ps1 for the New-AzDoAuthenticationProvider command +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoGitPermission 'UpdateGitPermission' + { + Ensure = 'Present' + ProjectName = 'Test Project' + RepositoryName = 'Test Repository' + isInherited = $true + Permissions = @( + @{ + Identity = '[Project]\Contributors' + Permission = @{ + read = 'allow' + contribute = 'deny' + } + }, + @{ + Identity = '[Project]\Readers' + Permission = @{ + read = 'deny' + } + } + ) + } + } +} diff --git a/source/Examples/Resources/AzDoGitPermission/3-DeleteAzDoGitPermission.ps1 b/source/Examples/Resources/AzDoGitPermission/3-DeleteAzDoGitPermission.ps1 new file mode 100644 index 000000000..374a5daa9 --- /dev/null +++ b/source/Examples/Resources/AzDoGitPermission/3-DeleteAzDoGitPermission.ps1 @@ -0,0 +1,27 @@ +<# + .DESCRIPTION + This example shows how to remove Git repository permissions. +#> + +# Refer to Authentication\1-NewAuthenticationPAT.ps1 for the New-AzDoAuthenticationProvider command +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoGitPermission 'DeleteGitPermission' + { + Ensure = 'Present' + ProjectName = 'Test Project' + RepositoryName = 'Test Repository' + isInherited = $true + # Note: Permissions can be empty to remove all permissions + # Ensure = 'Absent' is not required. + Permissions = @() + } + } +} diff --git a/source/Examples/Resources/AzDoGitRepository.md b/source/Examples/Resources/AzDoGitRepository.md new file mode 100644 index 000000000..2dbe24069 --- /dev/null +++ b/source/Examples/Resources/AzDoGitRepository.md @@ -0,0 +1,83 @@ +# DSC AzDoGitRepository Resource + +## Syntax + +```PowerShell +AzDoGitRepository [string] #ResourceName +{ + ProjectName = [String]$ProjectName + RepositoryName = [String]$RepositoryName + [ SourceRepository = [String]$SourceRepository ] + [ Ensure = [String] {'Present', 'Absent'}] +} +``` + +## Permissions Syntax + +This resource does not directly manage permissions. It focuses on managing Git repositories within an Azure DevOps project. + +## Permission Usage + +Not applicable for this resource. + +## Permission List + +Not applicable for this resource. + +## Common Properties + +- __Ensure__: Specifies whether the repository should exist. Defaults to 'Absent'. +- __ProjectName__: The name of the Azure DevOps project. +- __RepositoryName__: The name of the Git repository within the project. +- __SourceRepository__: (Optional) The source repository from which to create the new repository. + +## Additional Information + +This resource allows you to manage Git repositories in Azure DevOps projects using Desired State Configuration (DSC). It includes properties for specifying the project name, repository name, and optionally a source repository. + +## Examples + +### Example 1: Create a Git Repository + +```PowerShell +Configuration Sample_AzDoGitRepository +{ + Import-DscResource -ModuleName AzDevOpsDsc + + Node localhost + { + AzDoGitRepository MyRepository + { + ProjectName = 'MySampleProject' + RepositoryName = 'MySampleRepository' + SourceRepository = 'TemplateRepository' + Ensure = 'Present' + } + } +} + +Sample_AzDoGitRepository -OutputPath 'C:\DSC\' +Start-DscConfiguration -Path 'C:\DSC\' -Wait -Verbose -Force +``` + +### Example 2: Remove a Git Repository + +```PowerShell +Configuration Remove_AzDoGitRepository +{ + Import-DscResource -ModuleName AzDevOpsDsc + + Node localhost + { + AzDoGitRepository MyRepository + { + ProjectName = 'MySampleProject' + RepositoryName = 'MySampleRepository' + Ensure = 'Absent' + } + } +} + +Remove_AzDoGitRepository -OutputPath 'C:\DSC\' +Start-DscConfiguration -Path 'C:\DSC\' -Wait -Verbose -Force +``` diff --git a/source/Examples/Resources/AzDoGitRepository/1-AddAzDoGitRepository.ps1 b/source/Examples/Resources/AzDoGitRepository/1-AddAzDoGitRepository.ps1 new file mode 100644 index 000000000..6ad778863 --- /dev/null +++ b/source/Examples/Resources/AzDoGitRepository/1-AddAzDoGitRepository.ps1 @@ -0,0 +1,22 @@ +<# + .DESCRIPTION + This example shows how to add the Git Repository +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoGitRepository 'AddGitRepository' + { + Ensure = 'Present' + ProjectName = 'Test Project' + RepositoryName = 'Test Repository' + } + } +} diff --git a/source/Examples/Resources/AzDoGitRepository/2-UpdateAzDoGitRepository.ps1 b/source/Examples/Resources/AzDoGitRepository/2-UpdateAzDoGitRepository.ps1 new file mode 100644 index 000000000..24c6b5970 --- /dev/null +++ b/source/Examples/Resources/AzDoGitRepository/2-UpdateAzDoGitRepository.ps1 @@ -0,0 +1,6 @@ +<# + .DESCRIPTION + This example shows how to update the Git Repository +#> + +# Not Supported diff --git a/source/Examples/Resources/AzDoGitRepository/3-DeleteAzDoGitRepository.ps1 b/source/Examples/Resources/AzDoGitRepository/3-DeleteAzDoGitRepository.ps1 new file mode 100644 index 000000000..d05508d1c --- /dev/null +++ b/source/Examples/Resources/AzDoGitRepository/3-DeleteAzDoGitRepository.ps1 @@ -0,0 +1,22 @@ +<# + .DESCRIPTION + This example shows how to remove a Git Repository. +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoGitRepository 'RemoveGitRepository' + { + Ensure = 'Absent' + ProjectName = 'Test Project' + RepositoryName = 'Test Repository' + } + } +} diff --git a/source/Examples/Resources/AzDoGroupMember.md b/source/Examples/Resources/AzDoGroupMember.md new file mode 100644 index 000000000..15c1f7172 --- /dev/null +++ b/source/Examples/Resources/AzDoGroupMember.md @@ -0,0 +1,127 @@ +# DSC AzDoGroupMember Resource + +## Syntax + +```PowerShell +AzDoGroupMember [string] #ResourceName +{ + GroupName = [String]$GroupName # [ProjectName|OrganizationName]\GroupName + # For GroupMember Syntax, refer to # GroupMembers Syntax + [ GroupMembers = [String[]]$GroupMembers ] +} +``` + +### GroupMembers Syntax + +``` PowerShell +{ + GroupMember = [String]$GroupMemberName # [ProjectName|OrganizationName]\GroupName +} +``` + +The following string represents the service accounts for the project collection in Azure DevOps Organization: + +```text +[ProjectName|AZDOOrganizationName]\Project Collection Service Accounts +``` + +- __[ProjectName|AZDOOrganizationName]__: The AzDO Project or Organizational Name. +- __Project Collection Service Accounts__: The Group Member Name. This can be a Group Name, Service Principal Name or Service Principle. + +#### Example + +If your Azure DevOps Organization name is `MyOrg`, the string would look like: + +```text +[MyOrg]\Project Collection Service Accounts +``` + +## Properties + +Common Properties: + +- __GroupName__: The name of the Azure DevOps group. +- __GroupMembers__: An array of members to be included in the Azure DevOps group. + +## Additional Information + +This resource is used to manage Azure DevOps group memberships using Desired State Configuration (DSC). It allows you to define the properties of an Azure DevOps group and ensures that the group is configured according to those properties. + +## Examples + +### Example 1: Sample Configuration for Azure DevOps Group using AzDoGroupMember Resource + +```PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + AzDoGroupMember GroupExample { + GroupName = 'MySampleGroup' + GroupMembers = @('user1@example.com', 'user2@example.com') + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose +``` + +### Example 2: Sample Configuration for Azure DevOps Group using Invoke-DSCResource + +```PowerShell +# Return the current configuration for AzDoGroupMember +$properties = @{ + GroupName = 'MySampleGroup' + GroupMembers = @('user1@example.com', 'user2@example.com') +} + +Invoke-DSCResource -Name 'AzDoGroupMember' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +### Example 3: Sample Configuration to remove/exclude an Azure DevOps Group using Invoke-DSCResource + +```PowerShell +# Remove the Azure DevOps Group and ensure that it is not recreated. +$properties = @{ + GroupName = 'MySampleGroup' + Ensure = 'Absent' +} + +Invoke-DSCResource -Name 'AzDoGroupMember' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +### Example 4: Sample Configuration using AzDO-DSC-LCM + +```YAML +parameters: {} + +variables: { + GroupName: SampleGroup, + GroupMembers: ['user1@example.com', 'user2@example.com'] +} + +resources: + + - name: Group + type: AzureDevOpsDsc/AzDoGroupMember + properties: + groupName: $GroupName + groupMembers: $GroupMembers +``` + +## LCM Initialization + +```PowerShell +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +Invoke-AzDoLCM @params +``` diff --git a/source/Examples/Resources/AzDoGroupMember/1-AddAzDoGroupMember.ps1 b/source/Examples/Resources/AzDoGroupMember/1-AddAzDoGroupMember.ps1 new file mode 100644 index 000000000..ac5bb23e8 --- /dev/null +++ b/source/Examples/Resources/AzDoGroupMember/1-AddAzDoGroupMember.ps1 @@ -0,0 +1,25 @@ +<# + .DESCRIPTION + This example shows how to add groups to a membership. +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoGroupMember 'AddAzDoGroupMember' + { + Ensure = 'Present' + GroupName = '[ProjectName|OrganizationName]\GroupName' + GroupMembers = @( + '[Project]\Readers' + '[OrganizationName]\Project Collection Administrators' + ) + } + } +} diff --git a/source/Examples/Resources/AzDoGroupMember/2-UpdateAzDoGroupMember.ps1 b/source/Examples/Resources/AzDoGroupMember/2-UpdateAzDoGroupMember.ps1 new file mode 100644 index 000000000..1ce07d2ea --- /dev/null +++ b/source/Examples/Resources/AzDoGroupMember/2-UpdateAzDoGroupMember.ps1 @@ -0,0 +1,25 @@ +<# + .DESCRIPTION + TThis example shows how to update the group membership. +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoGroupMember 'UpdateAzDoGroupMember' + { + Ensure = 'Present' + GroupName = '[ProjectName|OrganizationName]\GroupName' + GroupMembers = @( + '[Project]\New Group Name' + '[OrganizationName]\Project Collection Administrators' + ) + } + } +} diff --git a/source/Examples/Resources/AzDoGroupMember/3-DeleteAzDoGroupMember.ps1 b/source/Examples/Resources/AzDoGroupMember/3-DeleteAzDoGroupMember.ps1 new file mode 100644 index 000000000..f725538e9 --- /dev/null +++ b/source/Examples/Resources/AzDoGroupMember/3-DeleteAzDoGroupMember.ps1 @@ -0,0 +1,23 @@ +<# + .DESCRIPTION + This example shows how to remove a group membership. +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoGroupMember 'RemoveAzDoGroupMember' + { + Ensure = 'Present' + GroupName = '[ProjectName|OrganizationName]\GroupName' + # Ensure: Absent is not required. Zeroing out the membership is sufficent. + GroupMembers = @() + } + } +} diff --git a/source/Examples/Resources/AzDoGroupPermission.md b/source/Examples/Resources/AzDoGroupPermission.md new file mode 100644 index 000000000..990575577 --- /dev/null +++ b/source/Examples/Resources/AzDoGroupPermission.md @@ -0,0 +1,147 @@ +# AzDoGroupPermission Resource Documentation (Currently Disabled) + +## Overview + +The `AzDoGroupPermission` resource is part of the Azure DevOps Desired State Configuration (DSC) module. It allows you to manage group permissions within an Azure DevOps project repository. This resource provides properties for specifying the group name, permission inheritance, and a list of permissions to be set. + +## Syntax + +```PowerShell +AzDoGroupPermission [string] #ResourceName +{ + GroupName = [String]$GroupName + [ isInherited = [Boolean]$isInherited ] + [ Permissions = [HashTable[]]$Permissions ] +} +``` + +### Properties + +- **GroupName**: The name of the Azure DevOps group. This property is mandatory. +- **isInherited**: Specifies whether the permissions should be inherited. Defaults to `$true`. +- **Permissions**: A HashTable array that specifies the permissions to be set for the group. Refer to the 'Permissions Syntax' section below. + +## Permissions Syntax + +```PowerShell +AzDoGroupPermission/Permissions +{ + Identity = [String]$Identity + # SYNTAX: '[ProjectName | OrganizationName]\ServicePrincipalName, UserPrincipalName, UserDisplayName, GroupDisplayName' + # ALTERNATIVE SYNTAX: 'this' Referring to the group. + # EXAMPLE: '[TestProject]\UserName@email.com' + # EXAMPLE: '[SampleOrganizationName]\Project Collection Administrators' + Permission = [Hashtable[]]$Permissions +} +``` + +### Permission Usage + +```PowerShell +AzDoGroupPermission/Permissions/Permission +{ + PermissionName|PermissionDisplayName = [String]$Name { 'Allow, Deny' } +} +``` + +### Permission List + +Either 'Name' or 'DisplayName' can be used: + +| Name | DisplayName | Values | Note | +|-------------------------|------------------------------------------------------|-----------------|------------------| +| Read | View identity information | [ allow, deny ] | | +| Write | Edit identity information | [ allow, deny ] | | +| Delete | Delete identity information | [ allow, deny ] | | +| ManageMembership | Manage group membership | [ allow, deny ] | | +| CreateScope | Create identity scopes | [ allow, deny ] | | +| RestoreScope | Restore identity scopes | [ allow, deny ] | | + +## Examples + +### Example 1: Set Group Permissions + +```PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + AzDoGroupPermission GroupPermission { + GroupName = 'SampleGroup' + isInherited = $true + Permissions = @( + @{ + Identity = '[SampleProject]\SampleGroup' + Permissions = @{ + "Read" = 'Allow' + "Write" = 'Allow' + "Delete" = 'Deny' + } + } + ) + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose +``` + +### Example 2: Clear Group Permissions + +```PowerShell +# Remove all permissions from the group. +$properties = @{ + GroupName = 'SampleGroup' + isInherited = $true + Permissions = @( + @{ + Identity = '[SampleProject]\SampleGroup' + Permissions = @{} + } + ) +} + +Invoke-DSCResource -Name 'AzDoGroupPermission' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Methods + +### Get Method + +Retrieves the current state properties of the `AzDoGroupPermission` resource. + +```PowerShell +[AzDoGroupPermission] Get() +{ + return [AzDoGroupPermission]$($this.GetDscCurrentStateProperties()) +} +``` + +### GetDscCurrentStateProperties Method + +Returns the current state properties of the resource object. + +```PowerShell +hidden [Hashtable] GetDscCurrentStateProperties([PSCustomObject]$CurrentResourceObject) +{ + $properties = @{ + Ensure = [Ensure]::Absent + } + + if ($null -eq $CurrentResourceObject) + { + return $properties + } + + $properties.GroupName = $CurrentResourceObject.GroupName + $properties.isInherited = $CurrentResourceObject.isInherited + $properties.Permissions = $CurrentResourceObject.Permissions + + Write-Verbose "[AzDoGroupPermission] Current state properties: $($properties | Out-String)" + + return $properties +} +``` + +This class inherits from the `AzDevOpsDscResourceBase` class, which provides the base functionality for DSC resources in the Azure DevOps DSC module. diff --git a/source/Examples/Resources/AzDoOrganizationGroup.md b/source/Examples/Resources/AzDoOrganizationGroup.md new file mode 100644 index 000000000..d9b4d11ca --- /dev/null +++ b/source/Examples/Resources/AzDoOrganizationGroup.md @@ -0,0 +1,96 @@ +# DSC AzDoOrganizationGroup Resource + +## Syntax + +```PowerShell +AzDoOrganizationGroup [string] #ResourceName +{ + GroupName = [String]$GroupName + [ GroupDescription = [String]$GroupDescription ] +} +``` + +## Properties + +### Common Properties + +- **GroupName**: The name of the organization group. This property is mandatory and serves as the key property for the resource. +- **GroupDescription**: A description of the organization group. + +## Additional Information + +This resource is used to manage Azure DevOps organization groups using Desired State Configuration (DSC). It allows you to define the properties of an Azure DevOps organization group and ensures that the group is configured according to those properties. + +## Examples + +## Example 1: Sample Configuration using AzDoOrganizationGroup Resource + +``` PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + AzDoOrganizationGroup OrgGroup { + Ensure = 'Present' + GroupName = 'SampleGroup' + GroupDescription = 'This is a sample group!' + } + } +} + +OrgGroup +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose + +``` + +## Example 2: Sample Configuration using Invoke-DSCResource + +``` PowerShell +# Return the current configuration for AzDoGitPermission +# Ensure is not required +$properties = @{ + GroupName = 'SampleGroup' + GroupDescription = 'This is a sample group!' +} + +Invoke-DSCResource -Name 'AzDoOrganizationGroup' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Example 3: Sample Configuration using AzDO-DSC-LCM + +``` YAML +parameters: {} + +variables: { + "PlaceHolder2": "PlaceHolder" +} + +resources: +- name: Team Leaders Organization Group + type: AzureDevOpsDsc/AzDoOrganizationGroup + properties: + GroupName: AZDO_TeamLeaders_Group + GroupDescription: Team Leaders Organization Group + +- name: Service Accounts Organization Group + type: AzureDevOpsDsc/AzDoOrganizationGroup + properties: + GroupName: AZDO_ServiceAccounts_Group + GroupDescription: Service Accounts Organization Group +``` + +LCM Initialization: + +``` PowerShell + +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +Invoke-AzDoLCM @params diff --git a/source/Examples/Resources/AzDoOrganizationGroup/1-AddAzDoOrganizationGroup.ps1 b/source/Examples/Resources/AzDoOrganizationGroup/1-AddAzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..d140be3da --- /dev/null +++ b/source/Examples/Resources/AzDoOrganizationGroup/1-AddAzDoOrganizationGroup.ps1 @@ -0,0 +1,22 @@ +<# + .DESCRIPTION + This example shows how to add a Orgnaization Group +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoOrganizationGroup 'AddAzDoOrganizationGroup' + { + Ensure = 'Present' + GroupName = 'Initial Group Name' + GroupDescription = 'Initial Description' + } + } +} diff --git a/source/Examples/Resources/AzDoOrganizationGroup/2-UpdateAzDoOrganizationGroup.ps1 b/source/Examples/Resources/AzDoOrganizationGroup/2-UpdateAzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..6938960b1 --- /dev/null +++ b/source/Examples/Resources/AzDoOrganizationGroup/2-UpdateAzDoOrganizationGroup.ps1 @@ -0,0 +1,22 @@ +<# + .DESCRIPTION + This example shows how to update a Orgnaization Group +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoOrganizationGroup 'UpdateAzDoOrganizationGroup' + { + Ensure = 'Present' + GroupName = 'Updated Group Name' + GroupDescription = 'Initial Description' + } + } +} diff --git a/source/Examples/Resources/AzDoOrganizationGroup/3-DeleteAzDoOrganizationGroup.ps1 b/source/Examples/Resources/AzDoOrganizationGroup/3-DeleteAzDoOrganizationGroup.ps1 new file mode 100644 index 000000000..6cd179ad2 --- /dev/null +++ b/source/Examples/Resources/AzDoOrganizationGroup/3-DeleteAzDoOrganizationGroup.ps1 @@ -0,0 +1,21 @@ +<# + .DESCRIPTION + This example shows how to remove a Orgnaization Group +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoOrganizationGroup 'DeleteAzDoOrganizationGroup' + { + Ensure = 'Absent' + GroupName = 'Updated Group Name' + } + } +} diff --git a/source/Examples/Resources/AzDoProject.md b/source/Examples/Resources/AzDoProject.md new file mode 100644 index 000000000..dc3ee6168 --- /dev/null +++ b/source/Examples/Resources/AzDoProject.md @@ -0,0 +1,123 @@ +# DSC AzDoProject Resource + +# Syntax + +``` PowerShell +AzDoProject [string] #ResourceName +{ + ProjectName = [String]$ProjectName + [ Ensure = [String] {'Present', 'Absent'}] + [ ProjectDescription = [String]$ProjectDescription] + [ SourceControlType = [String] {'Git', 'Tfvc'}] + [ ProcessTemplate = [String] {'Agile', 'Scrum', 'CMMI', 'Basic'}] + [ Visibility = [String] {'Public', 'Private'}] +} +``` + +# Properties + +Common Properties: + +- __ProjectName__: The name of the Azure DevOps project. +- __ProjectDescription__: A description for the Azure DevOps project. +- __SourceControlType__: The type of source control (Git or Tfvc). Default is Git. +- __ProcessTemplate__: The process template to use (Agile, Scrum, CMMI, Basic). Default is Agile. +- __Visibility__: The visibility of the project (Public or Private). Default is Private. + +# Additional Information + +This resource is used to manage Azure DevOps projects using Desired State Configuration (DSC). +It allows you to define the properties of an Azure DevOps project and ensures that the project is configured according to those properties. + +# Examples + +## Example 1: Sample Configuration for Azure DevOps Project using AzDoProject Resource + +``` PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + AzDoProject ProjectExample { + Ensure = 'Present' + ProjectName = 'MySampleProject' + ProjectDescription = 'This is a sample Azure DevOps project.' + SourceControlType = 'Git' + ProcessTemplate = 'Agile' + Visibility = 'Private' + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose + +``` + +## Example 2: Sample Configuration for Azure DevOps Project using Invoke-DSCResource + +``` PowerShell +# Return the current configuration for AzDoProject +# Ensure is not required +$properties = @{ + ProjectName = 'MySameProject' + ProjectDiscription = 'This is a sample Azure DevOps project' + SourceControlType = 'Git' + ProcessTemplate = 'Agile' + Visibility = 'Private' +} + +Invoke-DSCResource -Name 'AzDoProject' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Example 3: Sample Configuration to remove/exclude an Azure DevOps Project using Invoke-DSCResource + +``` PowerShell +# Remove the Azure Devops Project and ensure that it is not recreated. +$properties = @{ + ProjectName = 'MySameProject' + Ensure = 'Absent' +} + +Invoke-DSCResource -Name 'AzDoProject' -Method Set -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +## Example 4: Sample Configuration using AzDO-DSC-LCM + +``` YAML +parameters: {} + +variables: { + ProjectName: SampleProject, + ProjectDescription: This is a SampleProject! +} + +resources: + + - name: Project + type: AzureDevOpsDsc/AzDoProject + properties: + projectName: $ProjectName + projectDescription: $ProjectDescription + visibility: private + SourceControlType: Git + ProcessTemplate: Agile +``` + +LCM Initialization: + +``` PowerShell + +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +Invoke-AzDoLCM @params + +``` diff --git a/source/Examples/Resources/AzDoProjectGroup.md b/source/Examples/Resources/AzDoProjectGroup.md new file mode 100644 index 000000000..eeeb148ba --- /dev/null +++ b/source/Examples/Resources/AzDoProjectGroup.md @@ -0,0 +1,102 @@ +# DSC AzDoProjectGroup Resource + +## Syntax + +```PowerShell +AzDoProjectGroup [string] #ResourceName +{ + GroupName = [String]$GroupName + ProjectName = [String]$ProjectName + [ GroupDescription = [String]$GroupDescription ] +} +``` + +## Properties + +### Common Properties + +- **GroupName**: The name of the project group. This property is mandatory and serves as the key property for the resource. +- **ProjectName**: The name of the Azure DevOps project associated with this group. This property is mandatory. +- **GroupDescription**: A description of the project group. + +## Additional Information + +This resource is used to manage Azure DevOps project groups using Desired State Configuration (DSC). It allows you to define the properties of an Azure DevOps project group and ensures that the group is configured according to those properties. + +## Examples + +### Example 1: Sample Configuration using AzDoProjectGroup Resource + +```PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + AzDoProjectGroup ProjectGroup { + Ensure = 'Present' + GroupName = 'SampleProjectGroup' + ProjectName = 'SampleProject' + GroupDescription = 'This is a sample project group!' + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose +``` + +### Example 2: Sample Configuration using Invoke-DSCResource + +```PowerShell +# Return the current configuration for AzDoProjectGroup +# Ensure is not required +$properties = @{ + GroupName = 'SampleProjectGroup' + ProjectName = 'SampleProject' + GroupDescription = 'This is a sample project group!' +} + +Invoke-DSCResource -Name 'AzDoProjectGroup' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +### Example 3: Sample Configuration using AzDO-DSC-LCM + +```YAML +parameters: {} + +variables: { + "PlaceHolder2": "PlaceHolder" +} + +resources: +- name: Team Leaders Project Group + type: AzureDevOpsDsc/AzDoProjectGroup + properties: + GroupName: AZDO_TeamLeaders_ProjectGroup + ProjectName: SampleProject + GroupDescription: Team Leaders Project Group + +- name: Service Accounts Project Group + type: AzureDevOpsDsc/AzDoProjectGroup + properties: + GroupName: AZDO_ServiceAccounts_ProjectGroup + ProjectName: SampleProject + GroupDescription: Service Accounts Project Group +``` + +LCM Initialization: + +```PowerShell + +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +Invoke-AzDoLCM @params +``` diff --git a/source/Examples/Resources/AzDoProjectGroup/1-AddAzDoProjectGroup.ps1 b/source/Examples/Resources/AzDoProjectGroup/1-AddAzDoProjectGroup.ps1 new file mode 100644 index 000000000..bc5114e9f --- /dev/null +++ b/source/Examples/Resources/AzDoProjectGroup/1-AddAzDoProjectGroup.ps1 @@ -0,0 +1,22 @@ +<# + .DESCRIPTION + This example shows how to add a Project Group +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoProjectGroup 'AddProjectGroup' { + Ensure = 'Present' + GroupName = 'SampleProjectGroup' + ProjectName = 'SampleProject' + GroupDescription = 'This is a sample project group!' + } + } +} diff --git a/source/Examples/Resources/AzDoProjectGroup/2-UpdateAzDoProjectGroup.ps1 b/source/Examples/Resources/AzDoProjectGroup/2-UpdateAzDoProjectGroup.ps1 new file mode 100644 index 000000000..1065ccc1f --- /dev/null +++ b/source/Examples/Resources/AzDoProjectGroup/2-UpdateAzDoProjectGroup.ps1 @@ -0,0 +1,22 @@ +<# + .DESCRIPTION + This example shows how to update a Project Group +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoProjectGroup 'UpdateProjectGroup' { + Ensure = 'Present' + GroupName = 'SampleProjectGroup' + ProjectName = 'SampleProject' + GroupDescription = 'New project group description' + } + } +} diff --git a/source/Examples/Resources/AzDoProjectGroup/3-DeleteAzDoProjectGroup.ps1 b/source/Examples/Resources/AzDoProjectGroup/3-DeleteAzDoProjectGroup.ps1 new file mode 100644 index 000000000..15b0a852a --- /dev/null +++ b/source/Examples/Resources/AzDoProjectGroup/3-DeleteAzDoProjectGroup.ps1 @@ -0,0 +1,21 @@ +<# + .DESCRIPTION + This example shows how to remove a Project Group +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + node localhost + { + AzDoProjectGroup 'RemoveProjectGroup' { + Ensure = 'Absent' + GroupName = 'SampleProjectGroup' + ProjectName = 'SampleProject' + } + } +} diff --git a/source/Examples/Resources/AzDoProjectServices.md b/source/Examples/Resources/AzDoProjectServices.md new file mode 100644 index 000000000..e44f21af3 --- /dev/null +++ b/source/Examples/Resources/AzDoProjectServices.md @@ -0,0 +1,111 @@ +# DSC AzDoProjectServices Resource + +## Syntax + +```PowerShell +AzDoProjectServices [string] #ResourceName +{ + ProjectName = [String]$ProjectName + [ GitRepositories = [String]$GitRepositories { 'Enabled' | 'Disabled' } ] + [ WorkBoards = [String]$WorkBoards { 'Enabled' | 'Disabled' } ] + [ BuildPipelines = [String]$BuildPipelines { 'Enabled' | 'Disabled' } ] + [ TestPlans = [String]$TestPlans { 'Enabled' | 'Disabled' } ] + [ AzureArtifact = [String]$AzureArtifact { 'Enabled' | 'Disabled' } ] +} +``` + +## Properties + +### Common Properties + +- **ProjectName**: The name of the Azure DevOps project. This property is mandatory and serves as the key property for the resource. +- **GitRepositories**: Specifies whether Git repositories are enabled or disabled. Valid values are `Enabled` or `Disabled`. Default is `Enabled`. +- **WorkBoards**: Specifies whether work boards are enabled or disabled. Valid values are `Enabled` or `Disabled`. Default is `Enabled`. +- **BuildPipelines**: Specifies whether build pipelines are enabled or disabled. Valid values are `Enabled` or `Disabled`. Default is `Enabled`. +- **TestPlans**: Specifies whether test plans are enabled or disabled. Valid values are `Enabled` or `Disabled`. Default is `Enabled`. +- **AzureArtifact**: Specifies whether Azure artifacts are enabled or disabled. Valid values are `Enabled` or `Disabled`. Default is `Enabled`. + +## Additional Information + +This resource is used to manage Azure DevOps project services using Desired State Configuration (DSC). It allows you to define the properties of an Azure DevOps project and ensures that the services are configured according to those properties. + +## Examples + +### Example 1: Sample Configuration using AzDoProjectServices Resource + +```PowerShell +Configuration ExampleConfig { + Import-DscResource -ModuleName 'AzDevOpsDsc' + + Node localhost { + AzDoProjectServices ProjectServices { + Ensure = 'Present' + ProjectName = 'SampleProject' + GitRepositories = 'Enabled' + WorkBoards = 'Enabled' + BuildPipelines = 'Enabled' + TestPlans = 'Enabled' + AzureArtifact = 'Enabled' + } + } +} + +ExampleConfig +Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose +``` + +### Example 2: Sample Configuration using Invoke-DSCResource + +```PowerShell +# Return the current configuration for AzDoProjectServices +# Ensure is not required +$properties = @{ + ProjectName = 'SampleProject' + GitRepositories = 'Enabled' + WorkBoards = 'Enabled' + BuildPipelines = 'Enabled' + TestPlans = 'Enabled' + AzureArtifact = 'Enabled' +} + +Invoke-DSCResource -Name 'AzDoProjectServices' -Method Get -Property $properties -ModuleName 'AzureDevOpsDsc' +``` + +### Example 3: Sample Configuration using AzDO-DSC-LCM + +```YAML +parameters: {} + +variables: { + "PlaceHolder2": "PlaceHolder" +} + +resources: +- name: Sample Project Services + type: AzureDevOpsDsc/AzDoProjectServices + properties: + ProjectName: SampleProject + GitRepositories: Enabled + WorkBoards: Enabled + BuildPipelines: Enabled + TestPlans: Enabled + AzureArtifact: Enabled +``` + +LCM Initialization: + +```PowerShell + +$params = @{ + AzureDevopsOrganizationName = "SampleAzDoOrgName" + ConfigurationDirectory = "C:\Datum\DSCOutput\" + ConfigurationUrl = 'https://configuration-path' + JITToken = 'SampleJITToken' + Mode = 'Set' + AuthenticationType = 'ManagedIdentity' + ReportPath = 'C:\Datum\DSCOutput\Reports' +} + +Invoke-AzDoLCM @params +``` + diff --git a/source/Examples/Resources/AzDoProjectServices/1-AddAzDoProjectServices.ps1 b/source/Examples/Resources/AzDoProjectServices/1-AddAzDoProjectServices.ps1 new file mode 100644 index 000000000..7ae9b07a2 --- /dev/null +++ b/source/Examples/Resources/AzDoProjectServices/1-AddAzDoProjectServices.ps1 @@ -0,0 +1,24 @@ +<# + .DESCRIPTION + This example shows how to add Project Services +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + Node localhost { + AzDoProjectServices 'AddProjectServices' { + Ensure = 'Present' + ProjectName = 'SampleProject' + GitRepositories = 'Enabled' + WorkBoards = 'Enabled' + BuildPipelines = 'Enabled' + TestPlans = 'Enabled' + AzureArtifact = 'Enabled' + } + } +} diff --git a/source/Examples/Resources/AzDoProjectServices/2-UpdateAzDoProjectServices.ps1 b/source/Examples/Resources/AzDoProjectServices/2-UpdateAzDoProjectServices.ps1 new file mode 100644 index 000000000..810327dc8 --- /dev/null +++ b/source/Examples/Resources/AzDoProjectServices/2-UpdateAzDoProjectServices.ps1 @@ -0,0 +1,24 @@ +<# + .DESCRIPTION + This example shows how to add Project Services +#> + +New-AzDoAuthenticationProvider -OrganizationName 'test-organization' -PersonalAccessToken 'my-pat' + +Configuration Example +{ + + Import-DscResource -ModuleName 'AzureDevOpsDsc' + + Node localhost { + AzDoProjectServices 'AddProjectServices' { + Ensure = 'Present' + ProjectName = 'SampleProject' + GitRepositories = 'Enabled' + WorkBoards = 'Disabled' + BuildPipelines = 'Disabled' + TestPlans = 'Disabled' + AzureArtifact = 'Disabled' + } + } +} diff --git a/source/Examples/Resources/AzDoProjectServices/3-DeleteAzDoProjectServices.ps1 b/source/Examples/Resources/AzDoProjectServices/3-DeleteAzDoProjectServices.ps1 new file mode 100644 index 000000000..11eaf9ff3 --- /dev/null +++ b/source/Examples/Resources/AzDoProjectServices/3-DeleteAzDoProjectServices.ps1 @@ -0,0 +1,6 @@ +<# + .DESCRIPTION + This example shows how to remove a Project Services. +#> + +# Not supported. See Update Project Services. diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 index 1cd5f169a..09aa09324 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.ps1 @@ -3,7 +3,7 @@ Obtains a managed identity token from Azure AD. .DESCRIPTION -The Get-AzManagedIdentityToken function is used to obtain an access token from Azure AD using a managed identity. It can only be called from the New-AzManagedIdentity or Update-AzManagedIdentity functions. +The Get-AzManagedIdentityToken function is used to obtain an access token from Azure AD using a managed identity. It can only be called from the New-AzDoAuthenticationProvider or Update-AzManagedIdentity functions. .PARAMETER OrganizationName Specifies the name of the organization. @@ -50,24 +50,15 @@ Function Get-AzManagedIdentityToken if ($env:IDENTITY_ENDPOINT) { - # Validate what type of machine it is. - if ($null -eq $IsCoreCLR) - { - # If the $IsCoreCLR variable is not set, the script is running on Windows PowerShell. - Write-Verbose "[Get-AzManagedIdentityToken] The machine is a Windows machine Running Windows PowerShell." - $IsWindows = $true - } + $OSInfo = Get-OperatingSystemInfo # Test if console is being run as Administrator - if ( - ($IsWindows) -and - (-not ( - [Security.Principal.WindowsPrincipal] - [Security.Principal.WindowsIdentity]::GetCurrent() - ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) - ) + if ($OSInfo.Windows) { - throw "[Get-AzManagedIdentityToken] Error: Authentication to Azure Arc requires Administrator privileges." + # Check if the current user is in the Administrator role + if (-not(Test-isWindowsAdmin)) { + throw "[Get-AzManagedIdentityToken] Error: Authentication to Azure Arc requires Administrator privileges." + } } Write-Verbose "[Get-AzManagedIdentityToken] The machine is an Azure Arc machine. The Uri needs to be updated to $($env:IDENTITY_ENDPOINT):" diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-OperatingSystemInfo.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-OperatingSystemInfo.ps1 new file mode 100644 index 000000000..44d5263eb --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-OperatingSystemInfo.ps1 @@ -0,0 +1,38 @@ +Function Get-OperatingSystemInfo +{ + $OS = @{ + Windows = $( + if ($PSVersionTable.PSVersion.Major -le 5) + { + $true + } + else + { + $IsWindows + } + ) + Linux = $( + if ($null -eq $IsLinux) + { + $false + } + else + { + $IsLinux + } + ) + MacOS = $( + if ($null -eq $IsMacOS) + { + $false + } + else + { + $IsMacOS + } + ) + } + + Write-Output $OS + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Test-isWindowsAdmin.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Test-isWindowsAdmin.ps1 new file mode 100644 index 000000000..1c4fd7cab --- /dev/null +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Test-isWindowsAdmin.ps1 @@ -0,0 +1,29 @@ +<# +.SYNOPSIS + Checks if the current user has Administrator privileges. + +.DESCRIPTION + The Test-isWindowsAdmin function verifies if the current user is in the Administrator role. + If the user does not have Administrator privileges, an error is thrown indicating that + authentication to Azure Arc requires Administrator privileges. + +.EXAMPLE + Test-isWindowsAdmin + + This command checks if the current user has Administrator privileges. If not, an error is thrown. + +.NOTES + This function is used to ensure that the current user has the necessary permissions to + authenticate to Azure Arc. +#> + +Function Test-isWindowsAdmin +{ + + $currentIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object System.Security.Principal.WindowsPrincipal($currentIdentity) + + # Check if the current user is in the Administrator role + ($principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) + +} diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 index 12432a42c..8a3cc2569 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.ps1 @@ -20,7 +20,7 @@ Function Update-AzManagedIdentity # Test if the Global Var's Exist $Global:DSCAZDO_OrganizationName if ($null -eq $Global:DSCAZDO_OrganizationName) { - Throw "[Update-AzManagedIdentity] Organization Name is not set. Please run 'New-AzManagedIdentity -OrganizationName '" + Throw "[Update-AzManagedIdentity] Organization Name is not set. Please run 'New-AzDoAuthenticationProvider -OrganizationName '" } # Clear the existing token. diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 index 0d2ebf848..fb3f36225 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 @@ -257,6 +257,6 @@ function Invoke-AzDevOpsApiRestMethod # If all retry attempts have failed, throw an exception $errorMessage = $script:localizedData.AzDevOpsApiRestMethodException -f $MyInvocation.MyCommand, $RetryAttempts, $restMethodExceptionMessage - New-InvalidOperationException -Message $errorMessage -Throw + throw (New-InvalidOperationException -Message $errorMessage) } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 deleted file mode 100644 index ff7fd85cd..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/System/New-Thread.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -<# -.SYNOPSIS -Creates and starts a new thread to run the specified script block. - -.DESCRIPTION -The New-Thread function creates a new thread using the provided script block and starts it. -This can be useful for running tasks concurrently. - -.PARAMETER ScriptBlock -The script block to be executed in the new thread. This parameter is mandatory. - -.EXAMPLE -$scriptBlock = { - # Your code here -} -$thread = New-Thread -ScriptBlock $scriptBlock - -.NOTES -Author: Your Name -Date: YYYY-MM-DD -#> -Function New-Thread -{ - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - [ScriptBlock]$ScriptBlock - ) - - # - # Logging - Write-Verbose "[New-Thread] Started." - - # - # Create a new thread - $thread = [System.Threading.Thread]::new($ScriptBlock) - $thread.Start() - - # - # Return the thread - Write-Verbose "[New-Thread] Completed." - return $thread -} diff --git a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 index 91d71242c..e1fb7c2e8 100644 --- a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 +++ b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 @@ -24,7 +24,7 @@ $functionSubDirectoryPaths = @( #"$ModuleRoot\Api\Enums\", # Data - "$ModuleRoot\Api\Data\", + #"$ModuleRoot\Api\Data\", "$ModuleRoot\LocalizedData\", # Api diff --git a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 index 75bcbca21..69504c45c 100644 --- a/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.ps1 @@ -25,11 +25,11 @@ data LocalizedDataAzACLTokenPatten @{ # Git ACL Token Patterns OrganizationGit = '^repoV2$' - GitProject = '^\(repoV2\)\\/\(\?[A-Za-z0-9-]+\)$' - GitRepository = '^\(repoV2\)\\/\(\?[A-Za-z0-9-]+\)\\/\(\?[A-Za-z0-9-]+\)$' - GitBranch = '^\(repoV2\)\\/\(\?[A-Za-z0-9-]+\)\\/\(\?[A-Za-z0-9-]+\)\\/refs/heads/\(\?[A-Za-z0-9]+\)$' + GitProject = '^(repoV2)\/(?[A-Za-z0-9-]+)$' + GitRepository = '^(repoV2)\/(?[A-Za-z0-9-]+)\/(?[A-Za-z0-9-]+)$' + GitBranch = '^(repoV2)\/(?[A-Za-z0-9-]+)\/(?[A-Za-z0-9-]+)\/refs\/heads\/(?[A-Za-z0-9]+)' # Identity ACL Token Patterns GroupPermission = '^(?[A-Za-z0-9-_]+)\\(?[A-Za-z0-9-_]+)$' - ResourcePermission = '^\(\?[A-Za-z0-9-_]+\)$' + ResourcePermission = '^(?[A-Za-z0-9-_]+)$' } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.ps1 index 1970ce6be..f99513f7f 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.ps1 @@ -72,7 +72,7 @@ Function New-AzDoGroupMember # Check if the group members are already cached if ( ($null -ne $CachedGroupMembers) -and - ($CachedGroupMembers.ContainsKey($GroupIdentity.principalName)) + (($CachedGroupMembers | Where-Object { $_.Key -eq $GroupIdentity.principalName }).Count -ne 0) ) { Write-Error "[New-AzDoGroupMember] Group members are already cached for group '$GroupName'." diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.ps1 index ffcfd0fca..a280ca3cc 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.ps1 @@ -88,6 +88,7 @@ function Get-AzDoProject propertiesChanged = @() status = $null } + Write-Verbose "[Get-AzDoProject] Initial result hashtable constructed." # Perform a lookup to see if the project exists in Azure DevOps @@ -121,7 +122,7 @@ function Get-AzDoProject Write-Warning "[Get-AzDoProject] Source control type cannot be changed. Please delete the project and recreate it." } - # If the project description is null, set it to an empty string. + # If the project description property does not exist. Create it and set it to an empty string. if ($null -eq $project.description) { $project | Add-Member -MemberType NoteProperty -Name description -Value '' @@ -144,9 +145,16 @@ function Get-AzDoProject Write-Verbose "[Get-AzDoProject] Project visibility has changed." } + # Test if the properties have changed. If the properties haven't changed, return Unchanged. + if ($result.propertiesChanged.Count -eq 0) + { + $result.Status = [DSCGetSummaryState]::Unchanged + Write-Verbose "[Get-AzDoProject] Project properties have not changed." + } + # Return the group from the cache Write-Verbose "[Get-AzDoProject] Returning final result." - return [PSCustomObject]$result + return $result } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.ps1 index fd9ecfeb9..30febf93a 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.ps1 @@ -65,7 +65,7 @@ function Test-AzDoProject [System.String]$Visibility = 'Private', [Parameter()] - [PSCustomObject]$LookupResult, + [HashTable]$LookupResult, [Parameter()] [Ensure]$Ensure, diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Get-AzDoProjectServices.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Get-AzDoProjectServices.ps1 index d6f9779c3..d841471c0 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Get-AzDoProjectServices.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectServices/Get-AzDoProjectServices.ps1 @@ -100,7 +100,7 @@ Function Get-AzDoProjectServices } # Attempt to retrive the Project from the Live Cache. - Write-Verbose "[Get-xAzDevOpsProjectServices] Retriving the Project from the Live Cache." + Write-Verbose "[Get-AzDevOpsProjectServices] Retriving the Project from the Live Cache." # Retrive the Repositories from the Live Cache. $Project = Get-CacheItem -Key $ProjectName -Type 'LiveProjects' @@ -108,7 +108,7 @@ Function Get-AzDoProjectServices # If the Project does not exist in the Live Cache, return the Project object. if ($null -eq $Project) { - Write-Warning "[Get-xAzDevOpsProjectServices] The Project '$ProjectName' was not found in the Live Cache." + Write-Warning "[Get-AzDevOpsProjectServices] The Project '$ProjectName' was not found in the Live Cache." $Result.Status = [DSCGetSummaryState]::NotFound return $Result } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 index 95b1c3198..db729326a 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.ps1 @@ -79,11 +79,11 @@ Function New-AzDoAuthenticationProvider # if the NoVerify switch is not set, verify the Token. if ($NoVerify) { - $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -PersonalAccessToken $PersonalAccessToken + $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -PersonalAccessToken $PersonalAccessToken -OrganizationName $OrganizationName } else { - $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -PersonalAccessToken $PersonalAccessToken -Verify + $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -PersonalAccessToken $PersonalAccessToken -Verify -OrganizationName $OrganizationName } } @@ -109,7 +109,7 @@ Function New-AzDoAuthenticationProvider { Write-Verbose "[New-AzDoAuthenticationProvider] Creating a new Personal Access Token with OrganizationName $OrganizationName." # If the Token is not Valid. Get a new Token. - $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -SecureStringPersonalAccessToken $SecureStringPersonalAccessToken + $Global:DSCAZDO_AuthenticationToken = Set-AzPersonalAccessToken -SecureStringPersonalAccessToken $SecureStringPersonalAccessToken -OrganizationName $OrganizationName } # Export the Token information to the Cache Directory @@ -119,6 +119,7 @@ Function New-AzDoAuthenticationProvider return } + # Initialize the Cache Get-AzDoCacheObjects | ForEach-Object { Initialize-CacheObject -CacheType $_ diff --git a/source/WikiSource/Resources/AzDoGitPermission.md b/source/WikiSource/Resources/AzDoGitPermission.md index 494ade5fc..a87b621de 100644 --- a/source/WikiSource/Resources/AzDoGitPermission.md +++ b/source/WikiSource/Resources/AzDoGitPermission.md @@ -190,6 +190,6 @@ $params = @{ ReportPath = 'C:\Datum\DSCOutput\Reports' } -.\Invoke-AZDOLCM.ps1 @params +Invoke-AzDoLCM @params ``` diff --git a/source/WikiSource/Resources/AzDoGroupMember.md b/source/WikiSource/Resources/AzDoGroupMember.md index 42c59879a..15c1f7172 100644 --- a/source/WikiSource/Resources/AzDoGroupMember.md +++ b/source/WikiSource/Resources/AzDoGroupMember.md @@ -123,5 +123,5 @@ $params = @{ ReportPath = 'C:\Datum\DSCOutput\Reports' } -.\Invoke-AZDOLCM.ps1 @params +Invoke-AzDoLCM @params ``` diff --git a/source/WikiSource/Resources/AzDoOrganizationGroup.md b/source/WikiSource/Resources/AzDoOrganizationGroup.md index a271e5a99..d9b4d11ca 100644 --- a/source/WikiSource/Resources/AzDoOrganizationGroup.md +++ b/source/WikiSource/Resources/AzDoOrganizationGroup.md @@ -93,4 +93,4 @@ $params = @{ ReportPath = 'C:\Datum\DSCOutput\Reports' } -.\Invoke-AZDOLCM.ps1 @params +Invoke-AzDoLCM @params diff --git a/source/WikiSource/Resources/AzDoProject.md b/source/WikiSource/Resources/AzDoProject.md index 4f042c0fa..dc3ee6168 100644 --- a/source/WikiSource/Resources/AzDoProject.md +++ b/source/WikiSource/Resources/AzDoProject.md @@ -118,6 +118,6 @@ $params = @{ ReportPath = 'C:\Datum\DSCOutput\Reports' } -.\Invoke-AZDOLCM.ps1 @params +Invoke-AzDoLCM @params ``` diff --git a/source/WikiSource/Resources/AzDoProjectGroup.md b/source/WikiSource/Resources/AzDoProjectGroup.md index cdf7ffd85..eeeb148ba 100644 --- a/source/WikiSource/Resources/AzDoProjectGroup.md +++ b/source/WikiSource/Resources/AzDoProjectGroup.md @@ -98,5 +98,5 @@ $params = @{ ReportPath = 'C:\Datum\DSCOutput\Reports' } -.\Invoke-AZDOLCM.ps1 @params +Invoke-AzDoLCM @params ``` diff --git a/source/WikiSource/Resources/AzDoProjectServices.md b/source/WikiSource/Resources/AzDoProjectServices.md index 2c088aa31..e44f21af3 100644 --- a/source/WikiSource/Resources/AzDoProjectServices.md +++ b/source/WikiSource/Resources/AzDoProjectServices.md @@ -106,6 +106,6 @@ $params = @{ ReportPath = 'C:\Datum\DSCOutput\Reports' } -.\Invoke-AZDOLCM.ps1 @params +Invoke-AzDoLCM @params ``` diff --git a/tests/DSC/InModuleScope.ps1 b/tests/DSC/InModuleScope.ps1 index f79362906..d4c4a4802 100644 --- a/tests/DSC/InModuleScope.ps1 +++ b/tests/DSC/InModuleScope.ps1 @@ -35,11 +35,3 @@ $ModuleRoot = 'C:\Temp\AzureDevOpsDSC\source\Modules\AzureDevOpsDsc.Common\' Initialize-CacheObject -CacheType $_ } -<# -# Create a Managed Identity Token -New-AzManagedIdentity -OrganizationName "akkodistestorg" -Verbose - -# Set the Group Cache -Set-AzDoAPICacheGroup -OrganizationName $Global:DSCAZDO_OrganizationName -Set-AzDoAPICacheProject -OrganizationName $Global:DSCAZDO_OrganizationName -Verbose -#> diff --git a/tests/Integration/Invoke-Tests.ps1 b/tests/Integration/Invoke-Tests.ps1 index 15404ac4e..44bfdf8bf 100644 --- a/tests/Integration/Invoke-Tests.ps1 +++ b/tests/Integration/Invoke-Tests.ps1 @@ -26,4 +26,4 @@ Invoke-Pester -Path "$PSScriptRoot\Resources" Get-ChildItem -Path "$($CurrentLocation.Path)\Supporting\API" -Filter "*.ps1" | ForEach-Object { . $_.FullName } Get-ChildItem -Path "$($CurrentLocation.Path)\Supporting\APICalls" -Filter "*.ps1" | ForEach-Object { . $_.FullName } -. "$($CurrentLocation.Path)\Supporting\Teardown.ps1" -ClearAll -OrganizationName $GLOBAL:DSCAZDO_OrganizationName +. "$($CurrentLocation.Path)\Supporting\Teardown.ps1" -ClearAll -OrganizationName $GLOBAL:DSCAZDO_OrganizationName -TestFrameworkConfiguration $TestFrameworkConfiguration diff --git a/tests/Integration/Resources/xAzDoGitPermission.tests.ps1 b/tests/Integration/Resources/AzDoGitPermission.tests.ps1 similarity index 99% rename from tests/Integration/Resources/xAzDoGitPermission.tests.ps1 rename to tests/Integration/Resources/AzDoGitPermission.tests.ps1 index 7e6f95b49..d1fc8312e 100644 --- a/tests/Integration/Resources/xAzDoGitPermission.tests.ps1 +++ b/tests/Integration/Resources/AzDoGitPermission.tests.ps1 @@ -81,7 +81,6 @@ Describe "AzDoGitPermission Integration Tests" { # Set up the parameters for the DSC resource invocation. $parameters.Method = 'Test' - $result = Invoke-DscResource @parameters $result.InDesiredState | Should -BeTrue } diff --git a/tests/Integration/Resources/xAzDoGitRepository.tests.ps1 b/tests/Integration/Resources/AzDoGitRepository.tests.ps1 similarity index 100% rename from tests/Integration/Resources/xAzDoGitRepository.tests.ps1 rename to tests/Integration/Resources/AzDoGitRepository.tests.ps1 diff --git a/tests/Integration/Resources/xAzDoGroupMember.tests.ps1 b/tests/Integration/Resources/AzDoGroupMember.tests.ps1 similarity index 100% rename from tests/Integration/Resources/xAzDoGroupMember.tests.ps1 rename to tests/Integration/Resources/AzDoGroupMember.tests.ps1 diff --git a/tests/Integration/Resources/xAzDoGroupPermission.tests.ps1 b/tests/Integration/Resources/AzDoGroupPermission.tests.ps1 similarity index 100% rename from tests/Integration/Resources/xAzDoGroupPermission.tests.ps1 rename to tests/Integration/Resources/AzDoGroupPermission.tests.ps1 diff --git a/tests/Integration/Resources/xAzDoOrganizationGroup.Description.tests.ps1 b/tests/Integration/Resources/AzDoOrganizationGroup.Description.tests.ps1 similarity index 100% rename from tests/Integration/Resources/xAzDoOrganizationGroup.Description.tests.ps1 rename to tests/Integration/Resources/AzDoOrganizationGroup.Description.tests.ps1 diff --git a/tests/Integration/Resources/xAzDoOrganizationGroup.NoDescription.tests.ps1 b/tests/Integration/Resources/AzDoOrganizationGroup.NoDescription.tests.ps1 similarity index 100% rename from tests/Integration/Resources/xAzDoOrganizationGroup.NoDescription.tests.ps1 rename to tests/Integration/Resources/AzDoOrganizationGroup.NoDescription.tests.ps1 diff --git a/tests/Integration/Resources/xAzDoProject.Description.tests.ps1 b/tests/Integration/Resources/AzDoProject.Description.tests.ps1 similarity index 100% rename from tests/Integration/Resources/xAzDoProject.Description.tests.ps1 rename to tests/Integration/Resources/AzDoProject.Description.tests.ps1 diff --git a/tests/Integration/Resources/xAzDoProject.NoDescription.tests.ps1 b/tests/Integration/Resources/AzDoProject.NoDescription.tests.ps1 similarity index 100% rename from tests/Integration/Resources/xAzDoProject.NoDescription.tests.ps1 rename to tests/Integration/Resources/AzDoProject.NoDescription.tests.ps1 diff --git a/tests/Integration/Resources/xAzDoProjectGroup.Description.tests.ps1 b/tests/Integration/Resources/AzDoProjectGroup.Description.tests.ps1 similarity index 100% rename from tests/Integration/Resources/xAzDoProjectGroup.Description.tests.ps1 rename to tests/Integration/Resources/AzDoProjectGroup.Description.tests.ps1 diff --git a/tests/Integration/Resources/xAzDoProjectServices.tests.ps1 b/tests/Integration/Resources/AzDoProjectServices.tests.ps1 similarity index 100% rename from tests/Integration/Resources/xAzDoProjectServices.tests.ps1 rename to tests/Integration/Resources/AzDoProjectServices.tests.ps1 diff --git a/tests/Integration/Supporting/API/Get-MIToken.ps1 b/tests/Integration/Supporting/API/Get-MIToken.ps1 index 964174f48..2a81eb6a3 100644 --- a/tests/Integration/Supporting/API/Get-MIToken.ps1 +++ b/tests/Integration/Supporting/API/Get-MIToken.ps1 @@ -7,7 +7,7 @@ Function Get-MIToken { $OrganizationName ) - Write-Verbose "[Get-AzManagedIdentityToken] Getting the managed identity token for the organization $OrganizationName." + Write-Verbose "[Get-MIToken] Getting the managed identity token for the organization $OrganizationName." # Obtain the access token from Azure AD using the Managed Identity @@ -15,14 +15,49 @@ Function Get-MIToken { # Define the Azure instance metadata endpoint to get the access token Uri = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=499b84ac-1321-427f-aa17-267ca6975798" Method = 'Get' - Headers = @{ Metadata="true" } + HttpHeaders = @{ Metadata="true" } ContentType = 'Application/json' } - Write-Verbose "[Get-AzManagedIdentityToken] Invoking the Azure Instance Metadata Service to get the access token." + # Dertimine if the machine is an arc machine + if ($env:IDENTITY_ENDPOINT) + { + + Write-Verbose "[Get-MIToken] The machine is an Azure Arc machine. The Uri needs to be updated to $($env:IDENTITY_ENDPOINT):" + $ManagedIdentityParams.Uri = '{0}?api-version=2020-06-01&resource=499b84ac-1321-427f-aa17-267ca6975798' -f $env:IDENTITY_ENDPOINT + $ManagedIdentityParams.AzureArcAuthentication = $true + + } + + Write-Verbose "[Get-MIToken] Invoking the Azure Instance Metadata Service to get the access token." # Invoke the RestAPI - try { $response = Invoke-RestMethod @ManagedIdentityParams } catch { Throw $_ } + try + { + $response = Invoke-APIRestMethod @ManagedIdentityParams + } + catch + { + # If there is an error it could be because it's an arc machine, and we need to use the secret file: + $wwwAuthHeader = $_.Exception.Response.Headers.WwwAuthenticate + if ($wwwAuthHeader -notmatch "Basic realm=.+") + { + Throw ('[Get-MIToken] {0}' -f $_) + } + + Write-Verbose "[Get-MIToken] Managed Identity Token Retrival Failed. Retrying with secret file." + + # Extract the secret file path from the WWW-Authenticate header + $secretFile = ($wwwAuthHeader -split "Basic realm=")[1] + # Read the secret file to get the token + $token = Get-Content -LiteralPath $secretFile -Raw + # Add the token to the headers + $ManagedIdentityParams.HttpHeaders.Authorization = "Basic $token" + + # Retry the request. Silently continue to suppress the error message, since we will handle it below. + $response = Invoke-APIRestMethod @ManagedIdentityParams -ErrorAction SilentlyContinue + } + # Test the response if ($null -eq $response.access_token) { diff --git a/tests/Integration/Supporting/API/Get-OperatingSystemInfo.ps1 b/tests/Integration/Supporting/API/Get-OperatingSystemInfo.ps1 new file mode 100644 index 000000000..44d5263eb --- /dev/null +++ b/tests/Integration/Supporting/API/Get-OperatingSystemInfo.ps1 @@ -0,0 +1,38 @@ +Function Get-OperatingSystemInfo +{ + $OS = @{ + Windows = $( + if ($PSVersionTable.PSVersion.Major -le 5) + { + $true + } + else + { + $IsWindows + } + ) + Linux = $( + if ($null -eq $IsLinux) + { + $false + } + else + { + $IsLinux + } + ) + MacOS = $( + if ($null -eq $IsMacOS) + { + $false + } + else + { + $IsMacOS + } + ) + } + + Write-Output $OS + +} diff --git a/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 b/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 index 94a86d30b..de3035d33 100644 --- a/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 +++ b/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 @@ -48,7 +48,11 @@ function Invoke-APIRestMethod [Parameter()] [Switch] - $NoAuthentication + $NoAuthentication, + + [Parameter()] + [Switch] + $AzureArcAuthentication ) @@ -68,116 +72,35 @@ function Invoke-APIRestMethod $invokeRestMethodParameters.Remove('ContentType') } - # Intially set this value to -1, as the first attempt does not want to be classed as a "RetryAttempt" - $CurrentNoOfRetryAttempts = -1 - # Set the Continuation Token to be False - $isContinuationToken = $false - $results = [System.Collections.ArrayList]::new() + if ($null -eq $HttpHeaders.Authorization) + { + $HttpHeaders.Authorization = Add-Header + } - while ($CurrentNoOfRetryAttempts -lt $RetryAttempts) + # + # Invoke the REST method + try { + # Invoke the REST method. If the 'Verbose' switch is present, set it to $false. + # This is to prevent the output from being displayed in the console. + $response = Invoke-RestMethod @invokeRestMethodParameters -Verbose:$false + return $response + } + catch + { + # If AzureArcAuthentication is present, then we need to handle the error differently. + # Stop and Pass the error back to the caller. The caller will handle the error. + if ($AzureArcAuthentication.IsPresent) + { + throw $_ + } - # - # Invoke the REST method. Loop until the Continuation Token is False. - - Do { - - # - # Add the Authentication Header - - # If the 'NoAuthentication' switch is NOT PRESENT and the 'Authentication' header is empty, add the authentication header - if (([String]::IsNullOrEmpty($invokeRestMethodParameters.Headers.Authentication)) -and (-not $NoAuthentication.IsPresent)) - { - $invokeRestMethodParameters.Headers.Authorization = Add-Header - } - - # - # Invoke the REST method - - try - { - # Invoke the REST method. If the 'Verbose' switch is present, set it to $false. - # This is to prevent the output from being displayed in the console. - $response = Invoke-RestMethod @invokeRestMethodParameters -Verbose:$false - - # Zero out the 'Authorization' header - $invokeRestMethodParameters.Headers.Authorization = $null - # Add the response to the results array - $null = $results.Add($response) - - # - # Test to see if there is no continuation token - if ([String]::IsNullOrEmpty($responseHeaders.'x-ms-continuationtoken')) - { - # If not, set the continuation token to False - $isContinuationToken = $false - # Update the Rate Limit information - $Global:DSCAZDO_APIRateLimit = $null - - Write-Verbose "[Invoke-APIRestMethod] No continuation token found. Breaking loop." - - return $results - - } - - # - # A continuation token is present. - - # If so, set the continuation token to True - $isContinuationToken = $true - # Update the URI to include the continuation token - $invokeRestMethodParameters.Uri = '{0}&continuationToken={1}&{2}' -f $ApiUri, $responseHeaders.'x-ms-continuationtoken', $ApiVersion - # Reset the RetryAttempts counter - $CurrentNoOfRetryAttempts = -1 - - } - catch - { - # Zero out the 'Authorization' header - $invokeRestMethodParameters.Headers.Authorization = $null - - # Check to see if it is an HTTP 429 (Too Many Requests) error - if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::TooManyRequests) - { - # If so, wait for the specified number of seconds before retrying - $retryAfter = $_.Exception.Response.Headers | ForEach-Object { - if ($_.Key -eq "Retry-After") - { - return $_.Value - } - } - - if ($retryAfter) - { - $retryAfter = [int]$retryAfter - Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $retryAfter seconds before retrying." - $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($retryAfter) - } else { - # If the Retry-After header is not present, wait for the specified number of milliseconds before retrying - Write-Verbose -Message "Received a 'Too Many Requests' response from the Azure DevOps API. Waiting for $RetryIntervalMs milliseconds before retrying." - $Global:DSCAZDO_APIRateLimit = [APIRateLimit]::New($RetryIntervalMs) - } - - } - - # Increment the number of retries attempted and obtain any exception message - $CurrentNoOfRetryAttempts++ - $restMethodExceptionMessage = $_.Exception.Message - - # Wait before the next attempt/retry - Start-Sleep -Milliseconds $RetryIntervalMs - - # Break the continuation token loop so that the next attempt can be made - break; - - } - - } Until (-not $isContinuationToken) + # Wait before the next attempt/retry + Start-Sleep -Milliseconds $RetryIntervalMs - } + # Break the continuation token loop so that the next attempt can be made + break; - # If all retry attempts have failed, throw an exception - $errorMessage = $script:localizedData.AzDevOpsApiRestMethodException -f $MyInvocation.MyCommand, $RetryAttempts, $restMethodExceptionMessage - New-InvalidOperationException -Message $errorMessage -Throw + } } diff --git a/tests/Integration/Supporting/API/Test-isWindowsAdmin.ps1 b/tests/Integration/Supporting/API/Test-isWindowsAdmin.ps1 new file mode 100644 index 000000000..a9e58b338 --- /dev/null +++ b/tests/Integration/Supporting/API/Test-isWindowsAdmin.ps1 @@ -0,0 +1,10 @@ +Function Test-isWindowsAdmin +{ + + $currentIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object System.Security.Principal.WindowsPrincipal($currentIdentity) + + # Check if the current user is in the Administrator role + (-not($principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))) + +} diff --git a/tests/Integration/Supporting/Teardown.ps1 b/tests/Integration/Supporting/Teardown.ps1 index c9aaf3689..83ffc0ee1 100644 --- a/tests/Integration/Supporting/Teardown.ps1 +++ b/tests/Integration/Supporting/Teardown.ps1 @@ -14,7 +14,11 @@ param ( [Parameter()] [String] - $OrganizationName + $OrganizationName, + + [Parameter()] + [Object] + $TestFrameworkConfiguration ) @@ -25,7 +29,7 @@ $Global:DSCAZDO_AuthenticationToken = Get-MIToken -OrganizationName $Organizatio if ($ClearAll -or $ClearProjects) { # List all projects and remove them - List-DevOpsProjects -OrganizationName $OrganizationName | ForEach-Object { + List-DevOpsProjects -OrganizationName $OrganizationName | Where-Object { $_.Name -notin $TestFrameworkConfiguration.excludedProjectsFromTeardown } | ForEach-Object { Remove-DevOpsProject -ProjectId $_.id -Organization $OrganizationName } } diff --git a/tests/Integration/TestFrameworkConfiguration.json b/tests/Integration/TestFrameworkConfiguration.json index 3dddec1f9..5afce3ea9 100644 --- a/tests/Integration/TestFrameworkConfiguration.json +++ b/tests/Integration/TestFrameworkConfiguration.json @@ -1,4 +1,7 @@ { "Organization" : "akkodistestorg", - "AuthenticationType" : "ManagedIdentity" + "AuthenticationType" : "ManagedIdentity", + "excludedProjectsFromTeardown": [ + "DSC Pipeline Services" + ] } diff --git a/tests/Unit/Classes/API/007.APIRateLimit.tests.ps1 b/tests/Unit/Classes/API/007.APIRateLimit.tests.ps1 index f4ecdeba9..d3e380eaf 100644 --- a/tests/Unit/Classes/API/007.APIRateLimit.tests.ps1 +++ b/tests/Unit/Classes/API/007.APIRateLimit.tests.ps1 @@ -2,16 +2,20 @@ # Requires -Module DscResource.Common # Test if the class is defined -if ($Global:ClassesLoaded -eq $null) +if ($null -eq $Global:ClassesLoaded) { # Attempt to find the root of the repository $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - # Load the classes - $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } - . $preInitialize.FullName -RepositoryPath $RepositoryRoot + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly } Describe 'APIRateLimit' { + + BeforeAll { + Mock Write-Warning + } + Context 'Constructor with HashTable parameter' { It 'should initialize properties correctly when given a valid hashtable' { $validHashTable = @{ diff --git a/tests/Unit/Classes/Authentication/001.AuthenticationToken.tests.ps1 b/tests/Unit/Classes/Authentication/001.AuthenticationToken.tests.ps1 index 508b0d0d8..a174aec1a 100644 --- a/tests/Unit/Classes/Authentication/001.AuthenticationToken.tests.ps1 +++ b/tests/Unit/Classes/Authentication/001.AuthenticationToken.tests.ps1 @@ -1,13 +1,12 @@ # Requires -Module Pester -Version 5.0.0 # Test if the class is defined -if ($Global:ClassesLoaded -eq $null) +if ($null -eq $Global:ClassesLoaded) { # Attempt to find the root of the repository $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - # Load the classes - $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } - . $preInitialize.FullName -RepositoryPath $RepositoryRoot + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly } Describe 'AuthenticationToken Class' { diff --git a/tests/Unit/Classes/Authentication/002.PersonalAccessToken.tests.ps1 b/tests/Unit/Classes/Authentication/002.PersonalAccessToken.tests.ps1 index 1776c1a38..703e4aecb 100644 --- a/tests/Unit/Classes/Authentication/002.PersonalAccessToken.tests.ps1 +++ b/tests/Unit/Classes/Authentication/002.PersonalAccessToken.tests.ps1 @@ -1,13 +1,12 @@ # Requires -Module Pester -Version 5.0.0 # Test if the class is defined -if ($Global:ClassesLoaded -eq $null) +if ($null -eq $Global:ClassesLoaded) { # Attempt to find the root of the repository $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - # Load the classes - $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } - . $preInitialize.FullName -RepositoryPath $RepositoryRoot + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly } Describe 'PersonalAccessToken Class' { diff --git a/tests/Unit/Classes/Authentication/003.ManagedIdentityToken.tests.ps1 b/tests/Unit/Classes/Authentication/003.ManagedIdentityToken.tests.ps1 index f918c25de..48164ebfd 100644 --- a/tests/Unit/Classes/Authentication/003.ManagedIdentityToken.tests.ps1 +++ b/tests/Unit/Classes/Authentication/003.ManagedIdentityToken.tests.ps1 @@ -1,13 +1,12 @@ # Requires -Module Pester -Version 5.0.0 # Test if the class is defined -if ($Global:ClassesLoaded -eq $null) +if ($null -eq $Global:ClassesLoaded) { # Attempt to find the root of the repository $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - # Load the classes - $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } - . $preInitialize.FullName -RepositoryPath $RepositoryRoot + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly } @@ -34,7 +33,6 @@ Describe 'ManagedIdentityToken Class' { # Assert $mit.tokenType | Should -Be 'ManagedIdentity' - $mit.ConvertFromSecureString($mit.access_token) | Should -Be "TestAccessToken" $mit.expires_on | Should -Be $epochStart.AddMinutes(10) $mit.expires_in | Should -Be 600 $mit.resource | Should -Be "https://resource.url" @@ -57,6 +55,11 @@ Describe 'ManagedIdentityToken Class' { } Context 'isExpired Method' { + + BeforeAll { + $epochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) + } + It 'Should return true if the token is expired' { # Arrange $expiredTokenObj = [PSCustomObject]@{ @@ -97,6 +100,11 @@ Describe 'ManagedIdentityToken Class' { } Describe 'New-ManagedIdentityToken Function' { + + BeforeAll { + $epochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) + } + It 'Should create a new ManagedIdentityToken object with a valid PSCustomObject' { # Arrange $epochStart = [datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) diff --git a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/BeforeAll.ps1 b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/BeforeAll.ps1 new file mode 100644 index 000000000..0dc71e37c --- /dev/null +++ b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/BeforeAll.ps1 @@ -0,0 +1,8 @@ +# Test if the class is defined +if ($null -eq $Global:ClassesLoaded) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly +} diff --git a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceFunctionName.Tests.ps1 b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceFunctionName.Tests.ps1 index 3c7ea0e61..570fde031 100644 --- a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceFunctionName.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceFunctionName.Tests.ps1 @@ -1,126 +1,110 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 - -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) - - - Describe "$script:subModuleName\Classes\AzDevOpsApiDscResourceBase\$script:commandName" -Tag $script:tag { - - $testCasesValidRequiredActionWithFunctions = @( - @{ - RequiredAction = 'Get' - }, - @{ - RequiredAction = 'New' - }, - @{ - RequiredAction = 'Set' - }, - @{ - RequiredAction = 'Remove' - }, - @{ - RequiredAction = 'Test' - } - ) - - $testCasesValidRequiredActionWithoutFunctions = @( - @{ - RequiredAction = 'Error' - }, - @{ - RequiredAction = 'None' - } - ) - - $testCasesInvalidRequiredActions = @( - @{ - RequiredAction = 'SomethingInvalid' - }, - @{ - RequiredAction = $null - }, - @{ - RequiredAction = '' - } - ) +Describe "[AzDevOpsApiDscResourceBase]::GetResourceFunctionName() Tests" -Tag 'Unit', 'AzDevOpsApiDscResourceBase' { + + $testCasesValidRequiredActionWithFunctions = @( + @{ + RequiredAction = 'Get' + }, + @{ + RequiredAction = 'New' + }, + @{ + RequiredAction = 'Set' + }, + @{ + RequiredAction = 'Remove' + }, + @{ + RequiredAction = 'Test' + } + ) + + $testCasesValidRequiredActionWithoutFunctions = @( + @{ + RequiredAction = 'Error' + }, + @{ + RequiredAction = 'None' + } + ) + + $testCasesInvalidRequiredActions = @( + @{ + RequiredAction = 'SomethingInvalid' + }, + @{ + RequiredAction = $null + }, + @{ + RequiredAction = '' + } + ) - class AzDevOpsApiDscResourceBaseExample : AzDevOpsApiDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) - { - [DscProperty(Key)] - [string]$DscKey - [string]GetResourceName() - { - return 'ApiDscResourceBaseExample' - } + class AzDevOpsApiDscResourceBaseExample : AzDevOpsApiDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [DscProperty(Key)] + [string]$DscKey + + [string]GetResourceName() + { + return 'ApiDscResourceBaseExample' } + } - Context 'When called with valid "RequiredAction" values' { + Context 'When called with valid "RequiredAction" values' { - Context 'When "RequiredAction" value should have a related function' { + Context 'When "RequiredAction" value should have a related function' { - It 'Should not throw - ' -TestCases $testCasesValidRequiredActionWithFunctions { - param ([System.String]$RequiredAction) + It 'Should not throw - ' -TestCases $testCasesValidRequiredActionWithFunctions { + param ([System.String]$RequiredAction) - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - {$azDevOpsApiDscResourceBase.GetResourceFunctionName($RequiredAction)} | Should -Not -Throw - } + {$azDevOpsApiDscResourceBase.GetResourceFunctionName($RequiredAction)} | Should -Not -Throw + } - It 'Should return the correct, function name - ""' -TestCases $testCasesValidRequiredActionWithFunctions { - param ([System.String]$RequiredAction) + It 'Should return the correct, function name - ""' -TestCases $testCasesValidRequiredActionWithFunctions { + param ([System.String]$RequiredAction) - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - $azDevOpsApiDscResourceBase.GetResourceFunctionName($RequiredAction) | Should -Be "$($RequiredAction)-AzDevOpsApiDscResourceBaseExample" - } + $azDevOpsApiDscResourceBase.GetResourceFunctionName($RequiredAction) | Should -Be "$($RequiredAction)-ApiDscResourceBaseExample" } + } - Context 'When "RequiredAction" value should not have a related function' { + Context 'When "RequiredAction" value should not have a related function' { - It 'Should not throw - ' -TestCases $testCasesValidRequiredActionWithoutFunctions { - param ([System.String]$RequiredAction) + It 'Should not throw - ' -TestCases $testCasesValidRequiredActionWithoutFunctions { + param ([System.String]$RequiredAction) - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - {$azDevOpsApiDscResourceBase.GetResourceFunctionName($RequiredAction)} | Should -Not -Throw - } + {$azDevOpsApiDscResourceBase.GetResourceFunctionName($RequiredAction)} | Should -Not -Throw + } - It 'Should return the correct, function name - ""' -TestCases $testCasesValidRequiredActionWithoutFunctions { - param ([System.String]$RequiredAction) + It 'Should return the correct, function name - ""' -TestCases $testCasesValidRequiredActionWithoutFunctions { + param ([System.String]$RequiredAction) - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - $azDevOpsApiDscResourceBase.GetResourceFunctionName($RequiredAction) | Should -BeNullOrEmpty - } + $azDevOpsApiDscResourceBase.GetResourceFunctionName($RequiredAction) | Should -BeNullOrEmpty } } + } - Context 'When called with invalid "RequiredAction" values' { + Context 'When called with invalid "RequiredAction" values' { - It 'Should not throw - ' -TestCases $testCasesInvalidRequiredActions { - param ([System.String]$RequiredAction) + It 'Should not throw - ' -TestCases $testCasesInvalidRequiredActions { + param ([System.String]$RequiredAction) - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - {$azDevOpsApiDscResourceBase.GetResourceFunctionName($RequiredAction)} | Should -Throw - } + {$azDevOpsApiDscResourceBase.GetResourceFunctionName($RequiredAction)} | Should -Throw } } } + diff --git a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceId.Tests.ps1 b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceId.Tests.ps1 index 0abb19f63..0b0fc6348 100644 --- a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceId.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceId.Tests.ps1 @@ -1,51 +1,34 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 +Describe "[AzDevOpsApiDscResourceBase]::GetResourceId() Tests" -Tag 'Unit', 'AzDevOpsApiDscResourceBase' { -InModuleScope 'AzureDevOpsDsc' { + class AzDevOpsApiDscResourceBaseExample : AzDevOpsApiDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [DscProperty(Key)] + [string]$DscKey - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) - - - Describe "$script:subModuleName\Classes\AzDevOpsApiDscResourceBase\$script:commandName" -Tag $script:tag { - - - class AzDevOpsApiDscResourceBaseExample : AzDevOpsApiDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [DscProperty(Key)] - [string]$DscKey - - [string]GetResourceName() - { - return 'ApiDscResourceBaseExample' - } + return 'ApiDscResourceBaseExample' } + } - Context 'When called from instance of the class with the correct/expected, DSC Resource prefix' { + Context 'When called from instance of the class with the correct/expected, DSC Resource prefix' { - It 'Should not throw' { + It 'Should not throw' { - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - $azDevOpsApiDscResourceBase | Add-Member -Name 'ApiDscResourceBaseExampleId' -Value 'SomeIdValue' -MemberType NoteProperty + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase | Add-Member -Name 'ApiDscResourceBaseExampleId' -Value 'SomeIdValue' -MemberType NoteProperty - {$azDevOpsApiDscResourceBase.GetResourceId()} | Should -Not -Throw - } + {$azDevOpsApiDscResourceBase.GetResourceId()} | Should -Not -Throw + } - It 'Should return the same name as the DSC Resource/class without the expected prefix' { + It 'Should return the same name as the DSC Resource/class without the expected prefix' { - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - $azDevOpsApiDscResourceBase | Add-Member -Name 'ApiDscResourceBaseExampleId' -Value 'SomeIdValue' -MemberType NoteProperty + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase | Add-Member -Name 'ApiDscResourceBaseExampleId' -Value 'SomeIdValue' -MemberType NoteProperty - $azDevOpsApiDscResourceBase.GetResourceId() | Should -Be 'SomeIdValue' - } + $azDevOpsApiDscResourceBase.GetResourceId() | Should -Be 'SomeIdValue' } } } + diff --git a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceIdPropertyName.Tests.ps1 b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceIdPropertyName.Tests.ps1 index 320b3a6e7..b7c7f5beb 100644 --- a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceIdPropertyName.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceIdPropertyName.Tests.ps1 @@ -1,51 +1,35 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 +Describe "[AzDevOpsApiDscResourceBase]::GetResourceIdPropertyName() tests" -Tag 'Unit', 'AzDevOpsApiDscResourceBase' { -InModuleScope 'AzureDevOpsDsc' { + class AzDevOpsApiDscResourceBaseExample : AzDevOpsApiDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [DscProperty(Key)] + [string]$DscKey - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) - - - Describe "$script:subModuleName\Classes\AzDevOpsApiDscResourceBase\$script:commandName" -Tag $script:tag { - - class AzDevOpsApiDscResourceBaseExample : AzDevOpsApiDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [DscProperty(Key)] - [string]$DscKey - - [string]GetResourceName() - { - return 'ApiDscResourceBaseExample' - } + return 'ApiDscResourceBaseExample' } + } - Context 'When called from instance of the class with the correct/expected, DSC Resource prefix' { + Context 'When called from instance of the class with the correct/expected, DSC Resource prefix' { - It 'Should not throw' { + It 'Should not throw' { - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - $azDevOpsApiDscResourceBase | Add-Member -Name 'ApiDscResourceBaseExampleId' -Value 'SomeIdValue' -MemberType NoteProperty + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase | Add-Member -Name 'ApiDscResourceBaseExampleId' -Value 'SomeIdValue' -MemberType NoteProperty - {$azDevOpsApiDscResourceBase.GetResourceIdPropertyName()} | Should -Not -Throw - } + {$azDevOpsApiDscResourceBase.GetResourceIdPropertyName()} | Should -Not -Throw + } - It 'Should return the same name as the DSC Resource/class without the expected prefix' { + It 'Should return the same name as the DSC Resource/class without the expected prefix' { - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - $azDevOpsApiDscResourceBase | Add-Member -Name 'ApiDscResourceBaseExampleId' -Value 'SomeIdValue' -MemberType NoteProperty + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase | Add-Member -Name 'ApiDscResourceBaseExampleId' -Value 'SomeIdValue' -MemberType NoteProperty - $azDevOpsApiDscResourceBase.GetResourceIdPropertyName() | Should -Be 'ApiDscResourceBaseExampleId' - } + $azDevOpsApiDscResourceBase.GetResourceIdPropertyName() | Should -Be 'ApiDscResourceBaseExampleId' } } } + diff --git a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceKey.Tests.ps1 b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceKey.Tests.ps1 index e6b883b98..09134e483 100644 --- a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceKey.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceKey.Tests.ps1 @@ -1,52 +1,36 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 +Describe "[AzDevOpsApiDscResourceBase]::GetResourceKey() Tests" -Tag 'Unit', 'AzDevOpsApiDscResourceBase' { -InModuleScope 'AzureDevOpsDsc' { - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + Context 'When called from instance of the class with the correct/expected, DSC Resource prefix' { + class AzDevOpsApiDscResourceBaseWithKey : AzDevOpsApiDscResourceBase + { + [System.String]$ApiDscResourceBaseId - Describe "$script:subModuleName\Classes\AzDevOpsApiDscResourceBase\$script:commandName" -Tag $script:tag { - - - Context 'When called from instance of the class with the correct/expected, DSC Resource prefix' { + [DscProperty(Key)] + [System.String]$ApiDscResourceBaseKey + } - class AzDevOpsApiDscResourceBaseWithKey : AzDevOpsApiDscResourceBase - { - [System.String]$ApiDscResourceBaseId + It 'Should not throw' { - [DscProperty(Key)] - [System.String]$ApiDscResourceBaseKey + $azDevOpsApiDscResourceBaseWithKey = [AzDevOpsApiDscResourceBaseWithKey]@{ + ApiDscResourceBaseId = 'ApiDscResourceBaseIdValue' + ApiDscResourceBaseKey = 'ApiDscResourceBaseKeyValue' } - It 'Should not throw' { + {$azDevOpsApiDscResourceBaseWithKey.GetResourceKey()} | Should -Not -Throw + } - $azDevOpsApiDscResourceBaseWithKey = [AzDevOpsApiDscResourceBaseWithKey]@{ - ApiDscResourceBaseId = 'ApiDscResourceBaseIdValue' - ApiDscResourceBaseKey = 'ApiDscResourceBaseKeyValue' - } + It 'Should return the same name as the DSC Resource/class without the expected prefix' { - {$azDevOpsApiDscResourceBaseWithKey.GetResourceKey()} | Should -Not -Throw + $azDevOpsApiDscResourceBaseWithKey = [AzDevOpsApiDscResourceBaseWithKey]@{ + ApiDscResourceBaseId = 'ApiDscResourceBaseIdValue' + ApiDscResourceBaseKey = 'ApiDscResourceBaseKeyValue' } - It 'Should return the same name as the DSC Resource/class without the expected prefix' { - - $azDevOpsApiDscResourceBaseWithKey = [AzDevOpsApiDscResourceBaseWithKey]@{ - ApiDscResourceBaseId = 'ApiDscResourceBaseIdValue' - ApiDscResourceBaseKey = 'ApiDscResourceBaseKeyValue' - } - - $azDevOpsApiDscResourceBaseWithKey.GetResourceKey() | Should -Be 'ApiDscResourceBaseKeyValue' - } + $azDevOpsApiDscResourceBaseWithKey.GetResourceKey() | Should -Be 'ApiDscResourceBaseKeyValue' } } } + diff --git a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceKeyPropertyName.Tests.ps1 b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceKeyPropertyName.Tests.ps1 index 06117a6be..743837832 100644 --- a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceKeyPropertyName.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceKeyPropertyName.Tests.ps1 @@ -1,52 +1,36 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 +Describe "[AzDevOpsApiDscResourceBase]::GetResourceKeyPropertyName() tests" -Tag 'Unit', 'AzDevOpsApiDscResourceBase' { -InModuleScope 'AzureDevOpsDsc' { - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + Context 'When called from instance of the class with the correct/expected, DSC Resource prefix' { + class AzDevOpsApiDscResourceBaseWithKey : AzDevOpsApiDscResourceBase + { + [System.String]$ApiDscResourceBaseId - Describe "$script:subModuleName\Classes\AzDevOpsApiDscResourceBase\$script:commandName" -Tag $script:tag { - - - Context 'When called from instance of the class with the correct/expected, DSC Resource prefix' { + [DscProperty(Key)] + [System.String]$ApiDscResourceBaseKey + } - class AzDevOpsApiDscResourceBaseWithKey : AzDevOpsApiDscResourceBase - { - [System.String]$ApiDscResourceBaseId + It 'Should not throw' { - [DscProperty(Key)] - [System.String]$ApiDscResourceBaseKey + $azDevOpsApiDscResourceBaseWithKey = [AzDevOpsApiDscResourceBaseWithKey]@{ + ApiDscResourceBaseId = 'ApiDscResourceBaseIdValue' + ApiDscResourceBaseKey = 'ApiDscResourceBaseKeyValue' } - It 'Should not throw' { + {$azDevOpsApiDscResourceBaseWithKey.GetResourceKeyPropertyName()} | Should -Not -Throw + } - $azDevOpsApiDscResourceBaseWithKey = [AzDevOpsApiDscResourceBaseWithKey]@{ - ApiDscResourceBaseId = 'ApiDscResourceBaseIdValue' - ApiDscResourceBaseKey = 'ApiDscResourceBaseKeyValue' - } + It 'Should return the same name as the DSC Resource/class without the expected prefix' { - {$azDevOpsApiDscResourceBaseWithKey.GetResourceKeyPropertyName()} | Should -Not -Throw + $azDevOpsApiDscResourceBaseWithKey = [AzDevOpsApiDscResourceBaseWithKey]@{ + ApiDscResourceBaseId = 'ApiDscResourceBaseIdValue' + ApiDscResourceBaseKey = 'ApiDscResourceBaseKeyValue' } - It 'Should return the same name as the DSC Resource/class without the expected prefix' { - - $azDevOpsApiDscResourceBaseWithKey = [AzDevOpsApiDscResourceBaseWithKey]@{ - ApiDscResourceBaseId = 'ApiDscResourceBaseIdValue' - ApiDscResourceBaseKey = 'ApiDscResourceBaseKeyValue' - } - - $azDevOpsApiDscResourceBaseWithKey.GetResourceKeyPropertyName() | Should -Be 'ApiDscResourceBaseKey' - } + $azDevOpsApiDscResourceBaseWithKey.GetResourceKeyPropertyName() | Should -Be 'ApiDscResourceBaseKey' } } } + diff --git a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceName.Tests.ps1 b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceName.Tests.ps1 index e8d4e87ee..d9466bf26 100644 --- a/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceName.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsApiDscResourceBase/GetResourceName.Tests.ps1 @@ -1,81 +1,65 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 +Describe "[AzDevOpsApiDscResourceBase]::GetResourceName() Tests" -Tag 'Unit', 'AzDevOpsApiDscResourceBase' { -InModuleScope 'AzureDevOpsDsc' { - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + $DscResourcePrefix = 'AzDevOps' + Context 'When called from instance of the class without the correct/expected, DSC Resource prefix' { - Describe "$script:subModuleName\Classes\AzDevOpsApiDscResourceBase\$script:commandName" -Tag $script:tag { + class DscResourceWithWrongPrefix : AzDevOpsApiDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [DscProperty(Key)] + [string]$DscKey - - $DscResourcePrefix = 'AzDevOps' - - Context 'When called from instance of the class without the correct/expected, DSC Resource prefix' { - - class DscResourceWithWrongPrefix : AzDevOpsApiDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [DscProperty(Key)] - [string]$DscKey - - [string]GetResourceName() - { - return 'DscResourceWithWrongPrefix' - } + return 'DscResourceWithWrongPrefix' } - $dscResourceWithWrongPrefix = [DscResourceWithWrongPrefix]@{} + } + $dscResourceWithWrongPrefix = [DscResourceWithWrongPrefix]@{} - It 'Should not throw' { + It 'Should not throw' { - $dscResourceWithWrongPrefix = [DscResourceWithWrongPrefix]::new() + $dscResourceWithWrongPrefix = [DscResourceWithWrongPrefix]::new() - {$dscResourceWithWrongPrefix.GetResourceName()} | Should -Not -Throw - } + {$dscResourceWithWrongPrefix.GetResourceName()} | Should -Not -Throw + } - It 'Should return the same name as the DSC Resource/class' { + It 'Should return the same name as the DSC Resource/class' { - $dscResourceWithWrongPrefix = [DscResourceWithWrongPrefix]::new() + $dscResourceWithWrongPrefix = [DscResourceWithWrongPrefix]::new() - $dscResourceWithWrongPrefix.GetResourceName() | Should -Be 'DscResourceWithWrongPrefix' - } + $dscResourceWithWrongPrefix.GetResourceName() | Should -Be 'DscResourceWithWrongPrefix' } + } - Context 'When called from instance of the class with the correct/expected, DSC Resource prefix' { + Context 'When called from instance of the class with the correct/expected, DSC Resource prefix' { - class AzDevOpsApiDscResourceBaseExample : AzDevOpsApiDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) - { - [DscProperty(Key)] - [string]$DscKey + class AzDevOpsApiDscResourceBaseExample : AzDevOpsApiDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [DscProperty(Key)] + [string]$DscKey - [string]GetResourceName() - { - return 'ApiDscResourceBaseExample' - } + [string]GetResourceName() + { + return 'ApiDscResourceBaseExample' } + } - It 'Should not throw' { + It 'Should not throw' { - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - {$azDevOpsApiDscResourceBase.GetResourceName()} | Should -Not -Throw - } + {$azDevOpsApiDscResourceBase.GetResourceName()} | Should -Not -Throw + } - It 'Should return the same name as the DSC Resource/class without the expected prefix' { + It 'Should return the same name as the DSC Resource/class without the expected prefix' { - $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() + $azDevOpsApiDscResourceBase = [AzDevOpsApiDscResourceBaseExample]::new() - $azDevOpsApiDscResourceBase.GetResourceName() | Should -Be 'AzDevOpsApiDscResourceBaseExample'.Replace('AzDevOps','') - } + $azDevOpsApiDscResourceBase.GetResourceName() | Should -Be 'AzDevOpsApiDscResourceBaseExample'.Replace('AzDevOps','') } } } + diff --git a/tests/Unit/Classes/AzDevOpsDscResourceBase/AzDevOpsDscResourceBase.Initialization.Tests.ps1 b/tests/Unit/Classes/AzDevOpsDscResourceBase/AzDevOpsDscResourceBase.Initialization.Tests.ps1 deleted file mode 100644 index e9f0ef435..000000000 --- a/tests/Unit/Classes/AzDevOpsDscResourceBase/AzDevOpsDscResourceBase.Initialization.Tests.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 - -# Initialize tests for module function 'Classes' -. $PSScriptRoot\..\Classes.TestInitialization.ps1 - -# Note: Use of this functionality seems to pre-load the module and classes which subsquent tests can use -# which works around difficulty of referencing classes in 'source' directory when code coverage is -# using the dynamically/build-defined, 'output' directory. -InModuleScope 'AzureDevOpsDsc' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - -} diff --git a/tests/Unit/Classes/AzDevOpsDscResourceBase/BeforeAll.ps1 b/tests/Unit/Classes/AzDevOpsDscResourceBase/BeforeAll.ps1 new file mode 100644 index 000000000..0dc71e37c --- /dev/null +++ b/tests/Unit/Classes/AzDevOpsDscResourceBase/BeforeAll.ps1 @@ -0,0 +1,8 @@ +# Test if the class is defined +if ($null -eq $Global:ClassesLoaded) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly +} diff --git a/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateObject.Tests.ps1 b/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateObject.Tests.ps1 index 9b20b1196..be235c0ee 100644 --- a/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateObject.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateObject.Tests.ps1 @@ -1,109 +1,88 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 +Describe "[AzDevOpsDscResourceBase]::GetDscCurrentStateObject() Tests" -Tag 'Unit', 'AzDevOpsDscResourceBase' { -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 + Context 'When no "DscCurrentStateResourceObject" object returned' { -InModuleScope 'AzureDevOpsDsc' { + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [string]$ApiUri = 'https://some.api/_apis/' + [string]$Pat = '1234567890123456789012345678901234567890123456789012' - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' + [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - Describe "$script:subModuleName\Classes\AzDevOpsDscResourceBase\$script:commandName" -Tag $script:tag { - - - Context 'When no "DscCurrentStateResourceObject" object returned'{ - - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' - - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' - - [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } - - [Hashtable]GetDscCurrentStateObjectGetParameters() - { - return @{} - } - - [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) - { - return $null - } + return 'AzDevOpsDscResourceBaseExample' } - It 'Should not throw' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - - {$azDevOpsDscResourceBaseExample.GetDscCurrentStateObject()} | Should -Not -Throw + [Hashtable]GetDscCurrentStateObjectGetParameters() + { + return @{} } - It 'Should return an object with "Ensure" property value of "Absent"' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObject().Ensure | Should -Be 'Absent' + [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) + { + return $null } + } + It 'Should not throw' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + + {$azDevOpsDscResourceBaseExample.GetDscCurrentStateObject()} | Should -Not -Throw } + It 'Should return an object with "Ensure" property value of "Absent"' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObject().Ensure | Should -Be 'Absent' + } - Context 'When no "DscCurrentStateResourceObject" object returned'{ + } - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) - { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' + Context 'When no "DscCurrentStateResourceObject" object returned' { - [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [string]$ApiUri = 'https://some.api/_apis/' + [string]$Pat = '1234567890123456789012345678901234567890123456789012' - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' - [Hashtable]GetDscCurrentStateObjectGetParameters() - { - return @{} - } + [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) - { - return [PSObject]@{ - Ensure = 'Present' - } - } + [string]GetResourceName() + { + return 'AzDevOpsDscResourceBaseExample' } - It 'Should not throw' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + [Hashtable]GetDscCurrentStateObjectGetParameters() + { + return @{} + } - {$azDevOpsDscResourceBaseExample.GetDscCurrentStateObject()} | Should -Not -Throw + [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) + { + return [PSObject]@{ + Ensure = 'Present' + } } + } - It 'Should return an object with "Ensure" property value of "Present"' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + It 'Should not throw' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObject().Ensure | Should -Be 'Present' - } + {$azDevOpsDscResourceBaseExample.GetDscCurrentStateObject()} | Should -Not -Throw + } + It 'Should return an object with "Ensure" property value of "Present"' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObject().Ensure | Should -Be 'Present' } } + } diff --git a/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateObjectGetParameters.Tests.ps1 b/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateObjectGetParameters.Tests.ps1 index 05aae6bc0..9f7d563fd 100644 --- a/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateObjectGetParameters.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateObjectGetParameters.Tests.ps1 @@ -1,193 +1,175 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 +Describe "[AzDevOpsDscResourceBase]::GetDscCurrentStateObjectGetParameters() Tests" -Tag 'Unit', 'AzDevOpsDscResourceBase' { -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 -InModuleScope 'AzureDevOpsDsc' { + Context 'When a "ResourceId" property value is present'{ - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [DscProperty(Key)] + [string]$ApiUri = 'https://some.api/_apis/' + [DscProperty(Key)] + [string]$Pat = '1234567890123456789012345678901234567890123456789012' - Describe "$script:subModuleName\Classes\AzDevOpsDscResourceBase\$script:commandName" -Tag $script:tag { + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleKey = 'AzDevOpsDscResourceBaseExampleKeyValue' - - Context 'When a "ResourceId" property value is present'{ - - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' - - - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } - - - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleKey = 'AzDevOpsDscResourceBaseExampleKeyValue' - - [string]GetResourceKeyPropertyName() - { - return 'AzDevOpsDscResourceBaseExampleKey' - } - - [string]GetResourceKey() - { - return 'AzDevOpsDscResourceBaseExampleKeyValue' - } - + return 'AzDevOpsDscResourceBaseExample' + } - [DscProperty()] - [string]$AzDevOpsDscResourceBaseExampleId = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID + [string]GetResourceKeyPropertyName() + { + return 'AzDevOpsDscResourceBaseExampleKey' + } - [string]GetResourceIdPropertyName() - { - return 'AzDevOpsDscResourceBaseExampleId' - } + [string]GetResourceKey() + { + return 'AzDevOpsDscResourceBaseExampleKeyValue' + } - [string]GetResourceId() - { - return '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - } + [DscProperty()] + [string]$AzDevOpsDscResourceBaseExampleId = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID + [string]GetResourceIdPropertyName() + { + return 'AzDevOpsDscResourceBaseExampleId' } - It 'Should not throw' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - - {$azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()} | Should -Not -Throw + [string]GetResourceId() + { + return '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID } - It 'Should return an object with "ApiUri" property value equal to object instance "ApiUri" value' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters().ApiUri | Should -Be $azDevOpsDscResourceBaseExample.ApiUri - } + } - It 'Should return an object with "Pat" property value equal to object instance "Pat" value' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + It 'Should not throw' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + {$azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()} | Should -Not -Throw + } - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters().Pat | Should -Be $azDevOpsDscResourceBaseExample.Pat - } + It 'Should return an object with "ApiUri" property value equal to object instance "ApiUri" value' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters().ApiUri | Should -Be $azDevOpsDscResourceBaseExample.ApiUri + } - It 'Should return an object with "ResourceKey" property value equal to object instance "ResourceKey" value' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + It 'Should return an object with "Pat" property value equal to object instance "Pat" value' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters().Pat | Should -Be $azDevOpsDscResourceBaseExample.Pat + } - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceKeyPropertyName())" | - Should -Be $azDevOpsDscResourceBaseExample."$($azDevOpsDscResourceBaseExample.GetResourceKeyPropertyName())" - } + It 'Should return an object with "ResourceKey" property value equal to object instance "ResourceKey" value' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - It 'Should return an object with "ResourceId" property value equal to object instance "ResourceId" value' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceKeyPropertyName())" | + Should -Be $azDevOpsDscResourceBaseExample."$($azDevOpsDscResourceBaseExample.GetResourceKeyPropertyName())" + } - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" | - Should -Be $azDevOpsDscResourceBaseExample."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" - } + It 'Should return an object with "ResourceId" property value equal to object instance "ResourceId" value' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - It 'Should return an object with "ResourceId" property value that is not $null' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" | + Should -Be $azDevOpsDscResourceBaseExample."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" + } - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" | - Should -Not -BeNullOrEmpty - } + It 'Should return an object with "ResourceId" property value that is not $null' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" | + Should -Not -BeNullOrEmpty } + } - Context 'When a "ResourceId" property value is not present'{ - - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) - { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' + Context 'When a "ResourceId" property value is not present'{ - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [DscProperty(Key)] + [string]$ApiUri = 'https://some.api/_apis/' + [DscProperty(Key)] + [string]$Pat = '1234567890123456789012345678901234567890123456789012' - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleKey = 'AzDevOpsDscResourceBaseExampleKeyValue' + [string]GetResourceName() + { + return 'AzDevOpsDscResourceBaseExample' + } - [string]GetResourceKeyPropertyName() - { - return 'AzDevOpsDscResourceBaseExampleKey' - } - [string]GetResourceKey() - { - return 'AzDevOpsDscResourceBaseExampleKeyValue' - } + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleKey = 'AzDevOpsDscResourceBaseExampleKeyValue' + [string]GetResourceKeyPropertyName() + { + return 'AzDevOpsDscResourceBaseExampleKey' + } - [DscProperty()] - [string]$AzDevOpsDscResourceBaseExampleId + [string]GetResourceKey() + { + return 'AzDevOpsDscResourceBaseExampleKeyValue' + } - [string]GetResourceIdPropertyName() - { - return 'AzDevOpsDscResourceBaseExampleId' - } - [string]GetResourceId() - { - return $null - } + [DscProperty()] + [string]$AzDevOpsDscResourceBaseExampleId + [string]GetResourceIdPropertyName() + { + return 'AzDevOpsDscResourceBaseExampleId' + } + [string]GetResourceId() + { + return $null } - It 'Should not throw' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - {$azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()} | Should -Not -Throw - } + } - It 'Should return an object with "ApiUri" property value equal to object instance "ApiUri" value' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + It 'Should not throw' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters().ApiUri | Should -Be $azDevOpsDscResourceBaseExample.ApiUri - } + {$azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()} | Should -Not -Throw + } - It 'Should return an object with "Pat" property value equal to object instance "Pat" value' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + It 'Should return an object with "ApiUri" property value equal to object instance "ApiUri" value' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters().Pat | Should -Be $azDevOpsDscResourceBaseExample.Pat - } + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters().ApiUri | Should -Be $azDevOpsDscResourceBaseExample.ApiUri + } - It 'Should return an object with "ResourceKey" property value equal to object instance "ResourceKey" value' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + It 'Should return an object with "Pat" property value equal to object instance "Pat" value' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceKeyPropertyName())" | - Should -Be $azDevOpsDscResourceBaseExample."$($azDevOpsDscResourceBaseExample.GetResourceKeyPropertyName())" - } + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters().Pat | Should -Be $azDevOpsDscResourceBaseExample.Pat + } - It 'Should return an object with "ResourceId" property value equal to object instance "ResourceId" value' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + It 'Should return an object with "ResourceKey" property value equal to object instance "ResourceKey" value' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" | - Should -Be $azDevOpsDscResourceBaseExample."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" - } + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceKeyPropertyName())" | + Should -Be $azDevOpsDscResourceBaseExample."$($azDevOpsDscResourceBaseExample.GetResourceKeyPropertyName())" + } - It 'Should return an object with "ResourceId" property value that is $null' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + It 'Should return an object with "ResourceId" property value equal to object instance "ResourceId" value' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" | - Should -BeNullOrEmpty - } + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" | + Should -Be $azDevOpsDscResourceBaseExample."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" + } + + It 'Should return an object with "ResourceId" property value that is $null' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + $azDevOpsDscResourceBaseExample.GetDscCurrentStateObjectGetParameters()."$($azDevOpsDscResourceBaseExample.GetResourceIdPropertyName())" | + Should -BeNullOrEmpty } } + } diff --git a/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateResourceObject.Tests.ps1 b/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateResourceObject.Tests.ps1 index a4d9ffe62..edb40be2d 100644 --- a/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateResourceObject.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsDscResourceBase/GetDscCurrentStateResourceObject.Tests.ps1 @@ -1,118 +1,102 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 +Describe "[AzDevOpsDscResourceBase]::GetDscCurrentStateResourceObject() Tests" -Tag 'Unit', 'AzDevOpsDscResourceBase' { -InModuleScope 'AzureDevOpsDsc' { - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + Context 'When no "DscCurrentStateResourceObject" object returned'{ + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [string]$ApiUri = 'https://some.api/_apis/' + [string]$Pat = '1234567890123456789012345678901234567890123456789012' - Describe "$script:subModuleName\Classes\AzDevOpsDscResourceBase\$script:commandName" -Tag $script:tag { + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' + [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - Context 'When no "DscCurrentStateResourceObject" object returned'{ - - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' - - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' - - [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } + return 'AzDevOpsDscResourceBaseExample' + } - [Hashtable]GetDscCurrentStateObjectGetParameters() - { - return @{} - } + [Hashtable]GetDscCurrentStateObjectGetParameters() + { + return @{} + } - [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) - { - return $null - } + [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) + { + return $null + } - [string]GetResourceFunctionName([RequiredAction]$RequiredAction) - { - return 'Get-Module' - } - [Hashtable]GetDesiredStateParameters([Hashtable]$Current, [Hashtable]$Desired, [RequiredAction]$RequiredAction) - { - return @{ - Name = 'SomeModuleThatWillNotExist' - } + [string]GetResourceFunctionName([RequiredAction]$RequiredAction) + { + return 'Get-Module' + } + [Hashtable]GetDesiredStateParameters([Hashtable]$Current, [Hashtable]$Desired, [RequiredAction]$RequiredAction) + { + return @{ + Name = 'SomeModuleThatWillNotExist' } } + } - It 'Should not throw' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + It 'Should not throw' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - {$azDevOpsDscResourceBaseExample.GetDscCurrentStateResourceObject(@{})} | Should -Not -Throw - } + {$azDevOpsDscResourceBaseExample.GetDscCurrentStateResourceObject(@{})} | Should -Not -Throw } + } - Context 'When no "DscCurrentStateResourceObject" object returned'{ + Context 'When no "DscCurrentStateResourceObject" object returned'{ - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) - { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [string]$ApiUri = 'https://some.api/_apis/' + [string]$Pat = '1234567890123456789012345678901234567890123456789012' - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' - [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID + [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } + [string]GetResourceName() + { + return 'AzDevOpsDscResourceBaseExample' + } - [Hashtable]GetDscCurrentStateObjectGetParameters() - { - return @{} - } + [Hashtable]GetDscCurrentStateObjectGetParameters() + { + return @{} + } - [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) - { - return [PSObject]@{ - Ensure = 'Present' - } + [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) + { + return [PSObject]@{ + Ensure = 'Present' } + } - [string]GetResourceFunctionName([RequiredAction]$RequiredAction) - { - return 'Get-Module' - } - [Hashtable]GetDesiredStateParameters([Hashtable]$Current, [Hashtable]$Desired, [RequiredAction]$RequiredAction) - { - return @{ - Name = 'SomeModuleThatWillNotExist' - } + [string]GetResourceFunctionName([RequiredAction]$RequiredAction) + { + return 'Get-Module' + } + [Hashtable]GetDesiredStateParameters([Hashtable]$Current, [Hashtable]$Desired, [RequiredAction]$RequiredAction) + { + return @{ + Name = 'SomeModuleThatWillNotExist' } } + } - It 'Should not throw' { - $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() - - {$azDevOpsDscResourceBaseExample.GetDscCurrentStateResourceObject(@{})} | Should -Not -Throw - } + It 'Should not throw' { + $azDevOpsDscResourceBaseExample = [AzDevOpsDscResourceBaseExample]::new() + {$azDevOpsDscResourceBaseExample.GetDscCurrentStateResourceObject(@{})} | Should -Not -Throw } } + } + diff --git a/tests/Unit/Classes/AzDevOpsDscResourceBase/GetPostSetWaitTimeSeconds.Tests.ps1 b/tests/Unit/Classes/AzDevOpsDscResourceBase/GetPostSetWaitTimeSeconds.Tests.ps1 index 31f4b584b..c01aa8ce9 100644 --- a/tests/Unit/Classes/AzDevOpsDscResourceBase/GetPostSetWaitTimeSeconds.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsDscResourceBase/GetPostSetWaitTimeSeconds.Tests.ps1 @@ -1,65 +1,49 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 +Describe "[AzDevOpsDscResourceBase]::GetPostSetWaitTimeSeconds() Tests" -Tag 'Unit', 'AzDevOpsDscResourceBase' { -InModuleScope 'AzureDevOpsDsc' { + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [string]$ApiUri = 'https://some.api/_apis/' + [string]$Pat = '1234567890123456789012345678901234567890123456789012' - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' + [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - Describe "$script:subModuleName\Classes\AzDevOpsDscResourceBase\$script:commandName" -Tag $script:tag { - - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' - - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' - - [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } - - [Hashtable]GetDscCurrentStateObjectGetParameters() - { - return @{} - } + return 'AzDevOpsDscResourceBaseExample' + } - [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) - { - return $null - } + [Hashtable]GetDscCurrentStateObjectGetParameters() + { + return @{} } - Context 'When no "Set()" method is invoked'{ + [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) + { + return $null + } + } - It 'Should not throw' { + Context 'When no "Set()" method is invoked'{ - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + It 'Should not throw' { - { $azDevOpsDscResourceBase.GetPostSetWaitTimeMs() } | Should -Not -Throw - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - It 'Should return $null' { + { $azDevOpsDscResourceBase.GetPostSetWaitTimeMs() } | Should -Not -Throw + } - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + It 'Should return $null' { - $azDevOpsDscResourceBase.GetPostSetWaitTimeMs() | Should -Be 2000 - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + $azDevOpsDscResourceBase.GetPostSetWaitTimeMs() | Should -Be 2000 } } + } + diff --git a/tests/Unit/Classes/AzDevOpsDscResourceBase/Set.Tests.ps1 b/tests/Unit/Classes/AzDevOpsDscResourceBase/Set.Tests.ps1 index 12a0eda99..920235a53 100644 --- a/tests/Unit/Classes/AzDevOpsDscResourceBase/Set.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsDscResourceBase/Set.Tests.ps1 @@ -1,69 +1,52 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 +Describe "[AzDevOpsDscResourceBase]::Set() Tests" -Tag 'Unit', 'AzDevOpsDscResourceBase' { -InModuleScope 'AzureDevOpsDsc' { + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [string]$ApiUri = 'https://some.api/_apis/' + [string]$Pat = '1234567890123456789012345678901234567890123456789012' - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' + [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - Describe "$script:subModuleName\Classes\AzDevOpsDscResourceBase\$script:commandName" -Tag $script:tag { - - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' - - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' - - [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } - - [Hashtable]GetDscCurrentStateObjectGetParameters() - { - return @{} - } + return 'AzDevOpsDscResourceBaseExample' + } - [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) - { - return $null - } + [Hashtable]GetDscCurrentStateObjectGetParameters() + { + return @{} } - Context 'When no "Set()" method is invoked'{ + [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) + { + return $null + } + } - It 'Should not throw' { + Context 'When no "Set()" method is invoked'{ - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$setToDesiredState = {} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name SetToDesiredState -Value $setToDesiredState -Force + It 'Should not throw' { - { $azDevOpsDscResourceBase.Set() } | Should -Not -Throw - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$setToDesiredState = {} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name SetToDesiredState -Value $setToDesiredState -Force - It 'Should return $null' { + { $azDevOpsDscResourceBase.Set() } | Should -Not -Throw + } - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$setToDesiredState = {} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name SetToDesiredState -Value $setToDesiredState -Force + It 'Should return $null' { - $azDevOpsDscResourceBase.Set() | Should -BeNullOrEmpty - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$setToDesiredState = {} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name SetToDesiredState -Value $setToDesiredState -Force + $azDevOpsDscResourceBase.Set() | Should -BeNullOrEmpty } } + } diff --git a/tests/Unit/Classes/AzDevOpsDscResourceBase/SetToDesiredState.Tests.ps1 b/tests/Unit/Classes/AzDevOpsDscResourceBase/SetToDesiredState.Tests.ps1 index 5038ec86e..210e4bf37 100644 --- a/tests/Unit/Classes/AzDevOpsDscResourceBase/SetToDesiredState.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsDscResourceBase/SetToDesiredState.Tests.ps1 @@ -1,138 +1,123 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 -InModuleScope 'AzureDevOpsDsc' { +Describe "[AzDevOpsDscResourceBase]::SetToDesiredState() Tests" -Tag 'Unit', 'AzDevOpsDscResourceBase' { - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [string]$ApiUri = 'https://some.api/_apis/' + [string]$Pat = '1234567890123456789012345678901234567890123456789012' + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' - Describe "$script:subModuleName\Classes\AzDevOpsDscResourceBase\$script:commandName" -Tag $script:tag { + [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' - - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' - - [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } - - [Hashtable]GetDscCurrentStateObjectGetParameters() - { - return @{} - } + return 'AzDevOpsDscResourceBaseExample' + } - [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) - { - return $null - } + [Hashtable]GetDscCurrentStateObjectGetParameters() + { + return @{} + } - [string]GetResourceFunctionName([RequiredAction]$RequiredAction) - { - return 'Get-Module' - } - [Hashtable]GetDesiredStateParameters([Hashtable]$Current, [Hashtable]$Desired, [RequiredAction]$RequiredAction) - { - return @{ - Name = 'SomeModuleThatWillNotExist' - } - } + [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) + { + return $null + } - [Int32]GetPostSetWaitTimeMs() - { - return 0 + [string]GetResourceFunctionName([RequiredAction]$RequiredAction) + { + return 'Get-Module' + } + [Hashtable]GetDesiredStateParameters([Hashtable]$Current, [Hashtable]$Desired, [RequiredAction]$RequiredAction) + { + return @{ + Name = 'SomeModuleThatWillNotExist' } } - $testCasesValidRequiredActionThatDoNotRequireAction = @( - @{ - RequiredAction = [RequiredAction]::Get - }, - @{ - RequiredAction = [RequiredAction]::Test - }, - @{ - RequiredAction = [RequiredAction]::Error - } - ) - - $testCasesValidRequiredActionThatRequireAction = @( - @{ - RequiredAction = [RequiredAction]::New - }, - @{ - RequiredAction = [RequiredAction]::Set - }, - @{ - RequiredAction = [RequiredAction]::Remove - } - ) + [Int32]GetPostSetWaitTimeMs() + { + return 0 + } + } + $testCasesValidRequiredActionThatDoNotRequireAction = @( + @{ + RequiredAction = [RequiredAction]::Get + }, + @{ + RequiredAction = [RequiredAction]::Test + }, + @{ + RequiredAction = [RequiredAction]::Error + } + ) + + $testCasesValidRequiredActionThatRequireAction = @( + @{ + RequiredAction = [RequiredAction]::New + }, + @{ + RequiredAction = [RequiredAction]::Set + }, + @{ + RequiredAction = [RequiredAction]::Remove + } + ) - Context 'When no "GetDscRequiredAction()" method returns a "RequiredAction" that requires an action'{ - It 'Should not throw - ""' -TestCases $testCasesValidRequiredActionThatRequireAction { - param ([RequiredAction]$RequiredAction) + Context 'When no "GetDscRequiredAction()" method returns a "RequiredAction" that requires an action'{ - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force + It 'Should not throw - ""' -TestCases $testCasesValidRequiredActionThatRequireAction { + param ([RequiredAction]$RequiredAction) - { $azDevOpsDscResourceBase.SetToDesiredState() } | Should -Not -Throw - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force - It 'Should return $null - ""' -TestCases $testCasesValidRequiredActionThatDoNotRequireAction { - param ([RequiredAction]$RequiredAction) + { $azDevOpsDscResourceBase.SetToDesiredState() } | Should -Not -Throw + } - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + It 'Should return $null - ""' -TestCases $testCasesValidRequiredActionThatDoNotRequireAction { + param ([RequiredAction]$RequiredAction) - [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - $azDevOpsDscResourceBase.SetToDesiredState() | Should -BeNullOrEmpty - } + [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force + $azDevOpsDscResourceBase.SetToDesiredState() | Should -BeNullOrEmpty } + } - Context 'When no "GetDscRequiredAction()" method returns a "RequiredAction" that requires no action'{ - It 'Should not throw - ""' -TestCases $testCasesValidRequiredActionThatDoNotRequireAction { - param ([RequiredAction]$RequiredAction) + Context 'When no "GetDscRequiredAction()" method returns a "RequiredAction" that requires no action'{ - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force + It 'Should not throw - ""' -TestCases $testCasesValidRequiredActionThatDoNotRequireAction { + param ([RequiredAction]$RequiredAction) - { $azDevOpsDscResourceBase.SetToDesiredState() } | Should -Not -Throw - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force - It 'Should return $null - ""' -TestCases $testCasesValidRequiredActionThatDoNotRequireAction { - param ([RequiredAction]$RequiredAction) + { $azDevOpsDscResourceBase.SetToDesiredState() } | Should -Not -Throw + } - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force + It 'Should return $null - ""' -TestCases $testCasesValidRequiredActionThatDoNotRequireAction { + param ([RequiredAction]$RequiredAction) - $azDevOpsDscResourceBase.SetToDesiredState() | Should -BeNullOrEmpty - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force + $azDevOpsDscResourceBase.SetToDesiredState() | Should -BeNullOrEmpty } } + } + diff --git a/tests/Unit/Classes/AzDevOpsDscResourceBase/Test.Tests.ps1 b/tests/Unit/Classes/AzDevOpsDscResourceBase/Test.Tests.ps1 index 42f4a46d6..8d88ca8f2 100644 --- a/tests/Unit/Classes/AzDevOpsDscResourceBase/Test.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsDscResourceBase/Test.Tests.ps1 @@ -1,92 +1,76 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 +Describe "[AzDevOpsDscResourceBase]::Test() Tests" -Tag 'Unit', 'AzDevOpsDscResourceBase' { -InModuleScope 'AzureDevOpsDsc' { + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [string]$ApiUri = 'https://some.api/_apis/' + [string]$Pat = '1234567890123456789012345678901234567890123456789012' - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' + [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - Describe "$script:subModuleName\Classes\AzDevOpsDscResourceBase\$script:commandName" -Tag $script:tag { - - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' - - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' - - [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } - - [Hashtable]GetDscCurrentStateObjectGetParameters() - { - return @{} - } + return 'AzDevOpsDscResourceBaseExample' + } - [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) - { - return $null - } + [Hashtable]GetDscCurrentStateObjectGetParameters() + { + return @{} } - Context 'When no "TestDesiredState()" returns $true'{ + [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) + { + return $null + } + } - It 'Should not throw' { + Context 'When no "TestDesiredState()" returns $true'{ - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$testDesiredState = {return $true} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name TestDesiredState -Value $testDesiredState -Force + It 'Should not throw' { - { $azDevOpsDscResourceBase.Test() } | Should -BeTrue - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$testDesiredState = {return $true} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name TestDesiredState -Value $testDesiredState -Force - It 'Should return $true' { + { $azDevOpsDscResourceBase.Test() } | Should -BeTrue + } - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$testDesiredState = {return $true} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name TestDesiredState -Value $testDesiredState -Force + It 'Should return $true' { - $azDevOpsDscResourceBase.Test() | Should -BeTrue - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$testDesiredState = {return $true} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name TestDesiredState -Value $testDesiredState -Force + $azDevOpsDscResourceBase.Test() | Should -BeTrue } + } - Context 'When no "TestDesiredState()" returns $false'{ - It 'Should not throw' { + Context 'When no "TestDesiredState()" returns $false'{ - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$testDesiredState = {return $false} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name TestDesiredState -Value $testDesiredState -Force + It 'Should not throw' { - { $azDevOpsDscResourceBase.Test() } | Should -Not -Throw - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$testDesiredState = {return $false} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name TestDesiredState -Value $testDesiredState -Force - It 'Should return $false' { + { $azDevOpsDscResourceBase.Test() } | Should -Not -Throw + } - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$testDesiredState = {return $false} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name TestDesiredState -Value $testDesiredState -Force + It 'Should return $false' { - $azDevOpsDscResourceBase.Test() | Should -BeFalse - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$testDesiredState = {return $false} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name TestDesiredState -Value $testDesiredState -Force + $azDevOpsDscResourceBase.Test() | Should -BeFalse } } + } + diff --git a/tests/Unit/Classes/AzDevOpsDscResourceBase/TestDesiredState.Tests.ps1 b/tests/Unit/Classes/AzDevOpsDscResourceBase/TestDesiredState.Tests.ps1 index a88015039..1602ae7f4 100644 --- a/tests/Unit/Classes/AzDevOpsDscResourceBase/TestDesiredState.Tests.ps1 +++ b/tests/Unit/Classes/AzDevOpsDscResourceBase/TestDesiredState.Tests.ps1 @@ -1,115 +1,100 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 -InModuleScope 'AzureDevOpsDsc' { +Describe "[AzDevOpsDscResourceBase]::TestDesiredState() Tests" -Tag 'Unit', 'AzDevOpsDscResourceBase' { - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [string]$ApiUri = 'https://some.api/_apis/' + [string]$Pat = '1234567890123456789012345678901234567890123456789012' + [DscProperty(Key)] + [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' - Describe "$script:subModuleName\Classes\AzDevOpsDscResourceBase\$script:commandName" -Tag $script:tag { + [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID - class AzDevOpsDscResourceBaseExample : AzDevOpsDscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + [string]GetResourceName() { - [string]$ApiUri = 'https://some.api/_apis/' - [string]$Pat = '1234567890123456789012345678901234567890123456789012' + return 'AzDevOpsDscResourceBaseExample' + } + + [Hashtable]GetDscCurrentStateObjectGetParameters() + { + return @{} + } + + [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) + { + return $null + } + } - [DscProperty(Key)] - [string]$AzDevOpsDscResourceBaseExampleName = 'AzDevOpsDscResourceBaseExampleNameValue' + $testCasesValidButNotNone = @( + @{ + RequiredAction = [RequiredAction]::Get + }, + @{ + RequiredAction = [RequiredAction]::New + }, + @{ + RequiredAction = [RequiredAction]::Set + }, + @{ + RequiredAction = [RequiredAction]::Remove + }, + @{ + RequiredAction = [RequiredAction]::Test + }, + @{ + RequiredAction = [RequiredAction]::Error + } + ) - [string]$AzDevOpsDscResourceBaseExampleId # = '31e71307-09b3-4d8a-b65c-5c714f64205f' # Random GUID + Context 'When no "GetDscRequiredAction()" returns "None"'{ - [string]GetResourceName() - { - return 'AzDevOpsDscResourceBaseExample' - } + It 'Should not throw' { - [Hashtable]GetDscCurrentStateObjectGetParameters() - { - return @{} - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$getDscRequiredAction = {return [RequiredAction]::None} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force - [PSObject]GetDscCurrentStateResourceObject([Hashtable]$GetParameters) - { - return $null - } + {$azDevOpsDscResourceBase.TestDesiredState()} | Should -Not -Throw } - $testCasesValidButNotNone = @( - @{ - RequiredAction = [RequiredAction]::Get - }, - @{ - RequiredAction = [RequiredAction]::New - }, - @{ - RequiredAction = [RequiredAction]::Set - }, - @{ - RequiredAction = [RequiredAction]::Remove - }, - @{ - RequiredAction = [RequiredAction]::Test - }, - @{ - RequiredAction = [RequiredAction]::Error - } - ) - - Context 'When no "GetDscRequiredAction()" returns "None"'{ - - It 'Should not throw' { - - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$getDscRequiredAction = {return [RequiredAction]::None} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force - - {$azDevOpsDscResourceBase.TestDesiredState()} | Should -Not -Throw - } - - It 'Should return $true' { - - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$getDscRequiredAction = {return [RequiredAction]::None} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force - - $azDevOpsDscResourceBase.TestDesiredState() | Should -BeTrue - } + It 'Should return $true' { + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$getDscRequiredAction = {return [RequiredAction]::None} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force + + $azDevOpsDscResourceBase.TestDesiredState() | Should -BeTrue } + } - Context 'When no "GetDscRequiredAction()" does not return "None"'{ - It 'Should not throw - ""' -TestCases $testCasesValidButNotNone { - param ([RequiredAction]$RequiredAction) + Context 'When no "GetDscRequiredAction()" does not return "None"'{ - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force + It 'Should not throw - ""' -TestCases $testCasesValidButNotNone { + param ([RequiredAction]$RequiredAction) - {$azDevOpsDscResourceBase.TestDesiredState()} | Should -Not -Throw - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force - It 'Should return $false - ""' -TestCases $testCasesValidButNotNone { - param ([RequiredAction]$RequiredAction) + {$azDevOpsDscResourceBase.TestDesiredState()} | Should -Not -Throw + } - $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() - [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} - $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force + It 'Should return $false - ""' -TestCases $testCasesValidButNotNone { + param ([RequiredAction]$RequiredAction) - $azDevOpsDscResourceBase.TestDesiredState() | Should -BeFalse - } + $azDevOpsDscResourceBase = [AzDevOpsDscResourceBaseExample]::new() + [ScriptBlock]$getDscRequiredAction = {return $RequiredAction} + $azDevOpsDscResourceBase | Add-Member -MemberType ScriptMethod -Name GetDscRequiredAction -Value $getDscRequiredAction -Force + $azDevOpsDscResourceBase.TestDesiredState() | Should -BeFalse } } + } + diff --git a/tests/Unit/Classes/Classes.BeforeAll.ps1 b/tests/Unit/Classes/Classes.BeforeAll.ps1 deleted file mode 100644 index ebb497892..000000000 --- a/tests/Unit/Classes/Classes.BeforeAll.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -param( - [Parameter(Mandatory = $true)] - [String] - $RepositoryPath -) - -$ClassesDirectory = "$RepositoryPath\source\Classes" -$EnumsDirectory = "$RepositoryPath\source\Enum" -$Global:ClassesLoaded = $true - -# -# Load all the Enums - -Get-ChildItem -LiteralPath $EnumsDirectory -File | ForEach-Object { - Write-Verbose "Dot Sourcing $($_.FullName)" - . $_.FullName -} - -# -# Load all the Classes - -Get-ChildItem -LiteralPath $ClassesDirectory -File | ForEach-Object { - - Write-Verbose "Dot Sourcing $($_.FullName)" - # Read the file and remove [DscResource()] attribute - $file = Get-Command $_.FullName - # Remove [DscResource()] attribute - $content = $file.ScriptContents -replace '\[DscResource\(\)\]', '' - # Convert the string array into ScriptBlock - $scriptBlock = [ScriptBlock]::Create($content) - # Dot source the script block - . $scriptBlock - -} - -# -# Load all the Helper Functions - -Get-ChildItem -LiteralPath "$RepositoryPath\source\Modules\AzureDevOpsDsc.Common\Api\Functions\Private\Helper" -File -Recurse -Filter *.ps1 | ForEach-Object { - Write-Verbose "Dot Sourcing $($_.FullName)" - . $_.FullName -} diff --git a/tests/Unit/Classes/DscResourceBase/BeforeAll.ps1 b/tests/Unit/Classes/DscResourceBase/BeforeAll.ps1 new file mode 100644 index 000000000..0dc71e37c --- /dev/null +++ b/tests/Unit/Classes/DscResourceBase/BeforeAll.ps1 @@ -0,0 +1,8 @@ +# Test if the class is defined +if ($null -eq $Global:ClassesLoaded) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly +} diff --git a/tests/Unit/Classes/DscResourceBase/DscResourceBase.Initialization.Tests.ps1 b/tests/Unit/Classes/DscResourceBase/DscResourceBase.Initialization.Tests.ps1 deleted file mode 100644 index b9e289e79..000000000 --- a/tests/Unit/Classes/DscResourceBase/DscResourceBase.Initialization.Tests.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -# Initialize tests for module function 'Classes' -. $PSScriptRoot\..\Classes.TestInitialization.ps1 - -# Note: Use of this functionality seems to pre-load the module and classes which subsquent tests can use -# which works around difficulty of referencing classes in 'source' directory when code coverage is -# using the dynamically/build-defined, 'output' directory. -InModuleScope 'AzureDevOpsDsc' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - -} diff --git a/tests/Unit/Classes/DscResourceBase/GetDscResourceKey.Tests.ps1 b/tests/Unit/Classes/DscResourceBase/GetDscResourceKey.Tests.ps1 index 7a6ddb6d3..73b9f2a11 100644 --- a/tests/Unit/Classes/DscResourceBase/GetDscResourceKey.Tests.ps1 +++ b/tests/Unit/Classes/DscResourceBase/GetDscResourceKey.Tests.ps1 @@ -1,83 +1,65 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 +Describe "[DscResourceBase]::GetDscResourceKey() Tests" -Tag 'Unit', 'DscResourceBase' { -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 -InModuleScope 'AzureDevOpsDsc' { + Context 'When called from instance of the class without a DSC Resource key' { - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + It 'Should throw' { - - Describe "$script:subModuleName\Classes\DscResourceBase\Method\$script:commandName" -Tag $script:tag { + $dscResourceBase = [DscResourceBase]::new() + {$dscResourceBase.GetDscResourceKey()} | Should -Throw + } - Context 'When called from instance of the class without a DSC Resource key' { + Context 'When "GetDscResourceKeyPropertyName" returns a $null value' { It 'Should throw' { $dscResourceBase = [DscResourceBase]::new() + $dscResourceBase | Add-Member -MemberType ScriptMethod 'GetDscResourceKeyPropertyName' -Value { return $null } -Force {$dscResourceBase.GetDscResourceKey()} | Should -Throw } + } - Context 'When "GetDscResourceKeyPropertyName" returns a $null value' { - - It 'Should throw' { - - $dscResourceBase = [DscResourceBase]::new() - $dscResourceBase | Add-Member -MemberType ScriptMethod 'GetDscResourceKeyPropertyName' -Value { return $null } -Force - - {$dscResourceBase.GetDscResourceKey()} | Should -Throw - } - } - - - Context 'When "GetDscResourceKeyPropertyName" returns a "" (empty string) value' { + Context 'When "GetDscResourceKeyPropertyName" returns a "" (empty string) value' { - It 'Should throw' { + It 'Should throw' { - $dscResourceBase = [DscResourceBase]::new() - $dscResourceBase | Add-Member -MemberType ScriptMethod 'GetDscResourceKeyPropertyName' -Value { return '' } -Force + $dscResourceBase = [DscResourceBase]::new() + $dscResourceBase | Add-Member -MemberType ScriptMethod 'GetDscResourceKeyPropertyName' -Value { return '' } -Force - {$dscResourceBase.GetDscResourceKey()} | Should -Throw - } + {$dscResourceBase.GetDscResourceKey()} | Should -Throw } + } - } - + } - Context 'When called from instance of a class with multiple DSC Resource keys' { - It 'Should throw' { + Context 'When called from instance of a class with multiple DSC Resource keys' { - class DscResourceBase2DscKeys : DscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) - { - [DscProperty(Key)] - [string]$DscKey1 + It 'Should throw' { - [DscProperty(Key)] - [string]$DscKey2 - } - $dscResourceWith2Keys = [DscResourceBase2DscKeys]@{} + class DscResourceBase2DscKeys : DscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [DscProperty(Key)] + [string]$DscKey1 - {$dscResourceWith2Keys.GetDscResourceKey()} | Should -Throw + [DscProperty(Key)] + [string]$DscKey2 } - + $dscResourceWith2Keys = [DscResourceBase2DscKeys]@{} + {$dscResourceWith2Keys.GetDscResourceKey()} | Should -Throw } + } + - Context 'When called from instance of class with a DSC key' { + Context 'When called from instance of class with a DSC key' { + BeforeAll { class DscResourceBase1DscKey : DscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) { [DscProperty(Key)] @@ -87,16 +69,17 @@ InModuleScope 'AzureDevOpsDsc' { $dscResourceWith1Key = [DscResourceBase1DscKey]@{ DscKey1='DscKey1Value' } + } - It 'Should not throw' { + It 'Should not throw' { - {$dscResourceWith1Key.GetDscResourceKey()} | Should -Not -Throw - } + $dscResourceWith1Key.GetDscResourceKey() + {$dscResourceWith1Key.GetDscResourceKey()} | Should -Not -Throw + } - It 'Should return the value of the DSC Resource key' { + It 'Should return the value of the DSC Resource key' { - $dscResourceWith1Key.GetDscResourceKey() | Should -Be 'DscKey1Value' - } + $dscResourceWith1Key.GetDscResourceKey() | Should -Be 'DscKey1Value' } } } diff --git a/tests/Unit/Classes/DscResourceBase/GetDscResourceKeyPropertyName.Tests.ps1 b/tests/Unit/Classes/DscResourceBase/GetDscResourceKeyPropertyName.Tests.ps1 index e7a6085bc..1b0d58c76 100644 --- a/tests/Unit/Classes/DscResourceBase/GetDscResourceKeyPropertyName.Tests.ps1 +++ b/tests/Unit/Classes/DscResourceBase/GetDscResourceKeyPropertyName.Tests.ps1 @@ -1,59 +1,43 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 +Describe "[DscResourceBase]::GetDscResourceKeyPropertyName() Tests" -Tag 'Unit', 'DscResourceBase' { -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 -InModuleScope 'AzureDevOpsDsc' { + Context 'When called from instance of the class without a DSC Resource key' { - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + It 'Should throw' { + $dscResourceWithNoDscKey = [DscResourceBase]::new() - Describe "$script:subModuleName\Classes\DscResourceBase\Method\$script:commandName" -Tag $script:tag { - - - Context 'When called from instance of the class without a DSC Resource key' { - - It 'Should throw' { - - $dscResourceWithNoDscKey = [DscResourceBase]::new() - - {$dscResourceWithNoDscKey.GetDscResourceKeyPropertyName()} | Should -Throw - } + {$dscResourceWithNoDscKey.GetDscResourceKeyPropertyName()} | Should -Throw } + } - Context 'When called from instance of a class with multiple DSC Resource keys' { - - It 'Should throw' { + Context 'When called from instance of a class with multiple DSC Resource keys' { - class DscResourceBase2Keys : DscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) - { - [DscProperty(Key)] - [string]$DscKey1 + It 'Should throw' { - [DscProperty(Key)] - [string]$DscKey2 - } + class DscResourceBase2Keys : DscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [DscProperty(Key)] + [string]$DscKey1 - $dscResourceWith2Keys = [DscResourceBase2Keys]@{ - DscKey1 = 'DscKey1Value' - DscKey2 = 'DscKey2Value' - } + [DscProperty(Key)] + [string]$DscKey2 + } - {$dscResourceWith2Keys.GetDscResourceKeyPropertyName()} | Should -Throw + $dscResourceWith2Keys = [DscResourceBase2Keys]@{ + DscKey1 = 'DscKey1Value' + DscKey2 = 'DscKey2Value' } + + {$dscResourceWith2Keys.GetDscResourceKeyPropertyName()} | Should -Throw } + } - Context 'When called from instance of class with a DSC key' { + Context 'When called from instance of class with a DSC key' { + BeforeAll { class DscResourceBase1Key : DscResourceBase { [DscProperty(Key)] @@ -63,16 +47,16 @@ InModuleScope 'AzureDevOpsDsc' { $dscResourceWith1Key = [DscResourceBase1Key]@{ DscKey1 = 'DscKey1Value' } + } - It 'Should not throw' { - - {$dscResourceWith1Key.GetDscResourceKeyPropertyName()} | Should -Not -Throw - } + It 'Should not throw' { + $dscResourceWith1Key.GetDscResourceKeyPropertyName() + {$dscResourceWith1Key.GetDscResourceKeyPropertyName()} | Should -Not -Throw + } - It 'Should return the value of the DSC Resource key' { + It 'Should return the value of the DSC Resource key' { - $dscResourceWith1Key.GetDscResourceKeyPropertyName() | Should -Be 'DscKey1' - } + $dscResourceWith1Key.GetDscResourceKeyPropertyName() | Should -Be 'DscKey1' } } } diff --git a/tests/Unit/Classes/DscResourceBase/GetDscResourcePropertyNames.Tests.ps1 b/tests/Unit/Classes/DscResourceBase/GetDscResourcePropertyNames.Tests.ps1 index 47472fba2..e48954b19 100644 --- a/tests/Unit/Classes/DscResourceBase/GetDscResourcePropertyNames.Tests.ps1 +++ b/tests/Unit/Classes/DscResourceBase/GetDscResourcePropertyNames.Tests.ps1 @@ -1,43 +1,27 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 +Describe "[DscResourceBase]::GetDscResourcePropertyNames() Tests" -Tag 'Unit', 'DscResourceBase' { -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 -InModuleScope 'AzureDevOpsDsc' { + Context 'When called from instance of the class without any DSC properties' { - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + It 'Should not throw' { + $dscResourceWithNoDscProperties = [DscResourceBase]::new() - Describe "$script:subModuleName\Classes\DscResourceBase\Method\$script:commandName" -Tag $script:tag { - - - Context 'When called from instance of the class without any DSC properties' { - - It 'Should not throw' { - - $dscResourceWithNoDscProperties = [DscResourceBase]::new() - - {$dscResourceWithNoDscProperties.GetDscResourcePropertyNames()} | Should -Not -Throw - } + {$dscResourceWithNoDscProperties.GetDscResourcePropertyNames()} | Should -Not -Throw + } - It 'Should return empty array' { + It 'Should return empty array' { - $dscResourceWithNoDscProperties = [DscResourceBase]::new() + $dscResourceWithNoDscProperties = [DscResourceBase]::new() - $dscResourceWithNoDscProperties.GetDscResourcePropertyNames().Count | Should -Be 0 - } + $dscResourceWithNoDscProperties.GetDscResourcePropertyNames().Count | Should -Be 0 } + } - Context 'When called from instance of a class with multiple DSC properties' { + Context 'When called from instance of a class with multiple DSC properties' { + BeforeAll { class DscResourceBase2Properties : DscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) { [DscProperty()] @@ -47,28 +31,30 @@ InModuleScope 'AzureDevOpsDsc' { [string]$AnotherDscProperty } + $dscResourceWith2DscProperties = [DscResourceBase2Properties]@{ ADscProperty = 'ADscPropertyValue' AnotherDscProperty = 'AnotherDscPropertyValue' } - It 'Should not throw' { + } + + It 'Should not throw' { - { $dscResourceWith2DscProperties.GetDscResourcePropertyNames() } | Should -Not -Throw - } + { $dscResourceWith2DscProperties.GetDscResourcePropertyNames() } | Should -Not -Throw + } - It 'Should return 2 property names' { + It 'Should return 2 property names' { - $dscResourceWith2DscProperties.GetDscResourcePropertyNames().Count | Should -Be 2 - } + $dscResourceWith2DscProperties.GetDscResourcePropertyNames().Count | Should -Be 2 + } - It 'Should return the correct property names' { + It 'Should return the correct property names' { - $propertyNames = $dscResourceWith2DscProperties.GetDscResourcePropertyNames() + $propertyNames = $dscResourceWith2DscProperties.GetDscResourcePropertyNames() - $propertyNames | Should -Contain 'ADscProperty' - $propertyNames | Should -Contain 'AnotherDscProperty' - } + $propertyNames | Should -Contain 'ADscProperty' + $propertyNames | Should -Contain 'AnotherDscProperty' } } } diff --git a/tests/Unit/Classes/DscResourceBase/GetDscResourcePropertyNamesWithNoSetSupport.Tests.ps1 b/tests/Unit/Classes/DscResourceBase/GetDscResourcePropertyNamesWithNoSetSupport.Tests.ps1 index 70df3c168..0ac48f4df 100644 --- a/tests/Unit/Classes/DscResourceBase/GetDscResourcePropertyNamesWithNoSetSupport.Tests.ps1 +++ b/tests/Unit/Classes/DscResourceBase/GetDscResourcePropertyNamesWithNoSetSupport.Tests.ps1 @@ -1,74 +1,56 @@ -using module ..\..\..\..\output\AzureDevOpsDsc\0.2.0\AzureDevOpsDsc.psm1 +Describe "[DscResourceBase]::GetDscResourcePropertyNamesWithNoSetSupport() Tests" -Tag 'Unit', 'DscResourceBase' { -# Initialize tests for module function -. $PSScriptRoot\..\Classes.TestInitialization.ps1 -InModuleScope 'AzureDevOpsDsc' { + Context 'When called from instance of a class without any DSC properties with no "Set" support' { - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) + It 'Should not throw' { + $dscResourceWithNoSetSupportProperties = [DscResourceBase]::new() - Describe "$script:subModuleName\Classes\DscResourceBase\Method\$script:commandName" -Tag $script:tag { - - - Context 'When called from instance of a class without any DSC properties with no "Set" support' { - - It 'Should not throw' { - - $dscResourceWithNoSetSupportProperties = [DscResourceBase]::new() - - {$dscResourceWithNoSetSupportProperties.GetDscResourcePropertyNamesWithNoSetSupport()} | Should -Not -Throw - } + {$dscResourceWithNoSetSupportProperties.GetDscResourcePropertyNamesWithNoSetSupport()} | Should -Not -Throw + } - It 'Should return empty array' { + It 'Should return empty array' { - $dscResourceWithNoSetSupportProperties = [DscResourceBase]::new() + $dscResourceWithNoSetSupportProperties = [DscResourceBase]::new() - $dscResourceWithNoSetSupportProperties.GetDscResourcePropertyNamesWithNoSetSupport().Count | Should -Be 0 - } + $dscResourceWithNoSetSupportProperties.GetDscResourcePropertyNamesWithNoSetSupport().Count | Should -Be 0 } + } - Context 'When called from instance of a class with a DSC property with no "Set" support' { + Context 'When called from instance of a class with a DSC property with no "Set" support' { - class DscResourceBaseWithNoSet : DscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + class DscResourceBaseWithNoSet : DscResourceBase # Note: Ignore 'TypeNotFound' warning (it is available at runtime) + { + [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() { - [System.String[]]GetDscResourcePropertyNamesWithNoSetSupport() - { - return @('NoSetPropertyName1', 'NoSetPropertyName2') - } + return @('NoSetPropertyName1', 'NoSetPropertyName2') } + } - It 'Should not throw' { + It 'Should not throw' { - $dscResourceWithANoSetSupportProperty = [DscResourceBaseWithNoSet]@{} + $dscResourceWithANoSetSupportProperty = [DscResourceBaseWithNoSet]@{} - { $dscResourceWithANoSetSupportProperty.GetDscResourcePropertyNamesWithNoSetSupport() } | Should -Not -Throw - } + { $dscResourceWithANoSetSupportProperty.GetDscResourcePropertyNamesWithNoSetSupport() } | Should -Not -Throw + } - It 'Should return the correct number of DSC resource property names that do not support "SET"' { + It 'Should return the correct number of DSC resource property names that do not support "SET"' { - $dscResourceWithANoSetSupportProperty = [DscResourceBaseWithNoSet]@{} + $dscResourceWithANoSetSupportProperty = [DscResourceBaseWithNoSet]@{} - $dscResourceWithANoSetSupportProperty.GetDscResourcePropertyNamesWithNoSetSupport().Count | Should -Be 2 - } + $dscResourceWithANoSetSupportProperty.GetDscResourcePropertyNamesWithNoSetSupport().Count | Should -Be 2 + } - It 'Should return the correct DSC resource property names that do not support "SET"' { + It 'Should return the correct DSC resource property names that do not support "SET"' { - $dscResourceWithANoSetSupportProperty = [DscResourceBaseWithNoSet]@{} + $dscResourceWithANoSetSupportProperty = [DscResourceBaseWithNoSet]@{} - $propertyNames = $dscResourceWithANoSetSupportProperty.GetDscResourcePropertyNamesWithNoSetSupport() + $propertyNames = $dscResourceWithANoSetSupportProperty.GetDscResourcePropertyNamesWithNoSetSupport() - $propertyNames | Should -Contain 'NoSetPropertyName1' - $propertyNames | Should -Contain 'NoSetPropertyName2' - } + $propertyNames | Should -Contain 'NoSetPropertyName1' + $propertyNames | Should -Contain 'NoSetPropertyName2' } } } diff --git a/tests/Unit/Classes/Resources/000.BeforeAll.ps1 b/tests/Unit/Classes/Resources/000.BeforeAll.ps1 new file mode 100644 index 000000000..3186cd374 --- /dev/null +++ b/tests/Unit/Classes/Resources/000.BeforeAll.ps1 @@ -0,0 +1,11 @@ +# Import the module containing the AzDoGroupPermission class +# Describe block for AzDoGroupPermission tests + +# Test if the class is defined +if ($null -eq $Global:ClassesLoaded) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly +} diff --git a/tests/Unit/Classes/Resources/009.AzDoGroupPermission.tests.ps1 b/tests/Unit/Classes/Resources/009.AzDoGroupPermission.tests.ps1 new file mode 100644 index 000000000..789410c36 --- /dev/null +++ b/tests/Unit/Classes/Resources/009.AzDoGroupPermission.tests.ps1 @@ -0,0 +1,232 @@ + +Describe 'AzDoGroupPermission Tests' { + + BeforeAll { + $ENV:AZDODSC_CACHE_DIRECTORY = 'mocked_cache_directory' + + Mock -CommandName Import-Module + Mock -CommandName Test-Path -MockWith { $true } + Mock -CommandName Import-Clixml -MockWith { + return @{ + OrganizationName = 'mock-org' + Token = @{ + tokenType = 'ManagedIdentity' + access_token = 'mock_access_token' + } + + } + } + Mock -CommandName New-AzDoAuthenticationProvider + Mock -CommandName Get-AzDoCacheObjects -MockWith { + return @('mock-cache-type') + } + Mock -CommandName Initialize-CacheObject + + } + AfterAll { + + $ENV:AZDODSC_CACHE_DIRECTORY = $null + + } + + # Test case to check if the class can be instantiated + Context 'Instantiation' { + It 'Should create an instance of the AzDoGroupPermission class' { + $groupPermission = [AzDoGroupPermission]::new() + $groupPermission | Should -Not -BeNullOrEmpty + $groupPermission | Should -BeOfType 'AzDoGroupPermission' + } + } + + # Test case to check default values + Context 'Default Values' { + It 'Should have default value for isInherited as $true' { + $groupPermission = [AzDoGroupPermission]::new() + $groupPermission.isInherited | Should -Be $true + } + } + + # Test case to check property assignments + Context 'Property Assignments' { + It 'Should allow setting and getting GroupName property' { + $groupPermission = [AzDoGroupPermission]::new() + $groupPermission.GroupName = 'TestGroup' + $groupPermission.GroupName | Should -Be 'TestGroup' + } + + It 'Should allow setting and getting Permissions property' { + $groupPermission = [AzDoGroupPermission]::new() + $permissions = @( + @{ Permission = 'Read'; Allow = $true }, + @{ Permission = 'Write'; Allow = $false } + ) + $groupPermission.Permissions = $permissions + $groupPermission.Permissions | Should -Be $permissions + } + } + + # Test case for Get method + Context 'Get Method' { + + BeforeAll { + Mock -CommandName Get-AzDoGroupPermission { + + $properties = @{ + Ensure = [Ensure]::Absent + propertiesChanged = @() + GroupName = 'TestGroup' + Permissions = @{ + 'mock-permission' = @{ + Permission = 'Read' + Allow = $true + } + } + isInherited = $false + status = $null + reason = $null + } + + return $properties + + } + + } + + It 'Should return current state properties' { + + $groupPermission = [AzDoGroupPermission]::new() + $groupPermission.GroupName = 'TestGroup' + $groupPermission.isInherited = $false + $groupPermission.Permissions = @( + @{ Permission = 'Read'; Allow = $true } + ) + + $currentState = $groupPermission.Get() + + $currentState.GroupName | Should -Be 'TestGroup' + $currentState.isInherited | Should -Be $false + $currentState.Permissions | Should -Not -BeNullOrEmpty + + Assert-MockCalled Get-AzDoGroupPermission -Exactly 1 + + } + } + + Context 'Test Method' { + + BeforeAll { + + } + + It 'Should return $true when calling the test method - when the current state is unchanged' { + + Mock -CommandName Get-AzDoGroupPermission { + + $properties = @{ + Ensure = [Ensure]::Present + propertiesChanged = @() + GroupName = 'TestGroup' + Permissions = @{ + 'mock-permission' = @{ + Permission = 'Read' + Allow = $true + } + } + isInherited = $false + status = [DSCGetSummaryState]::Unchanged + reason = $null + } + + return $properties + + } + + $groupPermission = [AzDoGroupPermission]::new() + $groupPermission.GroupName = 'TestGroup' + $groupPermission.Permissions = @( + @{ Permission = 'Read'; Allow = $true } + ) + + $groupPermission.Test() | Should -Be $true + + + } + + It 'Should return $false when calling the test method - when the current state is changed' { + + Mock -CommandName Get-AzDoGroupPermission { + + $properties = @{ + Ensure = [Ensure]::Absent + propertiesChanged = @() + GroupName = 'TestGroup' + Permissions = @{ + 'mock-permission' = @{ + Permission = 'Read' + Allow = $true + } + } + isInherited = $false + status = [DSCGetSummaryState]::Missing + reason = $null + } + + return $properties + + } + + $groupPermission = [AzDoGroupPermission]::new() + $groupPermission.GroupName = 'DifferentGroup' + $groupPermission.Permissions = @( + @{ Permission = 'Read'; Allow = $true } + ) + + $groupPermission.Test() | Should -Be $false + + } + + It 'Should return $false when calling the test method - when the status is null' { + + Mock -CommandName Get-AzDoGroupPermission { + + $properties = @{ + Ensure = [Ensure]::Absent + propertiesChanged = @() + GroupName = 'TestGroup' + Permissions = @{ + 'mock-permission' = @{ + Permission = 'Read' + Allow = $true + } + } + isInherited = $false + status = $null + reason = $null + } + + return $properties + + } + + Mock -CommandName New-InvalidOperationException { + throw 'Invalid Operation Exception' + } -ParameterFilter { + $Message -like "*Could not obtain a valid 'LookupResult.Status' value within*" + } + + $groupPermission = [AzDoGroupPermission]::new() + $groupPermission.GroupName = 'TestGroup' + $groupPermission.Permissions = @( + @{ Permission = 'Read'; Allow = $true } + ) + + { $groupPermission.Test() } | Should -Not -Throw + $groupPermission.Test() | Should -Be $false + Assert-MockCalled New-InvalidOperationException -Times 1 + + } + + + } + +} diff --git a/tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 b/tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 deleted file mode 100644 index cb2f21133..000000000 --- a/tests/Unit/Classes/Resources/009.xAzDoGroupPermission.tests.ps1 +++ /dev/null @@ -1,59 +0,0 @@ -# Import the module containing the AzDoGroupPermission class -# Describe block for AzDoGroupPermission tests -Describe 'AzDoGroupPermission Tests' { - - # Test case to check if the class can be instantiated - Context 'Instantiation' { - It 'Should create an instance of the AzDoGroupPermission class' { - $groupPermission = [AzDoGroupPermission]::new() - $groupPermission | Should -Not -BeNullOrEmpty - $groupPermission | Should -BeOfType 'AzDoGroupPermission' - } - } - - # Test case to check default values - Context 'Default Values' { - It 'Should have default value for isInherited as $true' { - $groupPermission = [AzDoGroupPermission]::new() - $groupPermission.isInherited | Should -Be $true - } - } - - # Test case to check property assignments - Context 'Property Assignments' { - It 'Should allow setting and getting GroupName property' { - $groupPermission = [AzDoGroupPermission]::new() - $groupPermission.GroupName = 'TestGroup' - $groupPermission.GroupName | Should -Be 'TestGroup' - } - - It 'Should allow setting and getting Permissions property' { - $groupPermission = [AzDoGroupPermission]::new() - $permissions = @( - @{ Permission = 'Read'; Allow = $true }, - @{ Permission = 'Write'; Allow = $false } - ) - $groupPermission.Permissions = $permissions - $groupPermission.Permissions | Should -Be $permissions - } - } - - # Test case for Get method - Context 'Get Method' { - It 'Should return current state properties' { - $groupPermission = [AzDoGroupPermission]::new() - $groupPermission.GroupName = 'TestGroup' - $groupPermission.isInherited = $false - $groupPermission.Permissions = @( - @{ Permission = 'Read'; Allow = $true } - ) - - $currentState = $groupPermission.Get() - $currentState.GroupName | Should -Be 'TestGroup' - $currentState.isInherited | Should -Be $false - $currentState.Permissions | Should -Be @( - @{ Permission = 'Read'; Allow = $true } - ) - } - } -} diff --git a/tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Classes/Resources/011.AzDoOrganizationGroup.tests.ps1 similarity index 70% rename from tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 rename to tests/Unit/Classes/Resources/011.AzDoOrganizationGroup.tests.ps1 index c4aeba15a..a06d935bf 100644 --- a/tests/Unit/Classes/Resources/011.xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Classes/Resources/011.AzDoOrganizationGroup.tests.ps1 @@ -2,19 +2,42 @@ # Requires -Module DscResource.Common # Test if the class is defined -if ($Global:ClassesLoaded -eq $null) +if ($null -eq $Global:ClassesLoaded) { # Attempt to find the root of the repository $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - # Load the classes - $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } - . $preInitialize.FullName -RepositoryPath $RepositoryRoot + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly } Describe 'AzDoOrganizationGroup' { - # Mocking AzDevOpsDscResourceBase class since it's not provided - Class AzDevOpsDscResourceBase { - [void] Construct() {} + + BeforeAll { + $ENV:AZDODSC_CACHE_DIRECTORY = 'mocked_cache_directory' + + Mock -CommandName Import-Module + Mock -CommandName Test-Path -MockWith { $true } + Mock -CommandName Import-Clixml -MockWith { + return @{ + OrganizationName = 'mock-org' + Token = @{ + tokenType = 'ManagedIdentity' + access_token = 'mock_access_token' + } + + } + } + Mock -CommandName New-AzDoAuthenticationProvider + Mock -CommandName Get-AzDoCacheObjects -MockWith { + return @('mock-cache-type') + } + Mock -CommandName Initialize-CacheObject + + } + AfterAll { + + $ENV:AZDODSC_CACHE_DIRECTORY = $null + } Context 'Constructor' { diff --git a/tests/Unit/Classes/Resources/020.AzDoProject.tests.ps1 b/tests/Unit/Classes/Resources/020.AzDoProject.tests.ps1 new file mode 100644 index 000000000..64c82c130 --- /dev/null +++ b/tests/Unit/Classes/Resources/020.AzDoProject.tests.ps1 @@ -0,0 +1,120 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($null -eq $Global:ClassesLoaded) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly +} + +Describe "AzDoProject Class" { + + BeforeAll { + + $ENV:AZDODSC_CACHE_DIRECTORY = 'mocked_cache_directory' + + $TestProjectNameFunctionpath = Get-FunctionItem 'Test-AzDevOpsProjectName.ps1' + . $TestProjectNameFunctionpath + + Mock -CommandName Import-Module + Mock -CommandName Test-Path -MockWith { $true } + Mock -CommandName Import-Clixml -MockWith { + return @{ + OrganizationName = 'mock-org' + Token = @{ + tokenType = 'ManagedIdentity' + access_token = 'mock_access_token' + } + + } + } + Mock -CommandName New-AzDoAuthenticationProvider + Mock -CommandName Get-AzDoCacheObjects -MockWith { + return @('mock-cache-type') + } + Mock -CommandName Initialize-CacheObject + Mock -CommandName Test-AzDevOpsProjectName -MockWith { return $true } + + } + + Context "Initialization" { + It "Should initialize with default values" { + $project = [AzDoProject]::new() + + # Validate default values + $project.SourceControlType | Should -Be 'Git' + $project.ProcessTemplate | Should -Be 'Agile' + $project.Visibility | Should -Be 'Private' + } + } + + Context "Property Assignment" { + It "Should allow setting ProjectName and ProjectDescription" { + $project = [AzDoProject]::new() + $project.ProjectName = 'TestProject' + $project.ProjectDescription = 'This is a test project' + + # Validate assigned values + $project.ProjectName | Should -Be 'TestProject' + $project.ProjectDescription | Should -Be 'This is a test project' + } + + It "Should validate SourceControlType" { + $project = [AzDoProject]::new() + + # Valid value + { $project.SourceControlType = 'Tfvc' } | Should -Not -Throw + + # Invalid value + { $project.SourceControlType = 'InvalidValue' } | Should -Throw + } + + It "Should validate ProcessTemplate" { + $project = [AzDoProject]::new() + + # Valid value + { $project.ProcessTemplate = 'Scrum' } | Should -Not -Throw + + # Invalid value + { $project.ProcessTemplate = 'InvalidValue' } | Should -Throw + } + + It "Should validate Visibility" { + $project = [AzDoProject]::new() + + # Valid value + { $project.Visibility = 'Public' } | Should -Not -Throw + + # Invalid value + { $project.Visibility = 'InvalidValue' } | Should -Throw + } + } + + Context "Get Method" { + It "Should return an instance of AzDoProject" { + + Mock -CommandName Test-AzDevOpsProjectName -MockWith { return $true } + Mock -CommandName Get-AzDoProject -MockWith { + return @{ + Ensure = [Ensure]::Absent + ProjectName = 'MyProject' + ProjectDescription = 'This is a sample project' + SourceControlType = 'Git' + ProcessTemplate = 'Agile' + Visibility = 'Private' + propertiesChanged = @() + status = $null + } + } + + $project = [AzDoProject]::new() + $project.ProjectName = 'MyProject' + $result = $project.Get() + + $result | Should -BeOfType 'AzDoProject' + } + } +} diff --git a/tests/Unit/Classes/Resources/020.xAzDevOpsProject.tests.ps1 b/tests/Unit/Classes/Resources/020.xAzDevOpsProject.tests.ps1 deleted file mode 100644 index a298f7224..000000000 --- a/tests/Unit/Classes/Resources/020.xAzDevOpsProject.tests.ps1 +++ /dev/null @@ -1,71 +0,0 @@ -# Requires -Module Pester -Version 5.0.0 -# Requires -Module DscResource.Common - -# Test if the class is defined -if ($Global:ClassesLoaded -eq $null) -{ - # Attempt to find the root of the repository - $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - # Load the classes - $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } - . $preInitialize.FullName -RepositoryPath $RepositoryRoot -} - -Describe 'xAzDevOpsProject' { - # Mocking AzDevOpsDscResourceBase class since it's not provided - Class AzDevOpsDscResourceBase { - [String]$Pat - [String]$ApiUri - [String]$Ensure - [PSObject] GetDscCurrentStateProperties() { return $null } - } - - Context 'Constructor' { - It 'should initialize properties correctly when given valid parameters' { - $project = [xAzDevOpsProject]::new() - $project.ProjectId = "12345" - $project.ProjectName = "TestProject" - $project.ProjectDescription = "This is a test project" - $project.SourceControlType = "Git" - - $project.ProjectId | Should -Be "12345" - $project.ProjectName | Should -Be "TestProject" - $project.ProjectDescription | Should -Be "This is a test project" - $project.SourceControlType | Should -Be "Git" - } - } - - Context 'GetDscResourcePropertyNamesWithNoSetSupport Method' { - It 'should return SourceControlType as property with no set support' { - $project = [xAzDevOpsProject]::new() - $result = $project.GetDscResourcePropertyNamesWithNoSetSupport() - - $result | Should -Contain "SourceControlType" - } - } - - Context 'GetDscCurrentStateProperties Method' { - - It 'should return correct properties when CurrentResourceObject is not null' { - $project = [xAzDevOpsProject]::new() - $currentResourceObject = [PSCustomObject]@{ - id = "12345" - name = "TestProject" - description = "This is a test project" - capabilities = @{ - versioncontrol = @{ - sourceControlType = "Git" - } - } - } - - $result = $project.GetDscCurrentStateProperties($currentResourceObject) - - $result.ProjectId | Should -Be "12345" - $result.ProjectName | Should -Be "TestProject" - $result.ProjectDescription | Should -Be "This is a test project" - $result.SourceControlType | Should -Be "Git" - $result.Ensure | Should -Be "Present" - } - } -} diff --git a/tests/Unit/Classes/Resources/022.AzDoProjectGroup.tests.ps1 b/tests/Unit/Classes/Resources/022.AzDoProjectGroup.tests.ps1 new file mode 100644 index 000000000..a5641a37d --- /dev/null +++ b/tests/Unit/Classes/Resources/022.AzDoProjectGroup.tests.ps1 @@ -0,0 +1,73 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($null -eq $Global:ClassesLoaded) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly +} + +Describe 'AzDoProjectGroup' { + + BeforeAll { + $ENV:AZDODSC_CACHE_DIRECTORY = 'mocked_cache_directory' + + Mock -CommandName Import-Module + Mock -CommandName Test-Path -MockWith { $true } + Mock -CommandName Import-Clixml -MockWith { + return @{ + OrganizationName = 'mock-org' + Token = @{ + tokenType = 'ManagedIdentity' + access_token = 'mock_access_token' + } + + } + } + Mock -CommandName New-AzDoAuthenticationProvider + Mock -CommandName Get-AzDoCacheObjects -MockWith { + return @('mock-cache-type') + } + Mock -CommandName Initialize-CacheObject + + } + AfterAll { + + $ENV:AZDODSC_CACHE_DIRECTORY = $null + + } + + Context 'When getting the current state of a project group' { + + BeforeAll { + Mock -CommandName Get-AzDoProjectGroup -MockWith { + return @{ + Ensure = [Ensure]::Absent + propertiesChanged = @() + ProjectName = "MyProject" + GroupName = "MyGroup" + GroupDescription = "This is my project group." + } + } + } + + It 'Should return the current state properties' { + # Arrange + $projectGroup = [AzDoProjectGroup]::new() + $projectGroup.ProjectName = "MyProject" + $projectGroup.GroupName = "MyGroup" + $projectGroup.GroupDescription = "This is my project group." + + # Act + $currentState = $projectGroup.Get() + + # Assert + $currentState.GroupName | Should -Be "MyGroup" + $currentState.ProjectName | Should -Be "MyProject" + $currentState.GroupDescription | Should -Be "This is my project group." + } + } +} diff --git a/tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 b/tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 deleted file mode 100644 index 721add929..000000000 --- a/tests/Unit/Classes/Resources/022.xAzDoProjectGroup.tests.ps1 +++ /dev/null @@ -1,89 +0,0 @@ -# Requires -Module Pester -Version 5.0.0 -# Requires -Module DscResource.Common - -# Test if the class is defined -if ($Global:ClassesLoaded -eq $null) -{ - # Attempt to find the root of the repository - $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - # Load the classes - $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } - . $preInitialize.FullName -RepositoryPath $RepositoryRoot -} - -Describe 'AzDoProjectGroup' { - BeforeAll { - # Mock functions that interact with external resources - function Get-AzDoProjectGroup - { - param ( - [string]$GroupName, - [string]$GroupDescription, - [string]$ProjectName - ) - # Return a mock object representing the current state - return @{ - GroupName = $GroupName - GroupDescription = $GroupDescription - ProjectName = $ProjectName - Ensure = 'Present' - } - } - - function New-AzDoProjectGroup - { - param ( - [string]$ProjectName, - [string]$GroupName, - [string]$GroupDescription, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "New project group created: $GroupName in project $ProjectName" - } - - function Update-AzDoProjectGroup - { - param ( - [string]$ProjectName, - [string]$GroupName, - [string]$GroupDescription, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "Project group updated: $GroupName in project $ProjectName" - } - - function Remove-AzDoProjectGroup - { - param ( - [string]$ProjectName, - [string]$GroupName, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "Project group removed: $GroupName in project $ProjectName" - } - } - - Context 'When getting the current state of a project group' { - It 'Should return the current state properties' { - # Arrange - $projectGroup = [AzDoProjectGroup]::new() - $projectGroup.ProjectName = "MyProject" - $projectGroup.GroupName = "MyGroup" - $projectGroup.GroupDescription = "This is my project group." - - # Act - $currentState = $projectGroup.Get() - - # Assert - $currentState.GroupName | Should -Be "MyGroup" - $currentState.ProjectName | Should -Be "MyProject" - $currentState.GroupDescription | Should -Be "This is my project group." - } - } -} diff --git a/tests/Unit/Classes/Resources/031.AzDoGroupMember.tests.ps1 b/tests/Unit/Classes/Resources/031.AzDoGroupMember.tests.ps1 new file mode 100644 index 000000000..a4caf1796 --- /dev/null +++ b/tests/Unit/Classes/Resources/031.AzDoGroupMember.tests.ps1 @@ -0,0 +1,70 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($null -eq $Global:ClassesLoaded) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly +} + +Describe 'AzDoGroupMember' { + + BeforeAll { + $ENV:AZDODSC_CACHE_DIRECTORY = 'mocked_cache_directory' + + Mock -CommandName Import-Module + Mock -CommandName Test-Path -MockWith { $true } + Mock -CommandName Import-Clixml -MockWith { + return @{ + OrganizationName = 'mock-org' + Token = @{ + tokenType = 'ManagedIdentity' + access_token = 'mock_access_token' + } + + } + } + Mock -CommandName New-AzDoAuthenticationProvider + Mock -CommandName Get-AzDoCacheObjects -MockWith { + return @('mock-cache-type') + } + Mock -CommandName Initialize-CacheObject + + } + AfterAll { + + $ENV:AZDODSC_CACHE_DIRECTORY = $null + + } + + Context 'When getting the current state of group members' { + + BeforeAll { + Mock -CommandName Get-AzDoGroupMember -MockWith { + return @{ + Ensure = [Ensure]::Absent + propertiesChanged = @() + GroupName = "MyGroup" + GroupMembers = @("User1", "User2") + } + } + } + + It 'Should return the current state properties' { + # Arrange + $groupMember = [AzDoGroupMember]::new() + $groupMember.GroupName = "MyGroup" + $groupMember.GroupMembers = @("User1", "User2") + + # Act + $currentState = $groupMember.Get() + + # Assert + $currentState.GroupName | Should -Be "MyGroup" + $currentState.GroupMembers | Should -Be @("User1", "User2") + } + } +} diff --git a/tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 b/tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 deleted file mode 100644 index 2d4371695..000000000 --- a/tests/Unit/Classes/Resources/031.xAzDoGroupMember.tests.ps1 +++ /dev/null @@ -1,82 +0,0 @@ -# Requires -Module Pester -Version 5.0.0 -# Requires -Module DscResource.Common - -# Test if the class is defined -if ($Global:ClassesLoaded -eq $null) -{ - # Attempt to find the root of the repository - $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - # Load the classes - $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } - . $preInitialize.FullName -RepositoryPath $RepositoryRoot -} - -Describe 'AzDoGroupMember' { - BeforeAll { - # Mock functions that interact with external resources - function Get-AzDoGroupMember - { - param ( - [string]$GroupName, - [string[]]$GroupMembers - ) - # Return a mock object representing the current state - return @{ - GroupName = $GroupName - GroupMembers = $GroupMembers - Ensure = 'Present' - } - } - - function New-AzDoGroupMember - { - param ( - [string]$GroupName, - [string[]]$GroupMembers, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "New group member added to: $GroupName" - } - - function Update-AzDoGroupMember - { - param ( - [string]$GroupName, - [string[]]$GroupMembers, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "Group members updated in: $GroupName" - } - - function Remove-AzDoGroupMember - { - param ( - [string]$GroupName, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "Group members removed from: $GroupName" - } - } - - Context 'When getting the current state of group members' { - It 'Should return the current state properties' { - # Arrange - $groupMember = [AzDoGroupMember]::new() - $groupMember.GroupName = "MyGroup" - $groupMember.GroupMembers = @("User1", "User2") - - # Act - $currentState = $groupMember.Get() - - # Assert - $currentState.GroupName | Should -Be "MyGroup" - $currentState.GroupMembers | Should -Be @("User1", "User2") - } - } -} diff --git a/tests/Unit/Classes/Resources/040.AzDoGitRepository.tests.ps1 b/tests/Unit/Classes/Resources/040.AzDoGitRepository.tests.ps1 new file mode 100644 index 000000000..aad7dca8a --- /dev/null +++ b/tests/Unit/Classes/Resources/040.AzDoGitRepository.tests.ps1 @@ -0,0 +1,77 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($null -eq $Global:ClassesLoaded) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly +} + +Describe 'AzDoGitRepository' { + + BeforeAll { + $ENV:AZDODSC_CACHE_DIRECTORY = 'mocked_cache_directory' + + Mock -CommandName Import-Module + Mock -CommandName Test-Path -MockWith { $true } + Mock -CommandName Import-Clixml -MockWith { + return @{ + OrganizationName = 'mock-org' + Token = @{ + tokenType = 'ManagedIdentity' + access_token = 'mock_access_token' + } + + } + } + Mock -CommandName New-AzDoAuthenticationProvider + Mock -CommandName Get-AzDoCacheObjects -MockWith { + return @('mock-cache-type') + } + Mock -CommandName Initialize-CacheObject + + } + AfterAll { + + $ENV:AZDODSC_CACHE_DIRECTORY = $null + + } + + + Context 'When getting the current state of a Git repository' { + + BeforeAll { + Mock -CommandName Get-AzDoGitRepository -MockWith { + return @{ + Ensure = [Ensure]::Absent + propertiesChanged = @() + ProjectName = "MyProject" + RepositoryName = "MyRepository" + SourceRepository = 'https://github.com/MyUser/MyRepository.git' + } + } + } + + It 'Should return the current state properties' { + # Arrange + $gitRepository = [AzDoGitRepository]::new() + $gitRepository.ProjectName = "MyProject" + $gitRepository.RepositoryName = "MyRepository" + + # Act + $currentState = $gitRepository.Get() + + # Assert + $currentState.ProjectName | Should -Be "MyProject" + $currentState.RepositoryName | Should -Be "MyRepository" + $currentState.SourceRepository | Should -BeNullOrEmpty + $currentState.LookupResult | Should -Not -BeNullOrEmpty + $currentState.LookupResult.ProjectName | Should -Be "MyProject" + $currentState.LookupResult.RepositoryName | Should -Be "MyRepository" + $currentState.LookupResult.SourceRepository | Should -Be 'https://github.com/MyUser/MyRepository.git' + } + } +} diff --git a/tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 b/tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 deleted file mode 100644 index 780dd63a3..000000000 --- a/tests/Unit/Classes/Resources/040.xAzDoGitRepository.tests.ps1 +++ /dev/null @@ -1,87 +0,0 @@ -# Requires -Module Pester -Version 5.0.0 -# Requires -Module DscResource.Common - -# Test if the class is defined -if ($Global:ClassesLoaded -eq $null) -{ - # Attempt to find the root of the repository - $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - # Load the classes - $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } - . $preInitialize.FullName -RepositoryPath $RepositoryRoot -} - -Describe 'AzDoGitRepository' { - BeforeAll { - # Mock functions that interact with external resources - function Get-AzDoGitRepository - { - param ( - [string]$ProjectName, - [string]$GitRepositoryName - ) - # Return a mock object representing the current state - return @{ - ProjectName = $ProjectName - GitRepositoryName = $GitRepositoryName - SourceRepository = 'https://github.com/MyUser/MyRepository.git' - Ensure = 'Present' - } - } - - function New-AzDoGitRepository - { - param ( - [string]$ProjectName, - [string]$GitRepositoryName, - [string]$SourceRepository, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "New Git repository created: $ProjectName/$GitRepositoryName" - } - - function Update-AzDoGitRepository - { - param ( - [string]$ProjectName, - [string]$GitRepositoryName, - [string]$SourceRepository, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "Git repository updated: $ProjectName/$GitRepositoryName" - } - - function Remove-AzDoGitRepository - { - param ( - [string]$ProjectName, - [string]$GitRepositoryName, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "Git repository removed: $ProjectName/$GitRepositoryName" - } - } - - Context 'When getting the current state of a Git repository' { - It 'Should return the current state properties' { - # Arrange - $gitRepository = [AzDoGitRepository]::new() - $gitRepository.ProjectName = "MyProject" - $gitRepository.RepositoryName = "MyRepository" - - # Act - $currentState = $gitRepository.Get() - - # Assert - $currentState.ProjectName | Should -Be "MyProject" - $currentState.GitRepositoryName | Should -Be "MyRepository" - $currentState.SourceRepository | Should -Be 'https://github.com/MyUser/MyRepository.git' - } - } -} diff --git a/tests/Unit/Classes/Resources/041.AzDoGitPermission.tests.ps1 b/tests/Unit/Classes/Resources/041.AzDoGitPermission.tests.ps1 new file mode 100644 index 000000000..1945457b0 --- /dev/null +++ b/tests/Unit/Classes/Resources/041.AzDoGitPermission.tests.ps1 @@ -0,0 +1,76 @@ +# Requires -Module Pester -Version 5.0.0 +# Requires -Module DscResource.Common + +# Test if the class is defined +if ($null -eq $Global:ClassesLoaded) +{ + # Attempt to find the root of the repository + $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName + # Load the Dependencies + . "$RepositoryRoot\azuredevopsdsc.tests.ps1" -LoadModulesOnly +} + +Describe 'AzDoGitPermission' { + + BeforeAll { + + $ENV:AZDODSC_CACHE_DIRECTORY = 'mocked_cache_directory' + + Mock -CommandName Import-Module + Mock -CommandName Test-Path -MockWith { $true } + Mock -CommandName Import-Clixml -MockWith { + return @{ + OrganizationName = 'mock-org' + Token = @{ + tokenType = 'ManagedIdentity' + access_token = 'mock_access_token' + } + + } + } + Mock -CommandName New-AzDoAuthenticationProvider + Mock -CommandName Get-AzDoCacheObjects -MockWith { + return @('mock-cache-type') + } + Mock -CommandName Initialize-CacheObject + + } + AfterAll { + + $ENV:AZDODSC_CACHE_DIRECTORY = $null + + } + + + Context 'When getting the current state of Git permissions' { + + BeforeAll { + Mock -CommandName Get-AzDoGitPermission -MockWith { + return @{ + Ensure = [Ensure]::Absent + propertiesChanged = @() + ProjectName = "MyProject" + RepositoryName = "MyRepository" + isInherited = $true + Permissions = @('Read', 'Contribute') + } + } + } + + It 'Should return the current state properties' { + # Arrange + $gitPermission = [AzDoGitPermission]::new() + $gitPermission.ProjectName = "MyProject" + $gitPermission.RepositoryName = "MyRepository" + + # Act + $currentState = $gitPermission.Get() + + # Assert + $currentState.ProjectName | Should -Be "MyProject" + $currentState.RepositoryName | Should -Be "MyRepository" + $currentState.isInherited | Should -Be $true + $currentState.LookupResult.Permissions | Should -Be @('Read', 'Contribute') + } + } +} diff --git a/tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 b/tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 deleted file mode 100644 index 87e01071e..000000000 --- a/tests/Unit/Classes/Resources/041.xAzDoGitPermission.tests.ps1 +++ /dev/null @@ -1,91 +0,0 @@ -# Requires -Module Pester -Version 5.0.0 -# Requires -Module DscResource.Common - -# Test if the class is defined -if ($Global:ClassesLoaded -eq $null) -{ - # Attempt to find the root of the repository - $RepositoryRoot = (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName - # Load the classes - $preInitialize = Get-ChildItem -Path "$RepositoryRoot" -Recurse -Filter '*.ps1' | Where-Object { $_.Name -eq 'Classes.BeforeAll.ps1' } - . $preInitialize.FullName -RepositoryPath $RepositoryRoot -} - -Describe 'AzDoGitPermission' { - BeforeAll { - # Mock functions that interact with external resources - function Get-AzDoGitPermission - { - param ( - [string]$ProjectName, - [string]$RepositoryName - ) - # Return a mock object representing the current state - return @{ - ProjectName = $ProjectName - RepositoryName = $RepositoryName - isInherited = $true - Permissions = @('Read', 'Contribute') - Ensure = 'Present' - } - } - - function New-AzDoGitPermission - { - param ( - [string]$ProjectName, - [string]$RepositoryName, - [boolean]$isInherited, - [hashtable[]]$Permissions, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "New Git permissions set for: $ProjectName/$RepositoryName" - } - - function Update-AzDoGitPermission - { - param ( - [string]$ProjectName, - [string]$RepositoryName, - [boolean]$isInherited, - [hashtable[]]$Permissions, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "Git permissions updated for: $ProjectName/$RepositoryName" - } - - function Remove-AzDoPermission - { - param ( - [string]$ProjectName, - [string]$RepositoryName, - [string]$Pat, - [string]$ApiUri - ) - # Mock implementation - Write-Output "Git permissions removed from: $ProjectName/$RepositoryName" - } - } - - Context 'When getting the current state of Git permissions' { - It 'Should return the current state properties' { - # Arrange - $gitPermission = [AzDoGitPermission]::new() - $gitPermission.ProjectName = "MyProject" - $gitPermission.RepositoryName = "MyRepository" - - # Act - $currentState = $gitPermission.Get() - - # Assert - $currentState.ProjectName | Should -Be "MyProject" - $currentState.RepositoryName | Should -Be "MyRepository" - $currentState.isInherited | Should -Be $true - $currentState.Permissions | Should -Be @('Read', 'Contribute') - } - } -} diff --git a/tests/Unit/DSCClassResources/AzDevOpsProject/AzDevOpsProject.Tests.ps1 b/tests/Unit/DSCClassResources/AzDevOpsProject/AzDevOpsProject.Tests.ps1 deleted file mode 100644 index 0decd03ac..000000000 --- a/tests/Unit/DSCClassResources/AzDevOpsProject/AzDevOpsProject.Tests.ps1 +++ /dev/null @@ -1,301 +0,0 @@ -# Initialize tests for module function -. $PSScriptRoot\..\DSCClassResources.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) - - - Describe "$script:subModuleName\Classes\DscResourceBase\Method\$script:commandName" -Tag $script:tag { - - $validDscMethodNames = @( - 'Get', - 'Set', - 'Test' - ) - $testCasesValidDscMethodNames = $validDscMethodNames | ForEach-Object { - @{ - MethodName = $_ - } - } - - $validPropertyNames = @( - 'ApiUri', - 'Pat', - 'ProjectId', - 'ProjectName', - 'ProjectDescription' - ) - $testCasesValidPropertyNames = $validPropertyNames | ForEach-Object { - @{ - PropertyName = $_ - PropertyValue = $_ + "Value" - } - } - - $testCasesValidPropertyNames += @{ - PropertyName = 'SourceControlType' - PropertyValue = 'Git' - } - - Context 'When creating a new instance of the class' { - - It 'Should not throw' { - - {[AzDevOpsProject]::new()} | Should -Not -Throw - } - } - - - Context 'When evaluating properties of the class' { - - It 'Should contain expected property - ""' -TestCases $testCasesValidPropertyNames { - param ([System.String]$PropertyName) - - $azDevOpsProject = [AzDevOpsProject]::new() - - $azDevOpsProject.PSobject.Properties.Name | Should -Contain $PropertyName - } - - It 'Should contain expected property value - ""' -TestCases $testCasesValidPropertyNames { - param ([System.String]$PropertyName, [System.String]$PropertyValue) - - $azDevOpsProject = [AzDevOpsProject]@{ - "$PropertyName" = $PropertyValue - } - - $azDevOpsProject."$PropertyName" | Should -Be $PropertyValue - } - } - - - Context 'When evaluating DSC methods of the class' { - - It 'Should contain expected method - ""' -TestCases $testCasesValidDscMethodNames { - param ([System.String]$MethodName) - - $azDevOpsProject = [AzDevOpsProject]::new() - - $azDevOpsProject.PSobject.Methods.Name | Should -Contain $MethodName - } - - - } - } -} - - - - - - - - -# <# -# .SYNOPSIS -# Automated unit test for AzDevOpsProject DSC Resource. -# #> - -# $script:dscModuleName = 'AzureDevOpsDsc' -# $script:dscResourceName = 'AzDevOpsProject' - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } - -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Class' ` -# -TestType 'Unit' -# } - -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment -# } - -# # Begin Testing - -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# Set-StrictMode -Version 1.0 - -# Describe 'AzDevOpsProject\Parameters' -Tag 'Parameter' { -# BeforeAll { -# #$mockInstanceName = 'DSCTEST' - -# #Mock -CommandName Import-SQLPSModule -# } -# } - -# Describe 'AzDevOpsProject\Get' -Tag 'Get' { - - - -# BeforeAll { - -# $getApiUri = "https://www.someUri.api/_apis/" -# $getPat = "1234567890123456789012345678901234567890123456789012" - -# $getProjectId = [GUID]::NewGuid().ToString() -# $getProjectName = "ProjectName_$projectId" -# $getProjectDescription = "ProjectDescription_$projectId" - -# $getAzDevOpsResource = @{ -# id = $getProjectId -# name = $getProjectName -# description = $getProjectDescription -# } - -# $AzDevOpsProjectResource = [AzDevOpsProject]@{ -# ApiUri = $getApiUri -# Pat = $getPat -# ProjectId = $getProjectId -# ProjectName = $getProjectName -# ProjectDescription = $getProjectDescription -# } -# } - - -# Context 'When Azure DevOps is not in the desired state' { -# Context 'When the Azure DevOps "Project" does not exist' { -# BeforeAll { - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $null -# } -Force -PassThru - -# } - -# It 'Should return the correct values' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult | Should -Be $null -# $getResult.ApiUri | Should -Be $null -# $getResult.Pat | Should -Be $null -# $getResult.ProjectId | Should -Be $null -# $getResult.ProjectName | Should -Be $null -# $getResult.ProjectDescription | Should -Be $null -# } -# } - - -# Context 'When the Azure DevOps "Project" exists but "ProjectId" parameter is different' { -# BeforeAll { -# $differentProjectId = [GUID]::NewGuid().ToString() -# $getAzDevOpsResource.ProjectId = $differentProjectId - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectId" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Not -Be $differentProjectId # Different -# $getResult.ProjectName | Should -Be $getProjectName -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } -# } - - -# Context 'When the Azure DevOps "Project" exists but "ProjectName" parameter is different' { -# BeforeAll { -# $differentProjectName = "z" + $getAzDevOpsResource.ProjectName -# $getAzDevOpsResource.ProjectName = $differentProjectName - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectName" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Not -Be $differentProjectName # Different -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } -# } - -# Context 'When the Azure DevOps "Project" exists but "ProjectDescription" parameter is different' { -# BeforeAll { -# $differentProjectDescription = "z" + $getAzDevOpsResource.ProjectDescription -# $getAzDevOpsResource.ProjectDescription = $differentProjectDescription - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectDescription" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Be $getprojectName -# $getResult.ProjectDescription | Should -Not -Be $differentProjectDescription # Different -# } -# } -# } - -# Context 'When Azure DevOps is in the desired state' { -# BeforeAll { - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Be $getprojectName -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } - -# } -# } - -# } -# } -# finally -# { -# Invoke-TestCleanup -# } diff --git a/tests/Unit/DSCClassResources/AzDevOpsProject/Get.Tests.ps1 b/tests/Unit/DSCClassResources/AzDevOpsProject/Get.Tests.ps1 deleted file mode 100644 index 4a96d9ab0..000000000 --- a/tests/Unit/DSCClassResources/AzDevOpsProject/Get.Tests.ps1 +++ /dev/null @@ -1,262 +0,0 @@ -# Initialize tests for module function -. $PSScriptRoot\..\DSCClassResources.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) - - - Describe "$script:subModuleName\Classes\DscResourceBase\Method\$script:commandName" -Tag $script:tag { - - Context 'When calling Get() method' { - - $azDevOpsProjectProperties = @{ - Ensure = 'Present' - ApiUri = 'https://some.api.uri/_apis/' - Pat = '1234567890123456789012345678901234567890123456789012' - ProjectId = 'efb9c508-115d-4380-a038-51f970d7f918' # Random GUID - ProjectName = 'SomeProjectName' - ProjectDescription = 'SomeProjectDescription' - SourceControlType = 'Git' - } - - $azDevOpsProjectApiResource = [PSObject]@{ - id = 'efb9c508-115d-4380-a038-51f970d7f918' # Random GUID - name = 'SomeProjectName' - description = 'SomeProjectDescription' - capabilities = @{ - versioncontrol = @{ - sourceControlType = 'Git' - } - } - } - - $azDevOpsProject = [AzDevOpsProject]$azDevOpsProjectProperties - - # Override/mock the GetDscCurrentStateProperties() method in this class - [ScriptBlock]$GetDscCurrentStateProperties = {return [PSObject]$azDevOpsProjectProperties} - $azDevOpsProject | Add-Member -MemberType ScriptMethod -Name 'GetDscCurrentStateProperties' -Value $GetDscCurrentStateProperties -Force - - It 'Should not throw' { - - {$azDevOpsProject.Get()} | Should -Not -Throw - } - } - } -} - - - - - - - - -# <# -# .SYNOPSIS -# Automated unit test for AzDevOpsProject DSC Resource. -# #> - -# $script:dscModuleName = 'AzureDevOpsDsc' -# $script:dscResourceName = 'AzDevOpsProject' - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } - -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Class' ` -# -TestType 'Unit' -# } - -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment -# } - -# # Begin Testing - -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# Set-StrictMode -Version 1.0 - -# Describe 'AzDevOpsProject\Parameters' -Tag 'Parameter' { -# BeforeAll { -# #$mockInstanceName = 'DSCTEST' - -# #Mock -CommandName Import-SQLPSModule -# } -# } - -# Describe 'AzDevOpsProject\Get' -Tag 'Get' { - - - -# BeforeAll { - -# $getApiUri = "https://www.someUri.api/_apis/" -# $getPat = "1234567890123456789012345678901234567890123456789012" - -# $getProjectId = [GUID]::NewGuid().ToString() -# $getProjectName = "ProjectName_$projectId" -# $getProjectDescription = "ProjectDescription_$projectId" - -# $getAzDevOpsResource = @{ -# id = $getProjectId -# name = $getProjectName -# description = $getProjectDescription -# } - -# $AzDevOpsProjectResource = [AzDevOpsProject]@{ -# ApiUri = $getApiUri -# Pat = $getPat -# ProjectId = $getProjectId -# ProjectName = $getProjectName -# ProjectDescription = $getProjectDescription -# } -# } - - -# Context 'When Azure DevOps is not in the desired state' { -# Context 'When the Azure DevOps "Project" does not exist' { -# BeforeAll { - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $null -# } -Force -PassThru - -# } - -# It 'Should return the correct values' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult | Should -Be $null -# $getResult.ApiUri | Should -Be $null -# $getResult.Pat | Should -Be $null -# $getResult.ProjectId | Should -Be $null -# $getResult.ProjectName | Should -Be $null -# $getResult.ProjectDescription | Should -Be $null -# } -# } - - -# Context 'When the Azure DevOps "Project" exists but "ProjectId" parameter is different' { -# BeforeAll { -# $differentProjectId = [GUID]::NewGuid().ToString() -# $getAzDevOpsResource.ProjectId = $differentProjectId - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectId" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Not -Be $differentProjectId # Different -# $getResult.ProjectName | Should -Be $getProjectName -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } -# } - - -# Context 'When the Azure DevOps "Project" exists but "ProjectName" parameter is different' { -# BeforeAll { -# $differentProjectName = "z" + $getAzDevOpsResource.ProjectName -# $getAzDevOpsResource.ProjectName = $differentProjectName - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectName" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Not -Be $differentProjectName # Different -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } -# } - -# Context 'When the Azure DevOps "Project" exists but "ProjectDescription" parameter is different' { -# BeforeAll { -# $differentProjectDescription = "z" + $getAzDevOpsResource.ProjectDescription -# $getAzDevOpsResource.ProjectDescription = $differentProjectDescription - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectDescription" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Be $getprojectName -# $getResult.ProjectDescription | Should -Not -Be $differentProjectDescription # Different -# } -# } -# } - -# Context 'When Azure DevOps is in the desired state' { -# BeforeAll { - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Be $getprojectName -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } - -# } -# } - -# } -# } -# finally -# { -# Invoke-TestCleanup -# } diff --git a/tests/Unit/DSCClassResources/AzDevOpsProject/GetDscCurrentStateProperties.Tests.ps1 b/tests/Unit/DSCClassResources/AzDevOpsProject/GetDscCurrentStateProperties.Tests.ps1 deleted file mode 100644 index 103642a10..000000000 --- a/tests/Unit/DSCClassResources/AzDevOpsProject/GetDscCurrentStateProperties.Tests.ps1 +++ /dev/null @@ -1,366 +0,0 @@ -# Initialize tests for module function -. $PSScriptRoot\..\DSCClassResources.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) - - - Describe "$script:subModuleName\Classes\DscResourceBase\Method\$script:commandName" -Tag $script:tag { - - - $azDevOpsProjectProperties = @{ - Ensure = 'Present' - ApiUri = 'https://some.api.uri/_apis/' - Pat = '1234567890123456789012345678901234567890123456789012' - ProjectId = 'efb9c508-115d-4380-a038-51f970d7f918' # Random GUID - ProjectName = 'SomeProjectName' - ProjectDescription = 'SomeProjectDescription' - SourceControlType = 'Git' - } - - $azDevOpsProjectApiResource = [PSObject]@{ - id = 'efb9c508-115d-4380-a038-51f970d7f918' # Random GUID - name = 'SomeProjectName' - description = 'SomeProjectDescription' - capabilities = @{ - versioncontrol = @{ - sourceControlType = 'Git' - } - } - } - - $currentResourceObjectThatExists = [PSObject]$azDevOpsProjectApiResource - $currentResourceObjectThatDoesNotExist = [PSObject]@{} - - - $testCasesValidAzDevOpsProjectProperties = $azDevOpsProjectProperties.Keys | ForEach-Object { - @{ - PropertyName = $_ - } - } - $testCasesValidAzDevOpsProjectPropertiesWhenNotExists = $testCasesValidAzDevOpsProjectProperties | - Where-Object { $_.PropertyName -in @('Ensure', 'ApiUri', 'Pat') } - - - Context 'When current resource already exists' { - - It 'Should not throw' { - - $azDevOpsProject = [AzDevOpsProject]::new() - - {$azDevOpsProject.GetDscCurrentStateProperties($currentResourceObjectThatExists)} | Should -Not -Throw - } - - It 'Should return a hashtable with expected key/property - ""' -TestCases $testCasesValidAzDevOpsProjectProperties { - param ([System.String]$PropertyName) - - $azDevOpsProject = [AzDevOpsProject]::new() - - $azDevOpsProject.GetDscCurrentStateProperties($currentResourceObjectThatExists).ContainsKey($PropertyName) | Should -Be $true - } - - It 'Should return a hashtable with an "Ensure" key value of "Present"' { - - $azDevOpsProject = [AzDevOpsProject]::new() - - $azDevOpsProject.GetDscCurrentStateProperties($currentResourceObjectThatExists).Ensure | Should -Be "Present" - } - - It 'Should return a hashtable with output values matching corresponding, API resource values' { - - $azDevOpsProject = [AzDevOpsProject]@{ - ProjectId = $azDevOpsProjectProperties.ProjectId - } - - $dscCurrentStateProperties = $azDevOpsProject.GetDscCurrentStateProperties($currentResourceObjectThatExists) - - $dscCurrentStateProperties.ProjectId | Should -Be $azDevOpsProjectApiResource.id - $dscCurrentStateProperties.ProjectName | Should -Be $azDevOpsProjectApiResource.name - $dscCurrentStateProperties.ProjectDescription | Should -Be $azDevOpsProjectApiResource.description - $dscCurrentStateProperties.SourceControlType | Should -Be $azDevOpsProjectApiResource.capabilities.versioncontrol.sourceControlType - } - - It 'Should return a hashtable with output values matching corresponding, non-API resource values' { - - $azDevOpsProject = [AzDevOpsProject]@{ - ApiUri = $azDevOpsProjectProperties.ApiUri - Pat = $azDevOpsProjectProperties.Pat - } - - $dscCurrentStateProperties = $azDevOpsProject.GetDscCurrentStateProperties($currentResourceObjectThatExists) - - $dscCurrentStateProperties.ApiUri | Should -Be $azDevOpsProjectProperties.ApiUri - $dscCurrentStateProperties.Pat | Should -Be $azDevOpsProjectProperties.Pat - } - } - - - Context 'When current resource does not exist (not $null but with no "id" property)' { - - It 'Should not throw' { - - $azDevOpsProject = [AzDevOpsProject]::new() - - {$azDevOpsProject.GetDscCurrentStateProperties($currentResourceObjectThatDoesNotExist)} | Should -Not -Throw - } - - It 'Should return a hashtable with expected key/property - ""' -TestCases $testCasesValidAzDevOpsProjectPropertiesWhenNotExists { - param ([System.String]$PropertyName) - - $azDevOpsProject = [AzDevOpsProject]::new() - - $azDevOpsProject.GetDscCurrentStateProperties($currentResourceObjectThatDoesNotExist).ContainsKey($PropertyName) | Should -Be $true - } - - It 'Should return a hashtable with an "Ensure" key value of "Absent"' { - - $azDevOpsProject = [AzDevOpsProject]::new() - - $azDevOpsProject.GetDscCurrentStateProperties($currentResourceObjectThatDoesNotExist).Ensure | Should -Be "Absent" - } - } - - - Context 'When current resource is null' { - - It 'Should not throw' { - - $azDevOpsProject = [AzDevOpsProject]::new() - - {$azDevOpsProject.GetDscCurrentStateProperties($null)} | Should -Not -Throw - } - - It 'Should return a hashtable with expected key/property - ""' -TestCases $testCasesValidAzDevOpsProjectPropertiesWhenNotExists { - param ([System.String]$PropertyName) - - $azDevOpsProject = [AzDevOpsProject]::new() - - $azDevOpsProject.GetDscCurrentStateProperties($null).ContainsKey($PropertyName) | Should -Be $true - } - - It 'Should return a hashtable with an "Ensure" key value of "Absent"' { - - $azDevOpsProject = [AzDevOpsProject]::new() - - $azDevOpsProject.GetDscCurrentStateProperties($null).Ensure | Should -Be "Absent" - } - } - } -} - - - - - - - - -# <# -# .SYNOPSIS -# Automated unit test for AzDevOpsProject DSC Resource. -# #> - -# $script:dscModuleName = 'AzureDevOpsDsc' -# $script:dscResourceName = 'AzDevOpsProject' - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } - -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Class' ` -# -TestType 'Unit' -# } - -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment -# } - -# # Begin Testing - -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# Set-StrictMode -Version 1.0 - -# Describe 'AzDevOpsProject\Parameters' -Tag 'Parameter' { -# BeforeAll { -# #$mockInstanceName = 'DSCTEST' - -# #Mock -CommandName Import-SQLPSModule -# } -# } - -# Describe 'AzDevOpsProject\Get' -Tag 'Get' { - - - -# BeforeAll { - -# $getApiUri = "https://www.someUri.api/_apis/" -# $getPat = "1234567890123456789012345678901234567890123456789012" - -# $getProjectId = [GUID]::NewGuid().ToString() -# $getProjectName = "ProjectName_$projectId" -# $getProjectDescription = "ProjectDescription_$projectId" - -# $getAzDevOpsResource = @{ -# id = $getProjectId -# name = $getProjectName -# description = $getProjectDescription -# } - -# $AzDevOpsProjectResource = [AzDevOpsProject]@{ -# ApiUri = $getApiUri -# Pat = $getPat -# ProjectId = $getProjectId -# ProjectName = $getProjectName -# ProjectDescription = $getProjectDescription -# } -# } - - -# Context 'When Azure DevOps is not in the desired state' { -# Context 'When the Azure DevOps "Project" does not exist' { -# BeforeAll { - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $null -# } -Force -PassThru - -# } - -# It 'Should return the correct values' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult | Should -Be $null -# $getResult.ApiUri | Should -Be $null -# $getResult.Pat | Should -Be $null -# $getResult.ProjectId | Should -Be $null -# $getResult.ProjectName | Should -Be $null -# $getResult.ProjectDescription | Should -Be $null -# } -# } - - -# Context 'When the Azure DevOps "Project" exists but "ProjectId" parameter is different' { -# BeforeAll { -# $differentProjectId = [GUID]::NewGuid().ToString() -# $getAzDevOpsResource.ProjectId = $differentProjectId - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectId" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Not -Be $differentProjectId # Different -# $getResult.ProjectName | Should -Be $getProjectName -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } -# } - - -# Context 'When the Azure DevOps "Project" exists but "ProjectName" parameter is different' { -# BeforeAll { -# $differentProjectName = "z" + $getAzDevOpsResource.ProjectName -# $getAzDevOpsResource.ProjectName = $differentProjectName - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectName" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Not -Be $differentProjectName # Different -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } -# } - -# Context 'When the Azure DevOps "Project" exists but "ProjectDescription" parameter is different' { -# BeforeAll { -# $differentProjectDescription = "z" + $getAzDevOpsResource.ProjectDescription -# $getAzDevOpsResource.ProjectDescription = $differentProjectDescription - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectDescription" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Be $getprojectName -# $getResult.ProjectDescription | Should -Not -Be $differentProjectDescription # Different -# } -# } -# } - -# Context 'When Azure DevOps is in the desired state' { -# BeforeAll { - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Be $getprojectName -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } - -# } -# } - -# } -# } -# finally -# { -# Invoke-TestCleanup -# } diff --git a/tests/Unit/DSCClassResources/AzDevOpsProject/GetDscResourcePropertyNamesWithNoSetSupport.Tests.ps1 b/tests/Unit/DSCClassResources/AzDevOpsProject/GetDscResourcePropertyNamesWithNoSetSupport.Tests.ps1 deleted file mode 100644 index b33c98d45..000000000 --- a/tests/Unit/DSCClassResources/AzDevOpsProject/GetDscResourcePropertyNamesWithNoSetSupport.Tests.ps1 +++ /dev/null @@ -1,259 +0,0 @@ -# Initialize tests for module function -. $PSScriptRoot\..\DSCClassResources.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:dscResourceName = Split-Path $PSScriptRoot -Leaf - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Classes\$script:dscResourceName\$script:dscResourceName.psm1" - $script:tag = @($($script:commandName -replace '-')) - - - Describe "$script:subModuleName\Classes\DscResourceBase\Method\$script:commandName" -Tag $script:tag { - - $testCasesPropertyNamesWithNoSetSupport = @( - @{ - PropertyName = 'SourceControlType' - } - ) - - - Context 'When calling GetDscResourcePropertyNamesWithNoSetSupport() method' { - - It 'Should not throw' { - - $azDevOpsProject = [AzDevOpsProject]::new() - - {$azDevOpsProject.GetDscResourcePropertyNamesWithNoSetSupport()} | Should -Not -Throw - } - - It 'Should output expected number of property names' { - - $azDevOpsProject = [AzDevOpsProject]::new() - - $azDevOpsProject.GetDscResourcePropertyNamesWithNoSetSupport().Count | Should -Be $testCasesPropertyNamesWithNoSetSupport.Count - } - - It 'Should output expected "PropertyName" - ""' -TestCases $testCasesPropertyNamesWithNoSetSupport { - param ([System.String]$PropertyName) - - $azDevOpsProject = [AzDevOpsProject]::new() - - $azDevOpsProject.GetDscResourcePropertyNamesWithNoSetSupport() | Should -Contain $PropertyName - } - } - } -} - - - - - - - - -# <# -# .SYNOPSIS -# Automated unit test for AzDevOpsProject DSC Resource. -# #> - -# $script:dscModuleName = 'AzureDevOpsDsc' -# $script:dscResourceName = 'AzDevOpsProject' - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } - -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Class' ` -# -TestType 'Unit' -# } - -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment -# } - -# # Begin Testing - -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# Set-StrictMode -Version 1.0 - -# Describe 'AzDevOpsProject\Parameters' -Tag 'Parameter' { -# BeforeAll { -# #$mockInstanceName = 'DSCTEST' - -# #Mock -CommandName Import-SQLPSModule -# } -# } - -# Describe 'AzDevOpsProject\Get' -Tag 'Get' { - - - -# BeforeAll { - -# $getApiUri = "https://www.someUri.api/_apis/" -# $getPat = "1234567890123456789012345678901234567890123456789012" - -# $getProjectId = [GUID]::NewGuid().ToString() -# $getProjectName = "ProjectName_$projectId" -# $getProjectDescription = "ProjectDescription_$projectId" - -# $getAzDevOpsResource = @{ -# id = $getProjectId -# name = $getProjectName -# description = $getProjectDescription -# } - -# $AzDevOpsProjectResource = [AzDevOpsProject]@{ -# ApiUri = $getApiUri -# Pat = $getPat -# ProjectId = $getProjectId -# ProjectName = $getProjectName -# ProjectDescription = $getProjectDescription -# } -# } - - -# Context 'When Azure DevOps is not in the desired state' { -# Context 'When the Azure DevOps "Project" does not exist' { -# BeforeAll { - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $null -# } -Force -PassThru - -# } - -# It 'Should return the correct values' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult | Should -Be $null -# $getResult.ApiUri | Should -Be $null -# $getResult.Pat | Should -Be $null -# $getResult.ProjectId | Should -Be $null -# $getResult.ProjectName | Should -Be $null -# $getResult.ProjectDescription | Should -Be $null -# } -# } - - -# Context 'When the Azure DevOps "Project" exists but "ProjectId" parameter is different' { -# BeforeAll { -# $differentProjectId = [GUID]::NewGuid().ToString() -# $getAzDevOpsResource.ProjectId = $differentProjectId - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectId" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Not -Be $differentProjectId # Different -# $getResult.ProjectName | Should -Be $getProjectName -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } -# } - - -# Context 'When the Azure DevOps "Project" exists but "ProjectName" parameter is different' { -# BeforeAll { -# $differentProjectName = "z" + $getAzDevOpsResource.ProjectName -# $getAzDevOpsResource.ProjectName = $differentProjectName - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectName" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Not -Be $differentProjectName # Different -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } -# } - -# Context 'When the Azure DevOps "Project" exists but "ProjectDescription" parameter is different' { -# BeforeAll { -# $differentProjectDescription = "z" + $getAzDevOpsResource.ProjectDescription -# $getAzDevOpsResource.ProjectDescription = $differentProjectDescription - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values, with "ProjectDescription" values different' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Be $getprojectName -# $getResult.ProjectDescription | Should -Not -Be $differentProjectDescription # Different -# } -# } -# } - -# Context 'When Azure DevOps is in the desired state' { -# BeforeAll { - -# $AzDevOpsProjectResource = $AzDevOpsProjectResource | -# Add-Member -MemberType 'ScriptMethod' -Name 'GetAzDevOpsResource' -Value { -# return $getAzDevOpsResource -# } -Force -PassThru - -# } - -# It 'Should return the correct values' { -# $getResult = $AzDevOpsProjectResource.Get() - -# $getResult.ApiUri | Should -Be $getApiUri -# $getResult.Pat | Should -Be $getPat -# $getResult.ProjectId | Should -Be $getProjectId -# $getResult.ProjectName | Should -Be $getprojectName -# $getResult.ProjectDescription | Should -Be $getProjectDescription -# } - -# } -# } - -# } -# } -# finally -# { -# Invoke-TestCleanup -# } diff --git a/tests/Unit/DSCClassResources/DSCClassResources.TestInitialization.ps1 b/tests/Unit/DSCClassResources/DSCClassResources.TestInitialization.ps1 deleted file mode 100644 index aeea1dc63..000000000 --- a/tests/Unit/DSCClassResources/DSCClassResources.TestInitialization.ps1 +++ /dev/null @@ -1,22 +0,0 @@ -<# - .SYNOPSIS - Automated unit test for classes in AzureDevOpsDsc. -#> - -Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '\..\Modules\TestHelpers\CommonTestHelper.psm1') -Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '\..\Modules\TestHelpers\CommonTestCases.psm1') - -$script:dscModuleName = 'AzureDevOpsDsc' -$script:dscModule = Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1 -$script:dscModuleFile = $($script:dscModule.ModuleBase +'\'+ $script:dscModuleName + ".psd1") -Get-Module -Name $script:dscModuleName -All | - Remove-Module $script:dscModuleName -Force -ErrorAction SilentlyContinue - -$script:subModuleName = 'AzureDevOpsDsc.Common' -Import-Module -Name $script:dscModuleFile -Force - -Get-Module -Name $script:subModuleName -All | - Remove-Module -Force -ErrorAction SilentlyContinue -$script:subModulesFolder = Join-Path -Path $script:dscModule.ModuleBase -ChildPath 'Modules' -$script:subModuleFile = Join-Path $script:subModulesFolder "$($script:subModuleName)/$($script:subModuleName).psd1" -Import-Module -Name $script:subModuleFile -Force #-Verbose diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.tests.ps1 index e7fb7f907..1cc2ba464 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsACL.tests.ps1 @@ -10,7 +10,7 @@ Describe "Get-DevOpsACL" -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.tests.ps1 index b8248e392..4b33f5270 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ACL/Get-DevOpsDescriptorIdentity.tests.ps1 @@ -11,7 +11,7 @@ Describe 'Get-DevOpsDescriptorIdentity' -Tags "Unit", "API" { # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-AzDoPermission.tests.ps1 similarity index 96% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-AzDoPermission.tests.ps1 index 1da955f64..a14cc71c0 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-xAzDoPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Remove-AzDoPermission.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Remove-AzDoPermission' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-AzDoPermission.tests.ps1 similarity index 96% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-AzDoPermission.tests.ps1 index 59b01eb65..925dcf7cd 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-xAzDoPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/AzDoPermission/Set-AzDoPermission.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Set-AzDoPermission Tests' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.tests.ps1 index 2548a0f3c..68a7fae94 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGitRepository.tests.ps1 @@ -10,7 +10,7 @@ Describe 'List-DevOpsGitRepository' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.tests.ps1 index a2d80307a..f53c3c5cb 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroupMembers.tests.ps1 @@ -10,7 +10,7 @@ Describe 'List-DevOpsGroupMembers' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.tests.ps1 index 10dee9de8..5edd05531 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsGroups.tests.ps1 @@ -10,7 +10,7 @@ Describe 'List-DevOpsGroups' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.tests.ps1 index 84e2774a2..8cc8079bf 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProcess.tests.ps1 @@ -10,7 +10,7 @@ Describe 'List-DevOpsProcess' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.tests.ps1 index 7d0f9fd2e..dd8d447f4 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsProjects.tests.ps1 @@ -10,7 +10,7 @@ Describe "List-DevOpsProjects" -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.tests.ps1 index f96d04fbf..7ac7488cc 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsSecurityNamespaces.tests.ps1 @@ -10,7 +10,7 @@ Describe "List-DevOpsSecurityNamespaces" -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.tests.ps1 index 35c5a8712..8154ab171 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-DevOpsServicePrinciples.tests.ps1 @@ -10,7 +10,7 @@ Describe 'List-DevOpsServicePrinciples' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.tests.ps1 index dc0f4019a..b78593296 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Cache/List-UserCache.tests.ps1 @@ -10,7 +10,7 @@ Describe 'List-UserCache' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.tests.ps1 index 171ca704c..1e7a28456 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/New-GitRepository.tests.ps1 @@ -10,7 +10,7 @@ Describe 'New-GitRepository Tests' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.tests.ps1 index 11f972140..c029c96dc 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GitRepository/Remove-GitRepository.tests.ps1 @@ -10,7 +10,7 @@ Describe "Remove-GitRepository" -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.tests.ps1 index 1e273e257..d31d70865 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/New-DevOpsGroup.tests.ps1 @@ -10,7 +10,7 @@ Describe "New-DevOpsGroup" -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.tests.ps1 index c1990d6e3..898799b3c 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Remove-DevOpsGroup.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Remove-DevOpsGroup' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.tests.ps1 index 777d13548..caa207b32 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Group/Set-DevOpsGroup.tests.ps1 @@ -9,7 +9,7 @@ Describe 'Set-DevOpsGroup' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.tests.ps1 index cde060147..1ada5c190 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/New-DevOpsGroupMember.tests.ps1 @@ -10,7 +10,7 @@ Describe "New-DevOpsGroupMember Tests" -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.tests.ps1 index f0a9efe26..50dda54ca 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/GroupMember/Remove-DevOpsGroupMember.tests.ps1 @@ -9,7 +9,7 @@ Describe 'Remove-DevOpsGroupMember' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.tests.ps1 index 71755fd34..acb8f85f5 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/New-DevOpsProject.tests.ps1 @@ -9,7 +9,7 @@ Describe 'New-DevOpsProject' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.tests.ps1 index f105177c9..4e4613f3d 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Remove-DevOpsProject.tests.ps1 @@ -10,7 +10,7 @@ Describe "Remove-DevOpsProject" -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.tests.ps1 index c92b343d2..0360f554b 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.tests.ps1 @@ -10,7 +10,7 @@ Describe "Update-DevOpsProject" -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.tests.ps1 index 2d2f5630c..590784c8d 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.tests.ps1 @@ -10,7 +10,7 @@ Describe "Wait-DevOpsProject" -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.tests.ps1 index d822ce133..0c493f191 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Get-ProjectServiceStatus.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Get-ProjectServiceStatus' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.tests.ps1 index 665d2c257..af6753817 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/ProjectServices/Set-ProjectServiceStatus.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Set-ProjectServiceStatus' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.tests.ps1 index 676afe2e1..257ae5915 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/SecurityDescriptor/Get-DevOpsSecurityDescriptor.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Get-DevOpsSecurityDescriptor Tests' -Tags "Unit", "API" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.tests.ps1 index 4114161b3..4f8ff6608 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Add-AuthenticationHTTPHeader.tests.ps1 @@ -10,7 +10,7 @@ Describe "Add-AuthenticationHTTPHeader" -Tags "Unit", "Authentication" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.tests.ps1 index f18fb4007..ce7009dda 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Get-AzManagedIdentityToken.tests.ps1 @@ -10,7 +10,7 @@ Describe "Get-AzManagedIdentityToken Tests" -Tags "Unit", "Authentication" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } @@ -41,24 +41,138 @@ Describe "Get-AzManagedIdentityToken Tests" -Tags "Unit", "Authentication" { } } - Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { - return @{ - access_token = "fake-access-token" + Mock -CommandName Test-AzToken -MockWith { return $true } + Mock -CommandName Get-OperatingSystemInfo -MockWith { return @{ Windows = $true; Linux = $false; MacOS = $false } } + Mock -CommandName Get-Content -MockWith { return "mock-data" } + Mock -CommandName Test-isWindowsAdmin -MockWith { return $true } + + class CustomException : Exception { + [hashtable] $response + + CustomException($Message, $response) : base($Message) { + $this.response = $response } } - Mock -CommandName Test-AzToken -MockWith { return $true } + + function Invoke-MockError { + # Create a custom WebException with a response containing headers + $response = @{ + Headers = @{ + wwwAuthenticate = 'Basic realm=MOCKMOCKMOCKMOCKMOCKMOCK' + } + } + + throw [CustomException]::New("Mock Error", $response) + } + } Context "When Verify switch is not set" { + + BeforeAll { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ + access_token = "fake-access-token" + } + } + } + It "should return managed identity token" { $result = Get-AzManagedIdentityToken -OrganizationName "Contoso" $result.AccessToken | Should -Be "fake-access-token" } } + Context "When it is an Azure Arc Machine and the console is not being run as Administrator" { + + AfterAll { + Remove-Variable -Name IDENTITY_ENDPOINT -Scope Global -ErrorAction SilentlyContinue + } + + BeforeAll { + + $env:IDENTITY_ENDPOINT = 'mock-url' + + Mock -CommandName Test-isWindowsAdmin -MockWith { + return $false + } + } + + It "should throw error" { + { + Get-AzManagedIdentityToken -OrganizationName "Contoso" + } | Should -Throw "*Error: Authentication to Azure Arc requires Administrator privileges.*" + } + } + + Context "When it is an Azure Arc Machine and the console is being run as Administrator" { + + AfterAll { + Remove-Variable -Name IDENTITY_ENDPOINT -Scope Global -ErrorAction SilentlyContinue + } + + BeforeAll { + + $env:IDENTITY_ENDPOINT = 'mock-url' + + Mock -CommandName Test-isWindowsAdmin -MockWith { + return $true + } + + } + + It "should not throw error" { + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $null -eq $HttpHeaders.Authorization + } -MockWith { Invoke-MockError } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $null -ne $HttpHeaders.Authorization + } -MockWith { return @{ access_token = "mock-data" } } + + Get-AzManagedIdentityToken -OrganizationName "Contoso" + Assert-MockCalled -CommandName Test-isWindowsAdmin + Assert-MockCalled -CommandName Get-Content + Assert-MockCalled -CommandName Invoke-AzDevOpsApiRestMethod -Times 2 + + } + } + + Context "Linux Machines" { + + BeforeAll { + Mock -CommandName Test-isWindowsAdmin -MockWith { + return $false + } + Mock -CommandName Get-OperatingSystemInfo -MockWith { return @{ Windows = $false; Linux = $true; MacOS = $false } } + Mock -CommandName Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $null -eq $HttpHeaders.Authorization + } -MockWith { Invoke-MockError } + + Mock -CommandName Invoke-AzDevOpsApiRestMethod -ParameterFilter { + $null -ne $HttpHeaders.Authorization + } -MockWith { return @{ access_token = "mock-data" } } + } + + It "should not call Test-isWindowsAdmin" { + Get-AzManagedIdentityToken -OrganizationName "Contoso" + Assert-MockCalled -CommandName Test-isWindowsAdmin -Times 0 + } + } + Context "When Verify switch is set" { + + BeforeAll { + Mock -CommandName Invoke-AzDevOpsApiRestMethod -MockWith { + return @{ + access_token = "fake-access-token" + } + } + } + It "should return managed identity token after verification" { $result = Get-AzManagedIdentityToken -OrganizationName "Contoso" -Verify $result.AccessToken | Should -Be "fake-access-token" diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.tests.ps1 index 97c07e131..dfe1c9d09 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/ManagedIdentity/Update-AzManagedIdentity.tests.ps1 @@ -10,7 +10,7 @@ Describe "Update-AzManagedIdentity" -Tags "Unit", "Authentication" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.tests.ps1 index d33a7d349..aa64d4451 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/PersonalAccessToken/Set-AzPersonalAccessToken.tests.ps1 @@ -10,7 +10,7 @@ Describe "Set-AzPersonalAccessToken" -Tags "Unit", "Authentication" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.tests.ps1 index dbbe19b93..20f3bc4d9 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Authentication/Test-AzToken.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Test-AzToken' -Tags "Unit", "Authentication" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.tests.ps1 index 2ab4399e3..6dfad5c14 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.tests.ps1 @@ -17,7 +17,7 @@ Describe "Add-CacheItem" -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.tests.ps1 index f6fbbebc9..076e1f07f 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.tests.ps1 @@ -13,7 +13,7 @@ Describe 'AzDoAPI_0_ProjectCache' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.tests.ps1 index 4022b499f..3c9e83417 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/1.GroupCache.tests.ps1 @@ -20,7 +20,7 @@ Describe 'AzDoAPI_1_GroupCache' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.tests.ps1 index 27dc50814..bdda2538e 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/2.UserCache.tests.ps1 @@ -13,7 +13,7 @@ Describe 'AzDoAPI_2_UserCache' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.tests.ps1 index a922156e6..4a5f61dba 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/3.GroupMemberCache.tests.ps1 @@ -13,7 +13,7 @@ Describe 'AzDoAPI_3_GroupMemberCache' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.tests.ps1 index b52aa6b40..4d1c3c17c 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/4.GitRepositoryCache.tests.ps1 @@ -10,7 +10,7 @@ Describe "AzDoAPI_4_GitRepositoryCache Tests" -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.tests.ps1 index 829e13091..d51975541 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/5.PermissionsCache.tests.ps1 @@ -10,7 +10,7 @@ Describe 'AzDoAPI_5_PermissionsCache Tests' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.tests.ps1 index 54cee242f..82f86b85a 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/6.ServicePrinciple.tests.ps1 @@ -10,7 +10,7 @@ Describe 'AzDoAPI_6_ServicePrinciple' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.tests.ps1 index f5e1e3160..f76d31fdc 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/7.IdentitySubjectDescriptors.tests.ps1 @@ -10,7 +10,7 @@ Describe "AzDoAPI_7_IdentitySubjectDescriptors" -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.tests.ps1 index 40ee59a23..331963e53 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/8.ProcessTemplates.tests.ps1 @@ -10,7 +10,7 @@ Describe 'AzDoAPI_8_ProjectProcessTemplates' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.tests.ps1 index c0eec1940..e583f5bf7 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Export-CacheObject.tests.ps1 @@ -17,7 +17,7 @@ Describe "Export-CacheObject" -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.tests.ps1 index 33772d05b..9d37d4411 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.tests.ps1 @@ -22,7 +22,7 @@ Describe 'Find-CacheItem' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.tests.ps1 index c2b787b82..1cf9d6eeb 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheItem.tests.ps1 @@ -17,7 +17,7 @@ Describe 'Get-CacheItem' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.tests.ps1 index 53872183d..51d4cd313 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.tests.ps1 @@ -13,7 +13,7 @@ Describe 'Get-CacheObject Tests' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.tests.ps1 index ad76010a7..a3e7b8c0f 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Import-CacheObject.tests.ps1 @@ -13,7 +13,7 @@ Describe "Import-CacheObject Tests" -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.tests.ps1 index 8be3a79d9..04f471ec7 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Initialize-CacheObject.tests.ps1 @@ -13,7 +13,7 @@ Describe "Initialize-CacheObject Tests" -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.tests.ps1 index fcedd7d7a..ebaf91b26 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-AzDoCache.tests.ps1 @@ -13,7 +13,7 @@ Describe "Refresh-AzDoCache Tests" -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } @@ -39,6 +39,7 @@ Describe "Refresh-AzDoCache Tests" -Tags "Unit", "Cache" { Mock -CommandName AzDoAPI_CacheType1 Mock -CommandName AzDoAPI_CacheType2 Mock -CommandName Remove-Variable + Mock -CommandName Import-CacheObject } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.tests.ps1 index f50359c82..359389cfe 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheIdentity.tests.ps1 @@ -9,7 +9,7 @@ Describe 'Refresh-CacheIdentity' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.tests.ps1 index 11ff3eff5..aab3d497e 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Refresh-CacheObject.tests.ps1 @@ -16,11 +16,11 @@ Describe "Refresh-CacheObject" -tags Unit, Cache { # Load the functions to test if ($null -eq $currentFile) { - $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Remove-CacheItem.tests.ps1' + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'Refresh-CacheObject.tests.ps1' } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.tests.ps1 index 367fdd298..4de5d30af 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Remove-CacheItem.tests.ps1 @@ -16,7 +16,7 @@ Describe 'Remove-CacheItem' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.tests.ps1 index 3a4212921..269505a87 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.tests.ps1 @@ -13,7 +13,7 @@ Describe 'Set-CacheObject' -Tags "Unit", "Cache" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.tests.ps1 index 10eb829a8..976453ec4 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.tests.ps1 @@ -10,7 +10,7 @@ Describe "ConvertTo-ACEList" -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.tests.ps1 index aaf16cfb0..d9b920142 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACETokenList.tests.ps1 @@ -10,7 +10,7 @@ Describe "ConvertTo-ACETokenList Tests" -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.tests.ps1 index ac1813905..3c6e7a19c 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.tests.ps1 @@ -10,11 +10,11 @@ Describe "ConvertTo-ACL" -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } - + Mock -CommandName Write-Host Mock -CommandName Write-Warning Mock -CommandName New-ACLToken -MockWith { return @{ Token = "mockToken" } } Mock -CommandName ConvertTo-ACEList -MockWith { diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.tests.ps1 index bffc54d4b..105ccd094 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACLHashtable.tests.ps1 @@ -11,7 +11,7 @@ Describe "ConvertTo-ACLHashtable" -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.tests.ps1 index 1963a6c8a..fd54cdc4c 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.tests.ps1 @@ -12,7 +12,7 @@ Describe "ConvertTo-FormattedACL" -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.tests.ps1 index e3727e34a..b797c98e6 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedToken.tests.ps1 @@ -12,7 +12,7 @@ Describe "ConvertTo-FormattedToken" -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } @@ -24,8 +24,6 @@ Describe "ConvertTo-FormattedToken" -Tags "Unit", "ACL", "Helper" { type = 'GitOrganization' } - Mock -CommandName ConvertTo-FormattedToken -ParameterFilter { $Token.type -eq 'GitOrganization' } -MockWith { return 'repoV2' } - $result = ConvertTo-FormattedToken -Token $token $result | Should -Be 'repoV2' @@ -37,8 +35,6 @@ Describe "ConvertTo-FormattedToken" -Tags "Unit", "ACL", "Helper" { projectId = 'myProject' } - Mock -CommandName ConvertTo-FormattedToken -ParameterFilter { $Token.type -eq 'GitProject' -and $Token.projectId -eq 'myProject' } -MockWith { return 'repoV2/myProject' } - $result = ConvertTo-FormattedToken -Token $token $result | Should -Be 'repoV2/myProject' @@ -51,8 +47,6 @@ Describe "ConvertTo-FormattedToken" -Tags "Unit", "ACL", "Helper" { RepoId = 'myRepo' } - Mock -CommandName ConvertTo-FormattedToken -ParameterFilter { $Token.type -eq 'GitRepository' -and $Token.projectId -eq 'myProject' -and $Token.RepoId -eq 'myRepo' } -MockWith { return 'repoV2/myProject/myRepo' } - $result = ConvertTo-FormattedToken -Token $token $result | Should -Be 'repoV2/myProject/myRepo' @@ -63,8 +57,6 @@ Describe "ConvertTo-FormattedToken" -Tags "Unit", "ACL", "Helper" { type = 'UnknownType' } - Mock -CommandName ConvertTo-FormattedToken -ParameterFilter { $Token.type -eq 'UnknownType' } -MockWith { return '' } - $result = ConvertTo-FormattedToken -Token $token $result | Should -Be '' @@ -73,8 +65,6 @@ Describe "ConvertTo-FormattedToken" -Tags "Unit", "ACL", "Helper" { It "should return an empty string for an empty token" { $token = @{} - Mock -CommandName ConvertTo-FormattedToken -ParameterFilter { $null -eq $Token.type } -MockWith { return '' } - $result = ConvertTo-FormattedToken -Token $token $result | Should -Be '' diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.tests.ps1 index 9af20dcef..ee09895c2 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Format-ACEs.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Format-ACEs' -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.tests.ps1 index d6d610652..61a4d316c 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Get-BitwiseOrResult' -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.tests.ps1 index 5ee91cbb8..40c856ee9 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Group-ACEs.tests.ps1 @@ -10,29 +10,11 @@ Describe 'Group-ACEs' -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } - Mock -CommandName Group-ACEs -MockWith { - param ($ACEs) - if ($ACEs.Count -eq 0) { - return @() - } elseif ($ACEs.Count -eq 1) { - return $ACEs - } else { - $groupedACEs = @{} - foreach ($ace in $ACEs) { - $id = $ace.Identity.value.originId - if (-not $groupedACEs.ContainsKey($id)) { - $groupedACEs[$id] = $ace - } - } - return $groupedACEs.Values - } - } - $ace1 = @{ Identity = @{ value = @{ @@ -87,11 +69,17 @@ Describe 'Group-ACEs' -Tags "Unit", "ACL", "Helper" { } It 'Groups multiple identities correctly' { + $result = Group-ACEs -ACEs @($ace1, $ace2, $ace3) $result.Count | Should -Be 2 - $grouped = $result | Where-Object { $_.Identity.value.originId -eq "user1" } - $grouped.Permissions.Deny | Should -Be 0,1 - $grouped.Permissions.Allow | Should -Be 2,3 + $user1 = $result | Where-Object { $_.Identity.value.originId -eq "user1" } + $user2 = $result | Where-Object { $_.Identity.value.originId -eq "user2" } + + $user1.Permissions.Deny | Should -Be 0 + $user1.Permissions.Allow | Should -Be 2 + $user2.Permissions.Deny | Should -Be 0,1 + $user2.Permissions.Allow | Should -Be 2,3 + } It "Doesn't group single identity ACE" { diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.tests.ps1 index aa0722d9f..a2ec3e9b4 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/New-ACLToken.tests.ps1 @@ -10,7 +10,7 @@ Describe 'New-ACLToken Function Tests' -Skip -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.tests.ps1 index c690a0d64..d3c571da1 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Parse-ACLToken.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Parse-ACLToken' -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.tests.ps1 index 5e0d98632..40ef306c7 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Resolve-ACLToken.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Resolve-ACLToken' -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.tests.ps1 index f77401efc..9feba20f5 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Test-ACLListforChanges.tests.ps1 @@ -9,7 +9,7 @@ Describe "Test-ACLListforChanges" -Tags "Unit", "ACL", "Helper" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.tests.ps1 index 58e649927..055837aa7 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiHttpRequestHeader.tests.ps1 @@ -11,7 +11,7 @@ Describe 'Test-AzDevOpsApiHttpRequestHeader' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.tests.ps1 index bc6c46218..f29b05609 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiResourceId.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Test-AzDevOpsApiResourceId' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.tests.ps1 index ee2f9ff96..d1723909e 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiTimeoutExceeded.tests.ps1 @@ -9,7 +9,7 @@ Describe "Test-AzDevOpsApiTimeoutExceeded" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.tests.ps1 index d364c6cb2..65306952f 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiUri.tests.ps1 @@ -9,7 +9,7 @@ Describe 'Test-AzDevOpsApiUri' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.tests.ps1 index 4fe196070..3cab3d9f3 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Test-AzDevOpsApiVersion.tests.ps1 @@ -8,7 +8,7 @@ Describe 'Test-AzDevOpsApiVersion' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.tests.ps1 index 8ea659353..6d670dd67 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/API/Wait-AzDevOpsApiResource.tests.ps1 @@ -9,7 +9,7 @@ Describe 'Wait-AzDevOpsApiResource' -skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.tests.ps1 index 2416d53d7..42ff9e0ae 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.tests.ps1 @@ -10,7 +10,7 @@ Describe "ConvertTo-Base64String" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.tests.ps1 index 7f42f2b3d..7481edb56 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-AzDoIdentity.tests.ps1 @@ -10,7 +10,7 @@ Describe "Find-AzDoIdentity" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 index 54777eca1..2b400c4a9 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 @@ -11,7 +11,7 @@ Describe 'Find-Identity Function Tests' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } @@ -81,6 +81,48 @@ Describe 'Find-Identity Function Tests' { } + Context "when searching the cache with a known name" { + + BeforeAll { + + $params = @( + @{ + SearchType = 'descriptor' + } + @{ + SearchType = 'descriptorId' + } + @{ + SearchType = 'originId' + } + @{ + SearchType = 'principalName' + } + @{ + SearchType = 'displayName' + } + ) + + } + + it 'Should write a non-terminating error when the SearchType is incorrect' { + Mock Write-Error -Verifiable + { Find-Identity -Name 'groupDescriptor' -OrganizationName 'TestOrg' -SearchType 'invalidType' } | Should -Throw + $result | Should -BeNullOrEmpty + } + + it 'Should return a value for the search-type ' -TestCases $params { + param ( + [string]$SearchType + ) + + $result = Find-Identity -Name 'groupDescriptor' -OrganizationName 'TestOrg' -SearchType $SearchType + $result | Should -Not -BeNullOrEmpty + + } + + } + Context "when searching the API" { It 'Should return null for non-existent descriptor' { diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 index 24b3cab6a..e22b28db9 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoGroup.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Format-AzDoGroup' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.tests.ps1 index 760ecda39..fab0b1bbe 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-AzDoProjectName.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Format-AzDoProjectName' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.tests.ps1 index 6825763ee..bff737772 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Format-DescriptorType.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Format-DescriptorType' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiHttpRequestHeader.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiHttpRequestHeader.tests.ps1 index a52587068..00963ac3b 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiHttpRequestHeader.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiHttpRequestHeader.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Get-AzDevOpsApiHttpRequestHeader' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceName.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceName.tests.ps1 index 968195920..3e4201975 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceName.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiResourceName.tests.ps1 @@ -9,7 +9,7 @@ Describe 'Get-AzDevOpsApiResourceName' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriAreaName.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriAreaName.tests.ps1 index 6fca0f7e4..3507b6789 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriAreaName.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiUriAreaName.tests.ps1 @@ -9,7 +9,7 @@ Describe 'Get-AzDevOpsApiUriAreaName' -skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.tests.ps1 index 1ca0622cd..8fa65953f 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiVersion.tests.ps1 @@ -9,7 +9,7 @@ Describe 'Get-AzDevOpsApiVersion Tests' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitIntervalMs.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitIntervalMs.tests.ps1 index 6dcd8cde2..8bc920466 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitIntervalMs.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitIntervalMs.tests.ps1 @@ -10,7 +10,7 @@ Describe "Get-AzDevOpsApiWaitIntervalMs" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitTimeoutMs.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitTimeoutMs.tests.ps1 index 6b90d0338..7ca5d8e42 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitTimeoutMs.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDevOpsApiWaitTimeoutMs.tests.ps1 @@ -10,7 +10,7 @@ Describe "Get-AzDevOpsApiWaitTimeoutMs" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.tests.ps1 index 3f84ff4c5..27b5bf601 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Get-AzDoCacheObjects.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Get-AzDoCacheObjects' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.tests.ps1 index e6ea0bc84..58ece83a6 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.tests.ps1 @@ -10,7 +10,7 @@ Describe 'Invoke-AzDevOpsApiRestMethod' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.tests.ps1 index ecf4bb866..abe41de14 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/New-AzDevOpsACLToken.tests.ps1 @@ -11,7 +11,7 @@ Describe 'New-AzDevOpsACLToken' -Skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.InvokeTests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.InvokeTests.ps1 deleted file mode 100644 index 5347ffe02..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.InvokeTests.ps1 +++ /dev/null @@ -1,37 +0,0 @@ -<# - .SYNOPSIS - Automated unit test for classes in AzureDevOpsDsc. -#> - - -Function Split-RecurivePath { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - [string]$Path, - [Parameter(Mandatory = $false)] - [int]$Times = 1 - ) - - 1 .. $Times | ForEach-Object { - $Path = Split-Path -Path $Path -Parent - } - - $Path -} - -$Global:RepositoryRoot = Split-RecurivePath $PSScriptRoot -Times 4 -$script:CurrentFolder = Split-RecurivePath $PSScriptRoot -Times 1 - -Import-Module -Name (Join-Path -Path $script:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestCases.psm1') -Import-Module -Name (Join-Path -Path $script:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestHelper.psm1') -Import-Module -Name (Join-Path -Path $script:RepositoryRoot -ChildPath '/tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1') - -# -# Recurse through the folders and invoke the tests. - -$script:TestFolders = Get-ChildItem -Path (Join-Path -Path $script:CurrentFolder -ChildPath '\AzureDevOpsDsc.Common') -Directory - -ForEach ($TestFolder in $script:TestFolders) { - Invoke-Pester -Path $TestFolder.FullName -Output Detailed -PassThru -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzDevOpsCache.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzDevOpsCache.ps1.disabled deleted file mode 100644 index 5a42d6490..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzDevOpsCache.ps1.disabled +++ /dev/null @@ -1,87 +0,0 @@ -<# -.SYNOPSIS - Retrieves the Azure DevOps API cache. - -.DESCRIPTION - The Get-AzDevOpsApiCache function is used to retrieve the cached data from the Azure DevOps API. It checks for the presence of the cache files and verifies the parameters before returning the cached data. - -.PARAMETER ApiEndpoint - Specifies the API endpoint to retrieve the cache for. - -.PARAMETER Parameters - Specifies the parameters used for the API endpoint. - -.EXAMPLE - Get-AzDevOpsApiCache -ApiEndpoint 'projects/list' -Parameters @{ organization = 'myOrg' } - - This example retrieves the cached data for the 'projects/list' API endpoint with the specified parameters. - -#> - -. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc.Common' { - Describe 'Get-AzDevOpsApiCache Tests' { - BeforeAll { - # Mock the environment variable for cache path - $env:AZDODSCCachePath = "TestCachePath" - - # Define test data - $testApiEndpoint = 'projects/list' - $testParameters = @{ organization = 'myOrg' } - $normalizedApiEndpoint = $testApiEndpoint -replace '[\/:\*\?"<>|]', '_' - $metadataFilePath = Join-Path -Path $env:AZDODSCCachePath -ChildPath "${normalizedApiEndpoint}_test.metadata.json" - $cacheFilePath = Join-Path -Path $env:AZDODSCCachePath -ChildPath "${normalizedApiEndpoint}_test.cache.json" - - # Create mock metadata and cache files - $metadataObject = @{ - Parameters = @{ organization = 'myOrg' } - CacheFile = "${normalizedApiEndpoint}_test.cache.json" - } | ConvertTo-Json - Set-Content -Path $metadataFilePath -Value $metadataObject - - $cacheObject = @{ - Data = "Cached API response" - } | ConvertTo-Json - Set-Content -Path $cacheFilePath -Value $cacheObject - } - - It 'Throws an exception if AZDODSCCachePath environment variable is not set' { - # Temporarily remove the environment variable - Remove-Item Env:\AZDODSCCachePath - - { Get-AzDevOpsApiCache -ApiEndpoint $testApiEndpoint -Parameters $testParameters } | Should -Throw -ExpectedMessage 'AZDODSCCachePath environment variable is not set.' - - # Restore the environment variable - $env:AZDODSCCachePath = "TestCachePath" - } - - It 'Returns $null if no metadata files are found' { - Mock Get-ChildItem { return @() } - - $result = Get-AzDevOpsApiCache -ApiEndpoint $testApiEndpoint -Parameters $testParameters - $result | Should -Be $null - } - - It 'Returns $null if parameters do not match' { - $wrongParameters = @{ organization = 'anotherOrg' } - - $result = Get-AzDevOpsApiCache -ApiEndpoint $testApiEndpoint -Parameters $wrongParameters - $result | Should -Be $null - } - - It 'Returns cache content if parameters match and cache file exists' { - $result = Get-AzDevOpsApiCache -ApiEndpoint $testApiEndpoint -Parameters $testParameters - $result.Data | Should -Be 'Cached API response' - } - - AfterAll { - # Clean up test files - Remove-Item $metadataFilePath - Remove-Item $cacheFilePath - - # Clean up environment variable - Remove-Item Env:\AZDODSCCachePath - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzManagedIdentityToken.Tests.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzManagedIdentityToken.Tests.ps1.disabled deleted file mode 100644 index 9dba21da8..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Get-AzManagedIdentityToken.Tests.ps1.disabled +++ /dev/null @@ -1,72 +0,0 @@ -<# -.SYNOPSIS - Test suite for the Get-AzManagedIdentityToken function. - -.DESCRIPTION - This test suite validates the functionality of the Get-AzManagedIdentityToken function, ensuring it handles various scenarios correctly. -#> - -. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc.Common' { - - Describe "Get-AzManagedIdentityToken Function Tests" { - - BeforeAll { - - Import-Module 'AzureDevOpsDsc' - - Mock Invoke-AzDevOpsApiRestMethod { - return @{ - access_token = 'mock_access_token' - expires_on = 1000 - expires_in = 1000 - resource = "STRING" - token_type = "bearer" - } - } - - Mock Test-Token { - return $true - } - - } - - It "Obtains a token without verifying if the Verify switch is not set" { - # Arrange - $organizationName = "Contoso" - # Act - $token = Get-AzManagedIdentityToken -OrganizationName $organizationName - # Assert - $token.access_token | Should -BeOfType [System.Security.SecureString] - } - - It "Verifies the connection and obtains a token when the Verify switch is set" { - # Arrange - $organizationName = "Contoso" - # Act / Assert - { Get-AzManagedIdentityToken -OrganizationName $organizationName -Verify } | Should -Not -Throw - } - - It "Throws an exception if the Invoke-AzDevOpsApiRestMethod does not return an access token" { - # Arrange - Mock Invoke-AzDevOpsApiRestMethod { - return @{} - } - $organizationName = "Contoso" - # Act / Assert - { Get-AzManagedIdentityToken -OrganizationName $organizationName } | Should -Throw - } - - It "Throws an exception if the Test-Token returns false" { - # Arrange - Mock Test-Token { - return $false - } - $organizationName = "Contoso" - # Act / Assert - { Get-AzManagedIdentityToken -OrganizationName $organizationName -Verify } | Should -Throw - } - } - -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsACLToken.Tests.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsACLToken.Tests.ps1.disabled deleted file mode 100644 index 17ddbca92..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsACLToken.Tests.ps1.disabled +++ /dev/null @@ -1,68 +0,0 @@ -<# -.SYNOPSIS - This script contains unit tests for the New-AzDevOpsACLToken function. - -.DESCRIPTION - The New-AzDevOpsACLToken function is used to generate access tokens for Azure DevOps. - It can generate project-level access tokens or team-level access tokens. - - This script contains unit tests to verify the behavior of the New-AzDevOpsACLToken function. - -.NOTES - Author: Your Name - Date: Current Date - -.LINK - https://link-to-documentation - -.EXAMPLE - Describe "New-AzDevOpsACLToken Tests" { - Context "Project-level access token" { - It "Returns the correct token without TeamId" { - $OrganizationName = "MyOrg" - $ProjectId = "1234" - $result = New-AzDevOpsACLToken -OrganizationName $OrganizationName -ProjectId $ProjectId - $expectedToken = "vstfs:///Classification/TeamProject/$ProjectId" - $result | Should -BeExactly $expectedToken - } - } - - Context "Team-level access token" { - It "Returns the correct token with TeamId" { - $OrganizationName = "MyOrg" - $ProjectId = "1234" - $TeamId = "abcd" - $result = New-AzDevOpsACLToken -OrganizationName $OrganizationName -ProjectId $ProjectId -TeamId $TeamId - $expectedToken = "vstfs:///Classification/TeamProject/$ProjectId/$TeamId" - $result | Should -BeExactly $expectedToken - } - } - } -#> - -. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc.Common' { - Describe "New-AzDevOpsACLToken Tests" { - Context "Project-level access token" { - It "Returns the correct token without TeamId" { - $OrganizationName = "MyOrg" - $ProjectId = "1234" - $result = New-AzDevOpsACLToken -OrganizationName $OrganizationName -ProjectId $ProjectId - $expectedToken = "vstfs:///Classification/TeamProject/$ProjectId" - $result | Should -BeExactly $expectedToken - } - } - - Context "Team-level access token" { - It "Returns the correct token with TeamId" { - $OrganizationName = "MyOrg" - $ProjectId = "1234" - $TeamId = "abcd" - $result = New-AzDevOpsACLToken -OrganizationName $OrganizationName -ProjectId $ProjectId -TeamId $TeamId - $expectedToken = "vstfs:///Classification/TeamProject/$ProjectId/$TeamId" - $result | Should -BeExactly $expectedToken - } - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiCache.Tests.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiCache.Tests.ps1.disabled deleted file mode 100644 index 915806721..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiCache.Tests.ps1.disabled +++ /dev/null @@ -1,68 +0,0 @@ -<# -.SYNOPSIS -Unit tests for the New-AzDevOpsApiCache function. - -.DESCRIPTION -This script contains unit tests for the New-AzDevOpsApiCache function in the AzureDevOpsDsc.Common module. The tests cover various scenarios such as error handling, cache directory creation, file generation, and parameter handling. - -.PARAMETER ApiEndpoint -The API endpoint to be cached. - -.PARAMETER Parameters -The parameters to be passed to the API endpoint. - -.PARAMETER Content -The content to be cached. - -.PARAMETER Depth -The depth of the JSON conversion. - -.EXAMPLE -#> - -. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 - -Describe 'New-AzDevOpsApiCache Tests' { - BeforeAll { - # Mock the Get-Date cmdlet to return a predictable date-time - Mock Get-Date { return [DateTime]::ParseExact('2023-01-01T00:00:00.0000000Z', 'o', $null) } - } - - It 'Throws an error when AZDODSCCachePath is not set' { - # Temporarily clear the AZDODSCCachePath environment variable for this test - $originalAzDodscCachePath = $ENV:AZDODSCCachePath - $ENV:AZDODSCCachePath = $null - - { New-AzDevOpsApiCache -ApiEndpoint 'projects/list' -Parameters @{ organization = 'myOrg' } -Content @{ key1 = 'value1' } } | Should -Throw 'AZDODSCCachePath environment variable is not set.' - - # Restore the original AZDODSCCachePath value - $ENV:AZDODSCCachePath = $originalAzDodscCachePath - } - - It 'Creates the cache directory if it does not exist' { - Mock Test-Path { return $false } - Mock New-Item {} - - New-AzDevOpsApiCache -ApiEndpoint 'projects/list' -Parameters @{ organization = 'myOrg' } -Content @{ key1 = 'value1' } - - Assert-MockCalled New-Item -Times 1 -Exactly - } - - It 'Generates cache and metadata files with correct content' { - Mock Out-File {} - - $content = @{ key1 = 'value1'; key2 = 'value2' } - New-AzDevOpsApiCache -ApiEndpoint 'projects/list' -Parameters @{ organization = 'myOrg' } -Content $content - - Assert-MockCalled Out-File -Times 2 -Exactly - } - - It 'Handles the Depth parameter correctly' { - Mock ConvertTo-Json { return '{}' } - - $content = @{ key1 = @{ subKey = 'subValue' } } - New-AzDevOpsApiCache -ApiEndpoint 'projects/list' -Parameters @{ organization = 'myOrg' } -Content $content -Depth 5 - - Assert-MockCalled ConvertTo-Json -ParameterFilter { $Depth -eq 5 } -Times 1 -Exactly - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiResource.Tests.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiResource.Tests.ps1.disabled deleted file mode 100644 index b5c06decb..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzDevOpsApiResource.Tests.ps1.disabled +++ /dev/null @@ -1,9 +0,0 @@ -Describe "New-AzDevOpsApiResource" { - Context "When ApiVersion parameter is provided" { - It "Should set the ApiVersion property" { - $apiVersion = "v1" - $resource = New-AzDevOpsApiResource -ApiVersion $apiVersion - $resource.ApiVersion | Should Be $apiVersion - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzManagedIdentity.Tests.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzManagedIdentity.Tests.ps1.disabled deleted file mode 100644 index 997a27937..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/New-AzManagedIdentity.Tests.ps1.disabled +++ /dev/null @@ -1,53 +0,0 @@ -<# -.SYNOPSIS - Test suite for the New-AzManagedIdentity function. - -.DESCRIPTION - This test suite validates the functionality of the New-AzManagedIdentity function, ensuring it sets global variables correctly and handles token acquisition. -#> - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc.Common' { - - Describe "New-AzManagedIdentity Function Tests" { - - BeforeAll { - - Mock Get-AzManagedIdentityToken { - return @{ - access_token = "mocked_access_token" - expires_on = (Get-Date).AddHours(1).ToUniversalTime().Subtract([datetime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc)).TotalSeconds - expires_in = 3600 - resource = "https://management.azure.com/" - token_type = "Bearer" - } - } - } - - It "Sets the global organization name and managed identity token" { - # Arrange - $orgName = "TestOrganization" - - # Act - New-AzManagedIdentity -OrganizationName $orgName - - # Assert - $Global:DSCAZDO_OrganizationName | Should -Be $orgName - $Global:DSCAZDO_AuthenticationToken | Should -Not -Be $null - $Global:DSCAZDO_AuthenticationToken.access_token | Should -Be "mocked_access_token" - } - - It "Sets the global managed identity token to null if Get-AzManagedIdentityToken fails" { - # Arrange - Mock Get-AzManagedIdentityToken { throw "Failed to get token." } - $orgName = "TestOrganization" - - # Act / Assert - { New-AzManagedIdentity -OrganizationName $orgName } | Should -Throw "Failed to get token." - $Global:DSCAZDO_AuthenticationToken | Should -Be $null - } - } - -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzDevOpsPatCredential.Tests.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzDevOpsPatCredential.Tests.ps1.disabled deleted file mode 100644 index 87b2a93f8..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzDevOpsPatCredential.Tests.ps1.disabled +++ /dev/null @@ -1,107 +0,0 @@ - -# Initialize tests for module function -. $PSScriptRoot\..\..\..\..\AzureDevOpsDsc.Common.Tests.Initialization.ps1 - - -InModuleScope 'AzureDevOpsDsc.Common' { - - $script:dscModuleName = 'AzureDevOpsDsc' - $script:moduleVersion = $(Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1).Version - $script:subModuleName = 'AzureDevOpsDsc.Common' - $script:subModuleBase = $(Get-Module $script:subModuleName).ModuleBase - $script:commandName = $(Get-Item $PSCommandPath).BaseName.Replace('.Tests','') - $script:commandScriptPath = Join-Path "$PSScriptRoot\..\..\..\..\..\..\..\" -ChildPath "output\$($script:dscModuleName)\$($script:moduleVersion)\Modules\$($script:subModuleName)\Connection\Functions\Private\$($script:commandName).ps1" - $script:tag = @($($script:commandName -replace '-')) - - . $script:commandScriptPath - - - Describe "$script:subModuleName\Api\Function\$script:commandName" -Tag $script:tag { - - $testCasesValidPatCredentials = Get-TestCase -ScopeName 'PatCredential' -TestCaseName 'Valid' - $testCasesInvalidPatCredentials = Get-TestCase -ScopeName 'PatCredential' -TestCaseName 'Invalid' - - - Context 'When input parameters are valid' { - - - Context 'When called with "PatCredential" parameter value and the "IsValid" switch' { - - - Context 'When "PatCredential" parameter value is a valid "PatCredential"' { - - It 'Should not throw - ""' -TestCases $testCasesValidPatCredentials { - param ([PSCredential]$PatCredential) - - { Test-AzDevOpsPatCredential -PatCredential $PatCredential -IsValid } | Should -Not -Throw - } - - It 'Should return $true - ""' -TestCases $testCasesValidPatCredentials { - param ([PSCredential]$PatCredential) - - Test-AzDevOpsPatCredential -PatCredential $PatCredential -IsValid | Should -BeTrue - } - } - - - Context 'When "PatCredential" parameter value is an invalid "PatCredential"' { - - It 'Should not throw - ""' -TestCases $testCasesInvalidPatCredentials { - param ([PSCredential]$PatCredential) - - { Test-AzDevOpsPatCredential -PatCredential $PatCredential -IsValid } | Should -Not -Throw - } - - It 'Should return $false - ""' -TestCases $testCasesInvalidPatCredentials { - param ([PSCredential]$PatCredential) - - Test-AzDevOpsPatCredential -PatCredential $PatCredential -IsValid | Should -BeFalse - } - } - } - } - - - Context "When input parameters are invalid" { - - - Context 'When called with no/null/empty parameter values/switches' { - - It 'Should throw' { - - { Test-AzDevOpsPatCredential -PatCredential $([PSCredential]::Empty) -IsValid:$false } | Should -Throw - } - } - - - Context 'When "PatCredential" parameter value is a valid "PatCredential"' { - - - Context 'When called with "PatCredential" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesValidPatCredentials { - param ([PSCredential]$PatCredential) - - { Test-AzDevOpsPatCredential -PatCredential $PatCredential -IsValid:$false } | Should -Throw - } - } - } - - - Context 'When "PatCredential" parameter value is an invalid "PatCredential"' { - - - Context 'When called with "PatCredential" parameter value but a $false "IsValid" switch value' { - - It 'Should throw - ""' -TestCases $testCasesInvalidPatCredentials { - param ([PSCredential]$PatCredential) - - { Test-AzDevOpsPatCredential -PatCredential $PatCredential -IsValid:$false } | Should -Throw - } - } - } - - - } - } -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzManagedIdentityToken.Tests.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzManagedIdentityToken.Tests.ps1.disabled deleted file mode 100644 index 358b2af44..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Test-AzManagedIdentityToken.Tests.ps1.disabled +++ /dev/null @@ -1,59 +0,0 @@ -# Initialize tests for module function - -. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 - -<# -.SYNOPSIS - Test suite for the Test-Token function. - -.DESCRIPTION - This test suite validates the functionality of the Test-Token function, ensuring it properly tests the managed identity token. -#> - -InModuleScope 'AzureDevOpsDsc.Common' { - Describe "Test-Token Function Tests" { - - Mock Invoke-AzDevOpsApiRestMethod { - return @{ - value = @("Project1", "Project2") - } - } - - BeforeAll { - # Define a mock Managed Identity object with a Get method - $mockManagedIdentity = New-Object -TypeName psobject - $mockManagedIdentity | Add-Member -MemberType ScriptMethod -Name Get -Value { return "mocked_access_token" } - - # Set up a global variable as expected by the function - $GLOBAL:DSCAZDO_OrganizationName = "MockOrganization" - } - - It "Returns true when the managed identity token is valid" { - # Arrange - $AzManagedIdentityLocalizedData = @{ - Global_Url_AZDO_Project = "https://dev.azure.com/{0}/_apis/projects" - } - - # Act - $result = Test-Token -ManagedIdentity $mockManagedIdentity - - # Assert - $result | Should -Be $true - } - - It "Returns false when the managed identity token is not valid" { - # Arrange - Mock Invoke-AzDevOpsApiRestMethod { throw "Unauthorized access." } - $AzManagedIdentityLocalizedData = @{ - Global_Url_AZDO_Project = "https://dev.azure.com/{0}/_apis/projects" - } - - # Act - $result = Test-Token -ManagedIdentity $mockManagedIdentity - - # Assert - $result | Should -Be $false - } - } - -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Update-AzManagedIdentity.Tests.ps1.disabled b/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Update-AzManagedIdentity.Tests.ps1.disabled deleted file mode 100644 index f0ce773d2..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Connection/Functions/Private/Archive/Update-AzManagedIdentity.Tests.ps1.disabled +++ /dev/null @@ -1,28 +0,0 @@ -# Initialize tests for module function -. $PSScriptRoot\..\..\..\AzureDevOpsDsc.Common.TestInitialization.ps1 - -InModuleScope 'AzureDevOpsDsc.Common' { - - Describe 'Update-AzManagedIdentity' { - - Context 'When OrganizationName is set' { - - It 'Should update the global variable DSCAZDO_ManagedIdentityToken' { - $organizationName = 'MyOrganization' - $global:DSCAZDO_OrganizationName = $organizationName - - Update-AzManagedIdentity - - $Global:DSCAZDO_AuthenticationToken | Should -Not -Be $null - } - } - - Context 'When OrganizationName is not set' { - - It 'Should throw an error' { - { Update-AzManagedIdentity } | Should -Throw - } - } - } - -} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.tests.ps1 new file mode 100644 index 000000000..b0a1bb017 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/LocalizedData/000.LocalizedDataAzACLTokenPatten.tests.ps1 @@ -0,0 +1,61 @@ +$currentFile = $MyInvocation.MyCommand.Path + +# Define tests +Describe "Testing LocalizedDataAzACLTokenPattern regex patterns" { + + BeforeAll { + $source = Get-FunctionItem '000.LocalizedDataAzACLTokenPatten.ps1' + + . $source.FullName + + } + + It "OrganizationGit should match 'repoV2'" { + 'repoV2' -match $LocalizedDataAzACLTokenPatten.OrganizationGit | Should -BeTrue + } + + It "GitProject should match 'repoV2/Project123'" { + 'repoV2/Project123' -match $LocalizedDataAzACLTokenPatten.GitProject | Should -BeTrue + } + + It "GitRepository should match 'repoV2/Project123/Repo456'" { + 'repoV2/Project123/Repo456' -match $LocalizedDataAzACLTokenPatten.GitRepository | Should -BeTrue + } + + It "GitBranch should match 'repoV2/Project123/Repo456/refs/heads/main'" { + 'repoV2/Project123/Repo456/refs/heads/main' -match $LocalizedDataAzACLTokenPatten.GitBranch | Should -BeTrue + } + + It "GroupPermission should match 'Project123\Group456'" { + 'Project123\Group456' -match $LocalizedDataAzACLTokenPatten.GroupPermission | Should -BeTrue + } + + It "ResourcePermission should match 'Project123'" { + 'Project123' -match $LocalizedDataAzACLTokenPatten.ResourcePermission | Should -BeTrue + } + + # Negative tests + It "OrganizationGit should not match 'repoV3'" { + 'repoV3' -match $LocalizedDataAzACLTokenPatten.OrganizationGit | Should -BeFalse + } + + It "GitProject should not match 'repoV2/'" { + 'repoV2/' -match $LocalizedDataAzACLTokenPatten.GitProject | Should -BeFalse + } + + It "GitRepository should not match 'repoV2/Project123/'" { + 'repoV2/Project123/' -match $LocalizedDataAzACLTokenPatten.GitRepository | Should -BeFalse + } + + It "GitBranch should not match 'repoV2/Project123/Repo456/branches/main'" { + 'repoV2/Project123/Repo456/branches/main' -match $LocalizedDataAzACLTokenPatten.GitBranch | Should -BeFalse + } + + It "GroupPermission should not match 'Project123'" { + 'Project123' -match $LocalizedDataAzACLTokenPatten.GroupPermission | Should -BeFalse + } + + It "ResourcePermission should not match 'Project123\Extra'" { + 'Project123\Extra' -match $LocalizedDataAzACLTokenPatten.ResourcePermission | Should -BeFalse + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Get-AzDoGitPermission.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Get-AzDoGitPermission.tests.ps1 index 5d160a907..5d3ad181c 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Get-xAzDoGitPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Get-AzDoGitPermission.tests.ps1 @@ -16,7 +16,7 @@ Describe 'Get-AzDoGitPermission Tests' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/New-AzDoGitPermission.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/New-AzDoGitPermission.tests.ps1 index f0f46b8cd..1c7d03d99 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/New-xAzDoGitPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/New-AzDoGitPermission.tests.ps1 @@ -16,7 +16,7 @@ Describe 'New-AzDoGitPermission' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Remove-AzDoGitPermission.tests.ps1 similarity index 97% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Remove-AzDoGitPermission.tests.ps1 index 74f18f23e..d68d2821e 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Remove-xAzDoGitPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Remove-AzDoGitPermission.tests.ps1 @@ -17,7 +17,7 @@ Describe "Remove-AzDoGitPermission" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Set-AzDoGitPermission.tests.ps1 similarity index 97% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Set-AzDoGitPermission.tests.ps1 index 95bbf6c82..31660a606 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitPermission/Set-xAzDoGitPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitPermission/Set-AzDoGitPermission.tests.ps1 @@ -17,7 +17,7 @@ Describe 'Set-AzDoGitPermission' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Get-AzDoGitRepository.tests.ps1 similarity index 96% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Get-AzDoGitRepository.tests.ps1 index 84ed70bef..fa315ec0f 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Get-xAzDoGitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Get-AzDoGitRepository.tests.ps1 @@ -16,7 +16,7 @@ Describe "Get-AzDoGitRepository Tests" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/New-AzDoGitRepository.tests.ps1 similarity index 97% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/New-AzDoGitRepository.tests.ps1 index bdc8789fb..c66f92e91 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/New-xAzDoGitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/New-AzDoGitRepository.tests.ps1 @@ -17,7 +17,7 @@ Describe "New-AzDoGitRepository Tests" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Remove-AzDoGitRepository.tests.ps1 similarity index 97% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Remove-AzDoGitRepository.tests.ps1 index 636cced52..ed28b80b6 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Remove-xAzDoGitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Remove-AzDoGitRepository.tests.ps1 @@ -16,7 +16,7 @@ Describe 'Remove-AzDoGitRepository' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Set-AzDoGitRepository.tests.ps1 similarity index 85% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Set-AzDoGitRepository.tests.ps1 index 7a4e2b877..f224fd85b 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGitRepository/Set-xAzDoGitRepository.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGitRepository/Set-AzDoGitRepository.tests.ps1 @@ -14,7 +14,7 @@ Describe 'Set-AzDoGitRepository' -Skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Get-AzDoGroupMember.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Get-AzDoGroupMember.tests.ps1 index d2c7449b9..513c79d44 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Get-xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Get-AzDoGroupMember.tests.ps1 @@ -16,7 +16,7 @@ Describe "Get-AzDoGroupMember Tests" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.tests.ps1 similarity index 97% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.tests.ps1 index d41c9054d..bceae1cde 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/New-xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/New-AzDoGroupMember.tests.ps1 @@ -18,7 +18,7 @@ Describe "New-AzDoGroupMember" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Remove-AzDoGroupMember.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Remove-AzDoGroupMember.tests.ps1 index 94733b468..e2968d10d 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Remove-xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Remove-AzDoGroupMember.tests.ps1 @@ -18,7 +18,7 @@ Describe 'Remove-AzDoGroupMember Tests' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Set-AzDoGroupMember.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Set-AzDoGroupMember.tests.ps1 index 6d3c4aef4..e025df1d0 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Set-xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Set-AzDoGroupMember.tests.ps1 @@ -18,7 +18,7 @@ Describe 'Set-AzDoGroupMember' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Test-AzDoGroupMember.tests.ps1 similarity index 96% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Test-AzDoGroupMember.tests.ps1 index c02e5dee8..40609976c 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupMember/Test-xAzDoGroupMember.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupMember/Test-AzDoGroupMember.tests.ps1 @@ -18,7 +18,7 @@ Describe 'Test-AzDoGroupMember' -skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Get-AzDoGroupPermission.tests.ps1 similarity index 97% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Get-AzDoGroupPermission.tests.ps1 index 8b041c844..a92953e0f 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Get-xAzDoGroupPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Get-AzDoGroupPermission.tests.ps1 @@ -18,7 +18,7 @@ Describe 'Get-AzDoGroupPermission' -skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/New-AzDoGroupPermission.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/New-AzDoGroupPermission.tests.ps1 index 140a162b8..1615509d0 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/New-xAzDoGroupPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/New-AzDoGroupPermission.tests.ps1 @@ -17,7 +17,7 @@ Describe 'New-AzDoGroupPermission' -skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Remove-AzDoGroupPermission.tests.ps1 similarity index 97% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Remove-AzDoGroupPermission.tests.ps1 index 44ed26f54..cd207a8d6 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Remove-xAzDoGroupPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Remove-AzDoGroupPermission.tests.ps1 @@ -17,7 +17,7 @@ Describe 'Remove-AzDoGroupPermission' -skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Set-AzDoGroupPermission.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Set-AzDoGroupPermission.tests.ps1 index f0fc5316d..2c29b98ce 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoGroupPermission/Set-xAzDoGroupPermission.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoGroupPermission/Set-AzDoGroupPermission.tests.ps1 @@ -17,7 +17,7 @@ Describe 'Set-AzDoGroupPermission' -skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Get-AzDoOrganizationGroup.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Get-AzDoOrganizationGroup.tests.ps1 index 6215cb1a0..a6c94aee8 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Get-xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Get-AzDoOrganizationGroup.tests.ps1 @@ -16,7 +16,7 @@ Describe 'Get-AzDoOrganizationGroup' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/New-AzDoOrganizationGroup.tests.ps1 similarity index 96% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/New-AzDoOrganizationGroup.tests.ps1 index bdb4c46c3..41aaa3d0f 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/New-xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/New-AzDoOrganizationGroup.tests.ps1 @@ -16,7 +16,7 @@ Describe 'New-AzDoOrganizationGroup' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Remove-AzDoOrganizationGroup.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Remove-AzDoOrganizationGroup.tests.ps1 index 947eabc1f..48d5cd741 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Remove-xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Remove-AzDoOrganizationGroup.tests.ps1 @@ -16,7 +16,7 @@ Describe 'Remove-AzDoOrganizationGroup' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Set-AzDoOrganizationGroup.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Set-AzDoOrganizationGroup.tests.ps1 index 35ab3a1da..c2a16d68c 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Set-xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Set-AzDoOrganizationGroup.tests.ps1 @@ -14,7 +14,7 @@ Describe 'Set-AzDoOrganizationGroup' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Test-AzDoOrganizationGroup.tests.ps1 similarity index 96% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Test-AzDoOrganizationGroup.tests.ps1 index 279a4c38e..18c597264 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoOrganizationGroup/Test-xAzDoOrganizationGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Test-AzDoOrganizationGroup.tests.ps1 @@ -11,7 +11,7 @@ Describe "Test-AzDoOrganizationGroup" -skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.tests.ps1 similarity index 96% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.tests.ps1 index 959342529..b344b71e0 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Get-xAzDoProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Get-AzDoProject.tests.ps1 @@ -18,7 +18,7 @@ Describe "Get-AzDoProject" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName @@ -55,7 +55,7 @@ Describe "Get-AzDoProject" { It "should return the project details with status unchanged" { $result = Get-AzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' - $result.Status | Should -BeNullOrEmpty + $result.Status | Should -Be 'Unchanged' $result.ProjectName | Should -Be 'ExistingProject' $result.ProjectDescription | Should -Be 'ExistingDescription' } @@ -109,7 +109,7 @@ Describe "Get-AzDoProject" { It "should warn about source control type conflict" { $result = Get-AzDoProject -ProjectName 'ExistingProject' -ProjectDescription 'ExistingDescription' -SourceControlType 'Git' -ProcessTemplate 'Agile' -Visibility 'Private' - $result.Status | Should -BeNullOrEmpty + $result.Status | Should -Be 'UnChanged' $result.ProjectName | Should -Be 'ExistingProject' $result.SourceControlType | Should -Be 'Git' } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/New-AzDoProject.tests.ps1 similarity index 88% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/New-AzDoProject.tests.ps1 index 505868649..202f0670c 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/New-xAzDoProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/New-AzDoProject.tests.ps1 @@ -19,7 +19,7 @@ Describe "New-AzDoProject" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName @@ -41,6 +41,7 @@ Describe "New-AzDoProject" { } Mock -CommandName Test-AzDevOpsProjectName -MockWith { return $true } + Mock -CommandName Refresh-AzDoCache } @@ -55,8 +56,8 @@ Describe "New-AzDoProject" { # Mock Wait-DevOpsProject to simulate waiting for project creation Mock -CommandName Wait-DevOpsProject - # Mock AzDoAPI_0_ProjectCache to simulate cache refresh - Mock -CommandName AzDoAPI_0_ProjectCache + # Mock Refresh-AzDoCache to simulate cache refresh + Mock -CommandName Refresh-AzDoCache } @@ -82,8 +83,8 @@ Describe "New-AzDoProject" { $OrganizationName -eq 'TestOrganization' } - # Validate that AzDoAPI_0_ProjectCache was called with correct parameters - Assert-MockCalled -CommandName AzDoAPI_0_ProjectCache -Exactly 1 -ParameterFilter { + # Validate that Refresh-AzDoCache was called with correct parameters + Assert-MockCalled -CommandName Refresh-AzDoCache -Exactly 1 -ParameterFilter { $OrganizationName -eq 'TestOrganization' } } @@ -111,8 +112,8 @@ Describe "New-AzDoProject" { # Mock Wait-DevOpsProject to simulate waiting for project creation Mock -CommandName Wait-DevOpsProject - # Mock AzDoAPI_0_ProjectCache to simulate cache refresh - Mock -CommandName AzDoAPI_0_ProjectCache + # Mock Refresh-AzDoCache to simulate cache refresh + Mock -CommandName Refresh-AzDoCache } It "should create a new project even if it already exists when -Force is used" { @@ -134,8 +135,8 @@ Describe "New-AzDoProject" { $OrganizationName -eq 'TestOrg' } - # Validate that AzDoAPI_0_ProjectCache was called with correct parameters - Assert-MockCalled -CommandName AzDoAPI_0_ProjectCache -Exactly 1 -ParameterFilter { + # Validate that Refresh-AzDoCache was called with correct parameters + Assert-MockCalled -CommandName Refresh-AzDoCache -Exactly 1 -ParameterFilter { $OrganizationName -eq 'TestOrg' } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Remove-AzDoProject.tests.ps1 similarity index 97% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Remove-AzDoProject.tests.ps1 index 6a597bf45..ca73fda14 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Remove-xAzDoProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Remove-AzDoProject.tests.ps1 @@ -17,7 +17,7 @@ Describe "Remove-AzDoProject" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Set-AzDoProject.tests.ps1 similarity index 92% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Set-AzDoProject.tests.ps1 index 6d3ba9e0d..bed82e44a 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Set-xAzDoProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Set-AzDoProject.tests.ps1 @@ -17,7 +17,7 @@ Describe "Set-AzDoProject" { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName @@ -49,7 +49,7 @@ Describe "Set-AzDoProject" { } Mock -CommandName Wait-DevOpsProject - Mock -CommandName AzDoAPI_0_ProjectCache + Mock -CommandName Refresh-AzDoCache } @@ -86,7 +86,7 @@ Describe "Set-AzDoProject" { ($ProjectURL -eq "http://devopsprojecturl") -and ($OrganizationName -eq "TestOrg") } - Assert-MockCalled -CommandName AzDoAPI_0_ProjectCache -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName Refresh-AzDoCache -Exactly -Times 1 -ParameterFilter { $OrganizationName -eq "TestOrg" } } diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.tests.ps1 similarity index 96% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.tests.ps1 index 02cd1acbd..fc7b98aaa 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProject/Test-xAzDoProject.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProject/Test-AzDoProject.tests.ps1 @@ -13,7 +13,7 @@ Describe "Test-AzDevOpsProject" -Skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Get-AzDoProjectGroup.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Get-AzDoProjectGroup.tests.ps1 index 28c85070a..5d221bc9d 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Get-xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Get-AzDoProjectGroup.tests.ps1 @@ -17,7 +17,7 @@ Describe 'Get-AzDoProjectGroup' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/New-AzDoProjectGroup.tests.ps1 similarity index 97% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/New-AzDoProjectGroup.tests.ps1 index 021dca614..3f5f81f61 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/New-xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/New-AzDoProjectGroup.tests.ps1 @@ -15,7 +15,7 @@ Describe 'New-AzDoProjectGroup' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Remove-AzDoProjectGroup.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Remove-AzDoProjectGroup.tests.ps1 index f70c6e52d..4b54e4836 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Remove-xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Remove-AzDoProjectGroup.tests.ps1 @@ -18,7 +18,7 @@ Describe 'Remove-AzDoProjectGroup' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Set-AzDoProjectGroup.tests.ps1 similarity index 98% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Set-AzDoProjectGroup.tests.ps1 index 843a78a70..043693b01 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Set-xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Set-AzDoProjectGroup.tests.ps1 @@ -21,7 +21,7 @@ Describe 'Set-AzDoProjectGroup' { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Test-AzDoProjectGroup.tests.ps1 similarity index 97% rename from tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 rename to tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Test-AzDoProjectGroup.tests.ps1 index f9c2f5860..84c8f2d79 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/xAzDoProjectGroup/Test-xAzDoProjectGroup.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoProjectGroup/Test-AzDoProjectGroup.tests.ps1 @@ -19,7 +19,7 @@ Describe 'Test-AzDoProjectGroup' -skip { } # Load the functions to test - $files = Invoke-BeforeEachFunctions (Find-Functions -TestFilePath $currentFile) + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) ForEach ($file in $files) { . $file.FullName diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.tests.ps1 new file mode 100644 index 000000000..70a7a4127 --- /dev/null +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/New-AzDoAuthenticationProvider.tests.ps1 @@ -0,0 +1,130 @@ +$currentFile = $MyInvocation.MyCommand.Path + +Describe "New-AzDoAuthenticationProvider" { + + AfterAll { + Remove-Variable -Name DSCAZDO_OrganizationName -Scope Global + } + + BeforeAll { + + # Set the Organization Name + $Global:DSCAZDO_OrganizationName = 'TestOrganization' + + # Load the functions to test + if ($null -eq $currentFile) { + $currentFile = Join-Path -Path $PSScriptRoot -ChildPath 'New-AzDoAuthenticationProvider.tests.ps1' + } + + # Load the functions to test + $files = Get-FunctionItem (Find-MockedFunctions -TestFilePath $currentFile) + + ForEach ($file in $files) { + . $file.FullName + } + + # Mocking dependencies + Mock -CommandName Set-AzPersonalAccessToken -MockWith { return "mockedToken" } + Mock -CommandName Get-AzManagedIdentityToken -MockWith { return "mockedManagedIdentityToken" } + Mock -CommandName Get-AzDoCacheObjects -MockWith { return @() } + Mock -CommandName Get-Command + Mock -CommandName Initialize-CacheObject + Mock -CommandName Export-Clixml + + } + + BeforeEach { + $ENV:AZDODSC_CACHE_DIRECTORY = "C:\MockCacheDirectory" + $Global:DSCAZDO_AuthenticationToken = $null + } + + AfterEach { + $ENV:AZDODSC_CACHE_DIRECTORY = $null + $Global:DSCAZDO_AuthenticationToken = $null + } + + Context "When AZDODSC_CACHE_DIRECTORY is not set" { + + It "Should throw an error" { + # Arrange + $ENV:AZDODSC_CACHE_DIRECTORY = $null + + # Act & Assert + { + New-AzDoAuthenticationProvider -OrganizationName "Contoso" -PersonalAccessToken "dummyPat" + } | Should -Throw "*The Environment Variable 'AZDODSC_CACHE_DIRECTORY' is not set*" + } + } + + Context "Using PersonalAccessToken parameter set" { + It "Should set the global authentication token without verification" { + # Act + New-AzDoAuthenticationProvider -OrganizationName "Contoso" -PersonalAccessToken "dummyPat" -NoVerify + + # Assert + $Global:DSCAZDO_AuthenticationToken | Should -Be "mockedToken" + Assert-MockCalled -CommandName Set-AzPersonalAccessToken -Exactly 1 + } + + It "Should set the global authentication token with verification" { + # Act + New-AzDoAuthenticationProvider -OrganizationName "Contoso" -PersonalAccessToken "dummyPat" + + # Assert + $Global:DSCAZDO_AuthenticationToken | Should -Be "mockedToken" + Assert-MockCalled -CommandName Set-AzPersonalAccessToken -Exactly 1 -ParameterFilter { $Verify } + } + } + + Context "Using ManagedIdentity parameter set" { + It "Should set the global authentication token without verification" { + # Act + New-AzDoAuthenticationProvider -OrganizationName "Contoso" -useManagedIdentity -NoVerify + + # Assert + $Global:DSCAZDO_AuthenticationToken | Should -Be "mockedManagedIdentityToken" + Assert-MockCalled -CommandName Get-AzManagedIdentityToken -Exactly 1 + } + + It "Should set the global authentication token with verification" { + # Act + New-AzDoAuthenticationProvider -OrganizationName "Contoso" -useManagedIdentity + + # Assert + $Global:DSCAZDO_AuthenticationToken | Should -Be "mockedManagedIdentityToken" + Assert-MockCalled -CommandName Get-AzManagedIdentityToken -Exactly 1 -ParameterFilter { $Verify } + } + } + + Context "Using SecureStringPersonalAccessToken parameter set" { + It "Should set the global authentication token" { + # Arrange + $secureStringPAT = ConvertTo-SecureString "dummySecurePat" -AsPlainText -Force + + # Act + New-AzDoAuthenticationProvider -OrganizationName "Contoso" -SecureStringPersonalAccessToken $secureStringPAT + + # Assert + $Global:DSCAZDO_AuthenticationToken | Should -Be "mockedToken" + Assert-MockCalled -CommandName Set-AzPersonalAccessToken -Exactly 1 + } + } + + Context "Token export functionality" { + It "Should export token information when isResource is not set" { + # Act + New-AzDoAuthenticationProvider -OrganizationName "Contoso" -PersonalAccessToken "dummyPat" + + # Assert + Assert-MockCalled -CommandName Export-Clixml -Exactly 1 + } + + It "Should not export token information when isResource is set" { + # Act + New-AzDoAuthenticationProvider -OrganizationName "Contoso" -PersonalAccessToken "dummyPat" -isResource + + # Assert + Assert-MockCalled -CommandName Export-Clixml -Exactly 0 + } + } +} diff --git a/tests/Unit/Modules/AzureDevOpsDsc/AzureDevOpsDsc.DscClassResources.Tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc/AzureDevOpsDsc.DscClassResources.Tests.ps1 deleted file mode 100644 index 3a298f6de..000000000 --- a/tests/Unit/Modules/AzureDevOpsDsc/AzureDevOpsDsc.DscClassResources.Tests.ps1 +++ /dev/null @@ -1,30 +0,0 @@ - -# # Initialize tests -# . $PSScriptRoot\AzureDevOpsDsc.TestInitialization.ps1 - - -# InModuleScope 'AzureDevOpsDsc' { - -# Describe 'DSCClassResources\AzDevOpsApiDscResourceBase' -Tag 'AzDevOpsApiDscResourceBase' { - -# $dscModuleName = 'AzureDevOpsDsc' -# $testCasesValidResourceNames = Get-TestCase -ScopeName 'ResourceName' -TestCaseName 'Valid' -# $testCasesValidResourceNamesForDscResources = $testCasesValidResourceNames | Where-Object { $_.ResourceName -notin @('Operation')} - -# Context "When evaluating '$dscModuleName' module" { -# BeforeAll { -# $dscModuleName = 'AzureDevOpsDsc' -# $dscResourcePrefix = 'AzDevOps' -# [string[]]$exportedDscResources = (Get-Module $dscModuleName).ExportedDscResources -# } - -# It "Should contain an exported, DSCResource specific to the 'ResourceName' - ''" -TestCases $testCasesValidResourceNamesForDscResources { -# param ([string]$ResourceName) - -# "$dscResourcePrefix$ResourceName" | Should -BeIn $exportedDscResources -# } - -# } - -# } -# } diff --git a/tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1 b/tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1 index 0183414da..206eca773 100644 --- a/tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1 +++ b/tests/Unit/Modules/TestHelpers/CommonTestFunctions.psm1 @@ -14,7 +14,7 @@ Function Split-RecurivePath { $Path } -Function Invoke-BeforeEachFunctions { +Function Get-FunctionItem { param( [string[]]$FileNames ) @@ -44,7 +44,7 @@ Function Invoke-BeforeEachFunctions { } -Function Find-Functions { +Function Find-MockedFunctions { param( [String]$TestFilePath ) @@ -106,4 +106,4 @@ Function Import-Enums { return ($Global:TestPaths | Where-Object { $_.Directory.Name -eq 'Enum' }) } -Export-ModuleMember -Function Split-RecurivePath, Invoke-BeforeEachFunctions, Find-Functions, Get-ClassFilePath, Import-Enums +Export-ModuleMember -Function Split-RecurivePath, Get-FunctionItem, Find-MockedFunctions, Get-ClassFilePath, Import-Enums From 5f3d038d6e814048be211f18c8b826a428099725 Mon Sep 17 00:00:00 2001 From: Michael Zanatta Date: Mon, 25 Nov 2024 08:22:20 +1000 Subject: [PATCH 6/7] DeCoupling Logging File Path to Enviroment Variable (#6) Co-authored-by: Michael Zanatta --- USAGE.md | 26 ++++++++++++++++--- .../Logging/Proxy Functions/Write-Error.ps1 | 12 ++++++--- .../Logging/Proxy Functions/Write-Verbose.ps1 | 2 +- .../Logging/Proxy Functions/Write-Warning.ps1 | 12 ++++++--- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/USAGE.md b/USAGE.md index 4774a83de..6b35a35d6 100644 --- a/USAGE.md +++ b/USAGE.md @@ -26,10 +26,30 @@ Ensure you have the following prerequisites before proceeding: - `Sampler` - `xDSCResourceDesigner` -### Setting Up: *AZDODSC_CACHE_DIRECTORY* Environment Variable +### *AZDODSC_CACHE_DIRECTORY* Environment Variable -The system environment variable `AZDODSC_CACHE_DIRECTORY` is used by the module to store caching settings and the cache itself. -Make sure this variable is properly set up in your system environment. +The system environment variable `AZDODSC_CACHE_DIRECTORY` is used by the module +to store caching settings and the cache itself. Make sure this variable is properly +set up in your system environment. + +### *AZDO_WARNINGLOGGING_FILEPATH* and *AZDO_ERRORLOGGING_FILEPATH* Environment Variables + +The `AZDO_WARNINGLOGGING_FILEPATH` and `AZDO_ERRORLOGGING_FILEPATH`environment +variables are typically used in Azure DevOps (AzDO) pipelines or custom scripts +to specify file paths where warning and error logs should be stored. +These variables help manage logging by directing different types of log messages +to separate files, aiding in better organization and analysis. + +#### Usage + +- **AZDO_WARNINGLOGGING_FILEPATH**: This variable defines the path to a file +where all warnings generated during the execution of a pipeline or script will +be logged. It's useful for tracking non-critical issues that may need attention +but do not halt the execution. + +- **AZDO_ERRORLOGGING_FILEPATH**: This variable specifies the path to a file +designated for logging errors. Errors are typically more severe than warnings +and might require immediate action or investigation. ## Setting Up Managed Identity diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 index e80f9072c..9f2331e4b 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 @@ -33,15 +33,19 @@ Function Write-Error [string]$Message, [Parameter()] - [string]$LogFilePath = "C:\Temp\error_log.txt" + [string]$LogFilePath = "$($env:AZDO_ERRORLOGGING_FILEPATH)" ) # Call the original Write-Error cmdlet to display the message Microsoft.PowerShell.Utility\Write-Error $Message $VerbosePreference = $originalPreference - # Append the message to the log file - $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" - Add-Content -Path $LogFilePath -Value "[$timestamp] $Message" + # Test if the env:enableVerboseLogging variable is set to true + if ($null -ne $LogFilePath) + { + # Append the message to the log file + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Add-Content -Path $LogFilePath -Value "[$timestamp] $Message" + } } diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 index 265740b52..a9eea4170 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 @@ -35,7 +35,7 @@ Function Write-Verbose Microsoft.PowerShell.Utility\Write-Verbose $Message # Test if the env:enableVerboseLogging variable is set to true - if ($null -ne $env:AZDO_VERBOSELOGGING_FILEPATH) + if ($null -ne $LogFilePath) { # Append the message to the log file $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 index 9d53b5e21..3a285c46e 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 @@ -32,7 +32,7 @@ Function Write-Warning [string]$Message, [Parameter()] - [string]$LogFilePath = "C:\Temp\warning_log.txt" + [string]$LogFilePath = "$($env:AZDO_WARNINGLOGGING_FILEPATH)" ) # Call the original Write-Verbose cmdlet to display the message if verbose preference is enabled @@ -41,7 +41,11 @@ Function Write-Warning Microsoft.PowerShell.Utility\Write-Warning $Message $VerbosePreference = $originalPreference - # Append the message to the log file - $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" - Add-Content -Path $LogFilePath -Value "[$timestamp] $Message" + if ($null -ne $LogFilePath) + { + # Append the message to the log file + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Add-Content -Path $LogFilePath -Value "[$timestamp] $Message" + } + } From c4e2c1d969afadf4355657d2d2704a363a6c7155 Mon Sep 17 00:00:00 2001 From: Michael Zanatta Date: Wed, 8 Jan 2025 12:32:15 +1000 Subject: [PATCH 7/7] Code Review Updates (#8) * Code Review Fixes (#7) * Initial Commit * Code Review Adjustments --------- Co-authored-by: Michael Zanatta * Replace comment with amended version * Adding Install-Module and Install-PSResource commands. * Bug Fixes * Big Fixes with Unit Tests --------- Co-authored-by: Michael Zanatta --- .build/tasks/1.PreLoad.ps1 | 30 +++++++++++ USAGE.md | 50 +++++++++++++++++++ build.yaml | 2 + source/Classes/002.PersonalAccessToken.ps1 | 5 +- source/Classes/009.AzDoGroupPermission.ps1 | 4 +- source/Classes/011.AzDoOrganizationGroup.ps1 | 2 +- source/Classes/020.AzDoProject.ps1 | 4 +- source/Classes/021.AzDoProjectServices.ps1 | 2 +- source/Classes/022.AzDoProjectGroup.ps1 | 2 +- source/Classes/031.AzDoGroupMember.ps1 | 4 +- source/Classes/040.AzDoGitRepository.ps1 | 4 +- source/Classes/041.AzDoGitPermission.ps1 | 2 +- source/Enum/DescriptorType.ps1 | 1 - .../Examples/Resources/AzDoGitPermission.md | 12 ++--- .../2-UpdateAzDoGitRepository.ps1 | 2 +- .../Examples/Resources/AzDoGroupPermission.md | 2 +- .../Resources/AzDoOrganizationGroup.md | 2 - .../Api/Classes/000.CacheItem.ps1 | 2 +- .../Api/Project/Update-DevOpsProject.ps1 | 8 --- .../Api/Project/Wait-DevOpsProject.ps1 | 4 +- .../Cache Initalization/0.ProjectCache.ps1 | 4 +- .../Private/Cache/Find-CacheItem.ps1 | 4 +- .../Private/Cache/Get-CacheObject.ps1 | 2 +- .../Private/Cache/Set-CacheObject.ps1 | 4 +- .../Private/Helper/ACL/ConvertTo-ACEList.ps1 | 4 +- .../Private/Helper/ACL/ConvertTo-ACL.ps1 | 4 +- .../Helper/ACL/ConvertTo-FormattedACL.ps1 | 2 +- .../Helper/ACL/Get-BitwiseOrResult.ps1 | 4 +- .../Private/Helper/ConvertTo-Base64String.ps1 | 2 +- .../Private/Helper/Find-Identity.ps1 | 4 +- .../Helper/Invoke-AzDevOpsApiRestMethod.ps1 | 2 +- .../Logging/{Flush-Log.txt => Flush-Log.ps1} | 2 + ...{Initialize-Log.txt => Initialize-Log.ps1} | 2 + .../Logging/Proxy Functions/Write-Error.ps1 | 2 +- .../Logging/Proxy Functions/Write-Verbose.ps1 | 2 +- .../Logging/Proxy Functions/Write-Warning.ps1 | 2 +- .../Logging/{Write-Log.txt => Write-Log.ps1} | 2 + .../Private/Helper/PreCommandLookupAction.ps1 | 30 ----------- .../AzureDevOpsDsc.Common.psm1 | 3 +- .../Get-AzDoOrganizationGroup.ps1 | 9 +--- .../Test-AzDoOrganizationGroup.ps1 | 2 +- .../Functions/Public/Refresh-Cache.ps1 | 1 - tests/DSC/InModuleScope.ps1 | 37 -------------- .../Resources/AzDoGitPermission.tests.ps1 | 2 - .../Supporting/API/Get-AzDevOpsApiVersion.ps1 | 1 - .../Supporting/API/Invoke-APIRestMethod.ps1 | 1 - .../Supporting/APICalls/List-DevOpsGroups.ps1 | 3 -- .../APICalls/Remove-DevOpsProject.ps1 | 5 +- .../Functions/SupportingFunctions.ps1 | 4 -- tests/Integration/Supporting/Teardown.ps1 | 1 - .../Private/Helper/Find-Identity.tests.ps1 | 4 +- 51 files changed, 142 insertions(+), 153 deletions(-) create mode 100644 .build/tasks/1.PreLoad.ps1 rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/{Flush-Log.txt => Flush-Log.ps1} (99%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/{Initialize-Log.txt => Initialize-Log.ps1} (99%) rename source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/{Write-Log.txt => Write-Log.ps1} (99%) delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.ps1 delete mode 100644 source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 delete mode 100644 tests/DSC/InModuleScope.ps1 diff --git a/.build/tasks/1.PreLoad.ps1 b/.build/tasks/1.PreLoad.ps1 new file mode 100644 index 000000000..dc2ae836a --- /dev/null +++ b/.build/tasks/1.PreLoad.ps1 @@ -0,0 +1,30 @@ +task PreLoad { + + # Write a script to check the PSModule Path and add the output/ + # folder to the PSModule Path + + # Get the output directory + $RepositoryRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $outputDir = Join-Path -Path $RepositoryRoot -ChildPath 'output' + $supportingModules = Join-Path -Path $RepositoryRoot -ChildPath 'output/AzureDevOpsDsc/0.0.1/Modules' + + # Test if the output and supporting modules directories exist in the PSModulePath + if (-not $IsWindows) { + $modulelist = ($env:PSModulePath -split ":") + $delimiter = ":" + } else { + $modulelist = ($env:PSModulePath -split ";") + $delimiter = ";" + } + + # Check if the output directory is in the moduleList + if ($moduleList -notcontains $outputDir) { + $env:PSModulePath = "{0}{1}{2}" -f $env:PSModulePath, $delimiter, $outputDir + Write-Host "Adding $outputDir to PSModulePath" + } + if ($moduleList -notcontains $supportingModules) { + $env:PSModulePath = "{0}{1}{2}" -f $env:PSModulePath, $delimiter, $supportingModules + Write-Host "Adding $supportingModules to PSModulePath" + } + +} diff --git a/USAGE.md b/USAGE.md index 6b35a35d6..4fa772c7b 100644 --- a/USAGE.md +++ b/USAGE.md @@ -26,6 +26,56 @@ Ensure you have the following prerequisites before proceeding: - `Sampler` - `xDSCResourceDesigner` +__Using Install-Module__ + +``` PowerShell +# Run as Administrator +Install-Module -Scope AllUsers -Name @( + 'ChangelogManagement' + 'Configuration' + 'DscResource.AnalyzerRules' + 'DscResource.Common' + 'DscResource.DocGenerator' + 'DscResource.Test' + 'InvokeBuild' + 'MarkdownLinkCheck' + 'Metadata' + 'ModuleBuilder' + 'Pester' + 'Plaster' + 'PSDepend' + 'PSDscResources' + 'PSScriptAnalyzer' + 'Sampler' + 'xDSCResourceDesigner' +) +``` + +__Using Install-PSResource__ + +``` PowerShell +# Run as Administrator +Install-PSResource @( + 'ChangelogManagement' + 'Configuration' + 'DscResource.AnalyzerRules' + 'DscResource.Common' + 'DscResource.DocGenerator' + 'DscResource.Test' + 'InvokeBuild' + 'MarkdownLinkCheck' + 'Metadata' + 'ModuleBuilder' + 'Pester' + 'Plaster' + 'PSDepend' + 'PSDscResources' + 'PSScriptAnalyzer' + 'Sampler' + 'xDSCResourceDesigner' +) +``` + ### *AZDODSC_CACHE_DIRECTORY* Environment Variable The system environment variable `AZDODSC_CACHE_DIRECTORY` is used by the module diff --git a/build.yaml b/build.yaml index d725e206f..18816b55e 100644 --- a/build.yaml +++ b/build.yaml @@ -31,6 +31,7 @@ BuildWorkflow: - test build: + - PreLoad - Clean - Build_Module_ModuleBuilder - Build_NestedModules_ModuleBuilder @@ -46,6 +47,7 @@ BuildWorkflow: - DscResource_Tests_Stop_On_Fail test: + - PreLoad - Pester_Tests_Stop_On_Fail - Pester_if_Code_Coverage_Under_Threshold diff --git a/source/Classes/002.PersonalAccessToken.ps1 b/source/Classes/002.PersonalAccessToken.ps1 index da231279e..85bd85c5c 100644 --- a/source/Classes/002.PersonalAccessToken.ps1 +++ b/source/Classes/002.PersonalAccessToken.ps1 @@ -38,7 +38,10 @@ class PersonalAccessToken : AuthenticationToken } [Bool]isExpired() { - # Personal Access Tokens do not expire. + + # Personal Access Tokens don't contain expiry information. Without performing a global lookup of PAT tokens, + # we can't determine if a PAT is expired Therefore, we always return $false. + return $false } diff --git a/source/Classes/009.AzDoGroupPermission.ps1 b/source/Classes/009.AzDoGroupPermission.ps1 index a3b2f6a75..6693563a3 100644 --- a/source/Classes/009.AzDoGroupPermission.ps1 +++ b/source/Classes/009.AzDoGroupPermission.ps1 @@ -6,8 +6,8 @@ The AzDoGroupPermission class is a DSC resource that allows you to manage permissions for a group in an Azure DevOps project. .NOTES - Author: Your Name - Date: Current Date + Author: Michael Zanatta + Date: 2025-01-06 .LINK GitHub Repository: diff --git a/source/Classes/011.AzDoOrganizationGroup.ps1 b/source/Classes/011.AzDoOrganizationGroup.ps1 index 1f404c22c..f8f4e3018 100644 --- a/source/Classes/011.AzDoOrganizationGroup.ps1 +++ b/source/Classes/011.AzDoOrganizationGroup.ps1 @@ -8,7 +8,7 @@ .NOTES Author: Michael Zanatta - Date: 04/19/2024 + Date: 2025-01-06 .LINK GitHub Repository: diff --git a/source/Classes/020.AzDoProject.ps1 b/source/Classes/020.AzDoProject.ps1 index d0bc95d22..0e10dc775 100644 --- a/source/Classes/020.AzDoProject.ps1 +++ b/source/Classes/020.AzDoProject.ps1 @@ -6,8 +6,8 @@ The AzDoProject class is used to define and manage Azure DevOps projects. It inherits from the AzDevOpsDscResourceBase class. .NOTES - Author: Your Name - Date: Current Date + Author: Michael Zanatta + Date: 2025-01-06 .LINK GitHub Repository: diff --git a/source/Classes/021.AzDoProjectServices.ps1 b/source/Classes/021.AzDoProjectServices.ps1 index 805bad0e9..7998e87e9 100644 --- a/source/Classes/021.AzDoProjectServices.ps1 +++ b/source/Classes/021.AzDoProjectServices.ps1 @@ -44,7 +44,7 @@ .NOTES Version: 1.0 - Author: Your Name + Author: Michael Zanatta Required Modules: xAzDevOpsDSC #> diff --git a/source/Classes/022.AzDoProjectGroup.ps1 b/source/Classes/022.AzDoProjectGroup.ps1 index 3927ca579..9fb02a07a 100644 --- a/source/Classes/022.AzDoProjectGroup.ps1 +++ b/source/Classes/022.AzDoProjectGroup.ps1 @@ -7,7 +7,7 @@ .NOTES Author: Michael Zanatta - Date: 04/19/2024 + Date: 2025-01-06 .LINK GitHub Repository: diff --git a/source/Classes/031.AzDoGroupMember.ps1 b/source/Classes/031.AzDoGroupMember.ps1 index b4efc3a37..69c115d41 100644 --- a/source/Classes/031.AzDoGroupMember.ps1 +++ b/source/Classes/031.AzDoGroupMember.ps1 @@ -7,8 +7,8 @@ It inherits from the AzDevOpsDscResourceBase class. .NOTES - Author: Your Name - Date: Current Date + Author: Michael Zanatta + Date: 2025-01-06 .LINK GitHub Repository: diff --git a/source/Classes/040.AzDoGitRepository.ps1 b/source/Classes/040.AzDoGitRepository.ps1 index 80d5f3470..dcd627879 100644 --- a/source/Classes/040.AzDoGitRepository.ps1 +++ b/source/Classes/040.AzDoGitRepository.ps1 @@ -7,8 +7,8 @@ It inherits from the AzDevOpsDscResourceBase class. .NOTES - Author: Your Name - Date: Current Date + Author: Michael Zanatta + Date: 2025-01-06 .LINK GitHub Repository: diff --git a/source/Classes/041.AzDoGitPermission.ps1 b/source/Classes/041.AzDoGitPermission.ps1 index 65a8e51a2..c34f1169e 100644 --- a/source/Classes/041.AzDoGitPermission.ps1 +++ b/source/Classes/041.AzDoGitPermission.ps1 @@ -21,7 +21,7 @@ This class is part of the AzureDevOpsDSC module. .LINK - https://github.com/Azure/AzureDevOpsDSC + https://github.com/dsccommunity/AzureDevOpsDsc .EXAMPLE This example shows how to use the AzDoGitPermission class to manage Git permissions in Azure DevOps. diff --git a/source/Enum/DescriptorType.ps1 b/source/Enum/DescriptorType.ps1 index 608b6027d..fda06bd38 100644 --- a/source/Enum/DescriptorType.ps1 +++ b/source/Enum/DescriptorType.ps1 @@ -130,5 +130,4 @@ Enum DescriptorType { DashboardsPrivileges CSS VersionControlItems - } diff --git a/source/Examples/Resources/AzDoGitPermission.md b/source/Examples/Resources/AzDoGitPermission.md index a87b621de..3cb89a0ea 100644 --- a/source/Examples/Resources/AzDoGitPermission.md +++ b/source/Examples/Resources/AzDoGitPermission.md @@ -37,7 +37,7 @@ AzDoGitPermission/Permissions/Permission ## Permission List -> Either 'Name' or 'DisplayName' can be used +> Either 'Name' or 'DisplayName' can be used, but we Strongly Recommend that you use 'Name' in your configuration. | Name | DisplayName | Values | Note | | ------------- | ------------- | - | - | @@ -92,8 +92,8 @@ Configuration ExampleConfig { Identity = '[ProjectName]\GroupName' Permissions = @{ Read = 'Allow' - "Manage Notes" = 'Allow' - "Contribute" = 'Deny' + ManageNote = 'Allow' + Contribute = 'Deny' } } ) @@ -120,8 +120,8 @@ $properties = @{ Identity = '[ProjectName]\GroupName' Permissions = @{ Read = 'Allow' - "Manage Notes" = 'Allow' - "Contribute" = 'Deny' + ManageNote = 'Allow' + Contribute = 'Deny' } } ) @@ -173,7 +173,7 @@ resources: - Identity: '[$ProjectName]\SampleGroupReadAccess' Permission: Read: "Allow" - "Manage notes": "Allow" + ManageNote: "Allow" ``` LCM Initialization: diff --git a/source/Examples/Resources/AzDoGitRepository/2-UpdateAzDoGitRepository.ps1 b/source/Examples/Resources/AzDoGitRepository/2-UpdateAzDoGitRepository.ps1 index 24c6b5970..64204ba85 100644 --- a/source/Examples/Resources/AzDoGitRepository/2-UpdateAzDoGitRepository.ps1 +++ b/source/Examples/Resources/AzDoGitRepository/2-UpdateAzDoGitRepository.ps1 @@ -3,4 +3,4 @@ This example shows how to update the Git Repository #> -# Not Supported +# Not Currently Supported diff --git a/source/Examples/Resources/AzDoGroupPermission.md b/source/Examples/Resources/AzDoGroupPermission.md index 990575577..4553d0331 100644 --- a/source/Examples/Resources/AzDoGroupPermission.md +++ b/source/Examples/Resources/AzDoGroupPermission.md @@ -1,4 +1,4 @@ -# AzDoGroupPermission Resource Documentation (Currently Disabled) +# AzDoGroupPermission Resource Documentation (Not Currently Supported) ## Overview diff --git a/source/Examples/Resources/AzDoOrganizationGroup.md b/source/Examples/Resources/AzDoOrganizationGroup.md index d9b4d11ca..18e1d63f6 100644 --- a/source/Examples/Resources/AzDoOrganizationGroup.md +++ b/source/Examples/Resources/AzDoOrganizationGroup.md @@ -38,9 +38,7 @@ Configuration ExampleConfig { } } -OrgGroup Start-DscConfiguration -Path ./ExampleConfig -Wait -Verbose - ``` ## Example 2: Sample Configuration using Invoke-DSCResource diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 index c3ddd5345..abb704c56 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Classes/000.CacheItem.ps1 @@ -25,7 +25,7 @@ $cacheItem = [CacheItem]::new("exampleKey", "exampleValue") Creates a new CacheItem instance with the key "exampleKey" and the value "exampleValue". .NOTES -Author: Your Name +Author: Michael Zanatta #> class CacheItem diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 index e86d666ad..a0fda13cc 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Update-DevOpsProject.ps1 @@ -64,14 +64,6 @@ function Update-DevOpsProject $body = @{ name = $ProjectName visibility = $Visibility - <# - TODO: ISSUE with updating the ProcessTemplateId. - capabilities = @{ - processTemplate = @{ - templateTypeId = $ProcessTemplateId - } - } - #> } # Add the description if provided diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 index 2cb15cce0..fcc173295 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Project/Wait-DevOpsProject.ps1 @@ -18,8 +18,8 @@ Wait-DevOpsProject -OrganizationName "MyOrg" -ProjectURL "https://dev.azure.com/MyOrg/MyProject" .NOTES - Author: Your Name - Date: Current Date + Author: Michael Zanatta + Date: 2025-01-06 #> Function Wait-DevOpsProject diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 index eb2e787c1..1368700e9 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Cache Initalization/0.ProjectCache.ps1 @@ -19,8 +19,8 @@ None. None. .NOTES -Author: [Author Name] -Date: [Date] +Author: Michael Zanatta +Date: 2025-01-06 #> Function AzDoAPI_0_ProjectCache diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 index 62e27f068..1b8fe169b 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Find-CacheItem.ps1 @@ -22,8 +22,8 @@ $filteredCacheItem # Returns the CacheItem with the name 'MyCacheItem' from the list of cache items. .NOTES -Author: Your Name -Date: Today's Date +Author: Michael Zanatta +Date: 2025-01-06 #> Function Find-CacheItem { diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 index df2490662..f6c32a2d8 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Get-CacheObject.ps1 @@ -29,7 +29,7 @@ The cache object of the specified type. This function is part of the AzureDevOpsDsc module. .LINK -https://github.com/Azure/AzureDevOpsDsc +https://github.com/dsccommunity/AzureDevOpsDsc #> function Get-CacheObject diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 index 8dce154f3..9f455c199 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Set-CacheObject.ps1 @@ -29,8 +29,8 @@ None. None. .NOTES -Author: Your Name -Date: MM/DD/YYYY +Author: Michael Zanatta +Date: 2025-01-06 #> function Set-CacheObject diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 index 654737a41..d83f2d523 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACEList.ps1 @@ -23,8 +23,8 @@ ConvertTo-ACEList -SecurityNamespace "Namespace" -Identity "User1" -Permissions This example converts the permissions "Read" and "Write" for the identity "User1" in the specified security namespace and organization name to an ACE token. .NOTES -Author: Your Name -Date: Today's Date +Author: Michael Zanatta +Date: 2025-01-06 #> Function ConvertTo-ACEList diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 index 1567b962b..d57b20d83 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-ACL.ps1 @@ -38,8 +38,8 @@ System.Collections.Generic.List[HashTable] A list of Access Control Lists (ACLs) created from the provided permissions. .NOTES -Author: Your Name -Date: Today's Date +Author: Michael Zanatta +Date: 2025-01-06 #> Function ConvertTo-ACL { diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 index a47c66246..48ca8293a 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/ConvertTo-FormattedACL.ps1 @@ -26,7 +26,7 @@ A list of formatted ACLs. .NOTES Author: Michael Zanatta -Date: 06/26/2024 +Date: 2025-01-06 #> Function ConvertTo-FormattedACL diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 index 9385cee09..998433216 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ACL/Get-BitwiseOrResult.ps1 @@ -21,8 +21,8 @@ # Output: 15 .NOTES - Author: Your Name - Date: Current Date + Author: Michael Zanatta + Date: 2025-01-06 #> Function Get-BitwiseOrResult { diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 index 4ec6bbbd3..b55aed41a 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/ConvertTo-Base64String.ps1 @@ -16,7 +16,7 @@ $base64String .NOTES Author: GitHub Copilot -Date: September 2021 +Date: 2025-01-06 #> function ConvertTo-Base64String { diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 index a994eca8d..ddd94ecac 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.ps1 @@ -20,8 +20,8 @@ Returns the ACLIdentity object of the found identity. If no identity is found, null is returned. .NOTES - Author: Your Name - Date: Current Date + Author: Michael Zanatta + Date: 2025-01-06 .EXAMPLE Find-Identity -Name "JohnDoe" diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 index fb3f36225..011ab6108 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Invoke-AzDevOpsApiRestMethod.ps1 @@ -1,6 +1,6 @@ <# .SYNOPSIS - This is a light, generic, wrapper proceedure around 'Invoke-RestMethod' to handle + This is a light, generic, wrapper around 'Invoke-RestMethod' to handle multiple retries and error/exception handling. This function makes no assumptions around the versions of the API used, the resource diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Flush-Log.txt b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Flush-Log.ps1 similarity index 99% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Flush-Log.txt rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Flush-Log.ps1 index 4d6507145..3694487af 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Flush-Log.txt +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Flush-Log.ps1 @@ -1,3 +1,4 @@ +<# Function Flush-Log { [CmdletBinding()] @@ -34,3 +35,4 @@ Function Flush-Log Write-Verbose "[Flush-Log] Log messages written to log files." } +#> diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.ps1 similarity index 99% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.ps1 index a907109d2..f8ba02bb6 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.txt +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Initialize-Log.ps1 @@ -1,3 +1,4 @@ +<# Function Initialize-Log { [CmdletBinding()] param ( @@ -30,3 +31,4 @@ Function Initialize-Log { # Initialize the log files Write-Verbose "[Initialize-Log] Log files initialized at: $LogDirectory" } +#> diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 index 9f2331e4b..cc95a6848 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Error.ps1 @@ -41,7 +41,7 @@ Function Write-Error $VerbosePreference = $originalPreference # Test if the env:enableVerboseLogging variable is set to true - if ($null -ne $LogFilePath) + if (-not [String]::IsNullOrEmpty($LogFilePath)) { # Append the message to the log file $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 index a9eea4170..358b4f743 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Verbose.ps1 @@ -35,7 +35,7 @@ Function Write-Verbose Microsoft.PowerShell.Utility\Write-Verbose $Message # Test if the env:enableVerboseLogging variable is set to true - if ($null -ne $LogFilePath) + if (-not [String]::IsNullOrEmpty($LogFilePath)) { # Append the message to the log file $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 index 3a285c46e..ff5a80dc0 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Proxy Functions/Write-Warning.ps1 @@ -41,7 +41,7 @@ Function Write-Warning Microsoft.PowerShell.Utility\Write-Warning $Message $VerbosePreference = $originalPreference - if ($null -ne $LogFilePath) + if (-not [String]::IsNullOrEmpty($LogFilePath)) { # Append the message to the log file $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.ps1 similarity index 99% rename from source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt rename to source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.ps1 index 23295de1a..0aa72b804 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.txt +++ b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Logging/Write-Log.ps1 @@ -1,3 +1,4 @@ +<# Function Write-Log { [CmdletBinding()] @@ -49,3 +50,4 @@ Function Write-Log Write-Verbose "[Write-Log] Log message written to log file." } +#> diff --git a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.ps1 b/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.ps1 deleted file mode 100644 index fe89f743e..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/PreCommandLookupAction.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -<# -$ExecutionContext.InvokeCommand.PreCommandLookupAction = { - param($command, $commandScriptBlock) - - # - # If the Command is 'Add-AuthenticationHTTPHeader' and is called outside of 'Invoke-AzDevOpsApiRestMethod' function - - if ($command -eq 'Add-AuthenticationHTTPHeader' -and $MyInvocation.MyCommand.Name -ne 'Invoke-AzDevOpsApiRestMethod') - { - throw "The function 'Add-AuthenticationHTTPHeader' can only be called inside of 'Invoke-AzDevOpsApiRestMethod' function." - } - - # - # Any export function used within Invoke-AzDevOpsApiRestMethod is not allowed. - - if ($command -match 'Export-') - { - throw "The command '$command' is not allowed to be used within 'Invoke-AzDevOpsApiRestMethod' function." - } - - # - # Any attempt of using [System.Runtime.InteropServices.Marshal] outside of 'AuthenticationToken' class is not allowed. - - if ($command -match 'System.Runtime.InteropServices.Marshal' -and $MyInvocation.MyCommand.Name -ne 'AuthenticationToken') - { - throw "The command '$command' is not allowed to be used outside of 'AuthenticationToken' class." - } - -} -#> diff --git a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 index e1fb7c2e8..380a2fc75 100644 --- a/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 +++ b/source/Modules/AzureDevOpsDsc.Common/AzureDevOpsDsc.Common.psm1 @@ -4,12 +4,11 @@ param ( [Switch] $isClass ) -#using module AzureDevOpsDsc + # Setup/Import 'DscResource.Common' helper module #$script:resourceHelperModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common' #Import-Module -Name $script:resourceHelperModulePath - $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' $ModuleRoot = $PSScriptRoot diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Get-AzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Get-AzDoOrganizationGroup.ps1 index de551dde8..c4ea98412 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Get-AzDoOrganizationGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Get-AzDoOrganizationGroup.ps1 @@ -120,8 +120,6 @@ Function Get-AzDoOrganizationGroup { # Update the Result $getGroupResult.status = [DSCGetSummaryState]::Changed - # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('AzDoOrganizationGroup:AzDoOrganizationGroup:Deleted&ReCreate', 'The group was deleted and recreated with another group. The properties have changed') } else { @@ -157,11 +155,8 @@ Function Get-AzDoOrganizationGroup } ) - if ($getGroupResult.status -eq [DSCGetSummaryState]::Changed) + if ($getGroupResult.status -ne [DSCGetSummaryState]::Changed) { - # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('AzDoOrganizationGroup:AzDoOrganizationGroup:Changed', 'The group has changed') - } else { $getGroupResult.Ensure = [Ensure]::Present } @@ -230,8 +225,6 @@ Function Get-AzDoOrganizationGroup { $getGroupResult.status = [DSCGetSummaryState]::NotFound $getGroupResult.propertiesChanged = @('description', 'displayName') - # Add the reason - #$getGroupResult.Reasons += [DscResourceReason]::New('AzDoOrganizationGroup:AzDoOrganizationGroup:NotFound', 'The group is not found') return $getGroupResult } diff --git a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Test-AzDoOrganizationGroup.ps1 b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Test-AzDoOrganizationGroup.ps1 index ed7c35cd4..25d41b5d9 100644 --- a/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Test-AzDoOrganizationGroup.ps1 +++ b/source/Modules/AzureDevOpsDsc.Common/Resources/Functions/Public/AzDoOrganizationGroup/Test-AzDoOrganizationGroup.ps1 @@ -48,7 +48,7 @@ Function Test-AzDoOrganizationGroup ) # Firstly we need to compare to see if the group names are the same. If so we can return $false. - if ($GetReslut.Status -eq [DSCGetSummaryState]::Unchanged ) + if ($GetResult.Status -eq [DSCGetSummaryState]::Unchanged ) { $result = $true diff --git a/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 b/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 deleted file mode 100644 index 792d60054..000000000 --- a/source/Modules/AzureDevOpsDsc.Common/Services/Functions/Public/Refresh-Cache.ps1 +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/tests/DSC/InModuleScope.ps1 b/tests/DSC/InModuleScope.ps1 deleted file mode 100644 index d4c4a4802..000000000 --- a/tests/DSC/InModuleScope.ps1 +++ /dev/null @@ -1,37 +0,0 @@ -$VerbosePreference = 'Continue' -$ErrorActionPreference = 'Break' - -Get-ChildItem -LiteralPath 'C:\Temp\AzureDevOpsDSC\source\Enum' -File | ForEach-Object { - Write-Verbose "Dot Sourcing $($_.FullName)" - . $_.FullName -} -Get-ChildItem -LiteralPath 'C:\Temp\AzureDevOpsDSC\source\Classes' -File | ForEach-Object { - Write-Verbose "Dot Sourcing $($_.FullName)" - # Read the file and remove [DscResource()] attribute - $file = Get-Command $_.FullName - # Remove [DscResource()] attribute - $content = $file.ScriptContents -replace '\[DscResource\(\)\]', '' - # Conver the string array into ScriptBlock - $scriptBlock = [ScriptBlock]::Create($content) - # Dot source the script block - . $scriptBlock - -} - - -Get-ChildItem -LiteralPath 'C:\Temp\AzureDevOpsDSC\source\Modules\AzureDevOpsDsc.Common\Resources\Functions' -Recurse -File | ForEach-Object { - Write-Verbose "Dot Sourcing $($_.FullName)" - . $_.FullName -} -Get-ChildItem -LiteralPath 'C:\Temp\AzureDevOpsDSC\source\Modules\AzureDevOpsDsc.Common\Api' -Recurse -File | ForEach-Object { - Write-Verbose "Dot Sourcing $($_.FullName)" - . $_.FullName -} - -$ModuleRoot = 'C:\Temp\AzureDevOpsDSC\source\Modules\AzureDevOpsDsc.Common\' - -# Initalize the Cache -'LiveGroups', 'LiveProjects', 'Project','Team', 'Group', 'SecurityDescriptor' | ForEach-Object { - Initialize-CacheObject -CacheType $_ -} - diff --git a/tests/Integration/Resources/AzDoGitPermission.tests.ps1 b/tests/Integration/Resources/AzDoGitPermission.tests.ps1 index d1fc8312e..9a3820b69 100644 --- a/tests/Integration/Resources/AzDoGitPermission.tests.ps1 +++ b/tests/Integration/Resources/AzDoGitPermission.tests.ps1 @@ -1,5 +1,3 @@ - - Describe "AzDoGitPermission Integration Tests" { BeforeAll { diff --git a/tests/Integration/Supporting/API/Get-AzDevOpsApiVersion.ps1 b/tests/Integration/Supporting/API/Get-AzDevOpsApiVersion.ps1 index 84e5b10f7..afeb07a88 100644 --- a/tests/Integration/Supporting/API/Get-AzDevOpsApiVersion.ps1 +++ b/tests/Integration/Supporting/API/Get-AzDevOpsApiVersion.ps1 @@ -1,4 +1,3 @@ - function Get-AzDevOpsApiVersion { [CmdletBinding()] diff --git a/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 b/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 index de3035d33..b54f6f6f4 100644 --- a/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 +++ b/tests/Integration/Supporting/API/Invoke-APIRestMethod.ps1 @@ -1,4 +1,3 @@ - function Invoke-APIRestMethod { [CmdletBinding()] diff --git a/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 b/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 index 77b54ed7d..47b649ab9 100644 --- a/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 +++ b/tests/Integration/Supporting/APICalls/List-DevOpsGroups.ps1 @@ -25,9 +25,6 @@ Function List-DevOpsGroups { return $null } - # - # Perform a lookup to get the group - # # Return the groups from the cache return $groups.Value diff --git a/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 b/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 index 8b1d6d8ed..029218014 100644 --- a/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 +++ b/tests/Integration/Supporting/APICalls/Remove-DevOpsProject.ps1 @@ -49,9 +49,8 @@ function Remove-DevOpsProject try { # Invoke the Azure DevOps REST API to create the project - $response = Invoke-APIRestMethod @params - # Output the response which contains the created project details - return $response + return (Invoke-APIRestMethod @params) + } catch { Write-Error "[Remove-DevOpsProject] Failed to create the Azure DevOps project: $_" diff --git a/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 b/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 index e05415d5d..083092867 100644 --- a/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 +++ b/tests/Integration/Supporting/Functions/SupportingFunctions.ps1 @@ -1,5 +1,3 @@ - - Function New-Project { param( [string]$ProjectName @@ -46,8 +44,6 @@ Function New-Repository { } - - Function New-Group { param( [string]$ProjectName, diff --git a/tests/Integration/Supporting/Teardown.ps1 b/tests/Integration/Supporting/Teardown.ps1 index 83ffc0ee1..220fe5203 100644 --- a/tests/Integration/Supporting/Teardown.ps1 +++ b/tests/Integration/Supporting/Teardown.ps1 @@ -46,4 +46,3 @@ if ($ClearAll -or $ClearOrganizationGroups) Remove-DevOpsGroup -GroupDescriptor $_.descriptor -OrganizationName $OrganizationName } } - diff --git a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 index 2b400c4a9..b6081be97 100644 --- a/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 +++ b/tests/Unit/Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Helper/Find-Identity.tests.ps1 @@ -105,10 +105,8 @@ Describe 'Find-Identity Function Tests' { } - it 'Should write a non-terminating error when the SearchType is incorrect' { - Mock Write-Error -Verifiable + it 'Should write a terminating error when the SearchType is incorrect' { { Find-Identity -Name 'groupDescriptor' -OrganizationName 'TestOrg' -SearchType 'invalidType' } | Should -Throw - $result | Should -BeNullOrEmpty } it 'Should return a value for the search-type ' -TestCases $params {