AzureDevOpsDscNative.psd1

@{
    RootModule           = 'AzureDevOpsDscNative.psm1'

    # Version number of this module.
    #
    # This value is a fallback for local and CI builds only. Released versions are
    # driven by the git tag: the publish workflow sets $env:ModuleVersion from the
    # tag, and the build stamps that value into the packaged manifest instead of this
    # one. Do not hand-edit this to cut a release - push a vX.Y.Z tag.
    moduleVersion      = '1.3.0'

    # ID used to uniquely identify this module
    GUID                 = 'e7eb078e-5e97-42be-ae8f-decb140fc38e'

    # Author of this module
    Author               = 'ZanattaMichael'

    # Company or vendor of this module
    CompanyName          = 'ZanattaMichael'

    # Copyright statement for this module
    Copyright            = 'Copyright the DSC Community contributors and ZanattaMichael. All rights reserved.'

    # Description of the functionality provided by this module
    Description          = 'A fork of the DSC Community AzureDevOpsDsc module with native DSC v3 support: every resource is discoverable and invokable by dsc.exe via the Microsoft.Adapter/PowerShell adapter, using generated adapted resource manifests, without requiring a wrapper resource.'

    # Minimum version of the Windows PowerShell engine required by this module
    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 = @()

    # Cmdlets to export from this module
    #CmdletsToExport = @()

    # Variables to export from this module
    VariablesToExport    = @()

    # Aliases to export from this module
    AliasesToExport      = @()

    # Import all the 'DSCClassResource', modules as part of this module
    NestedModules        = @()

    DscResourcesToExport = @('AzDoGroupPermission','AzDoOrganizationGroup','AzDoProject','AzDoProjectServices','AzDoProjectGroup','AzDoGroupMember','AzDoGitRepository','AzDoGitPermission','AzDoAreaPermission','AzDoIterationPermission','AzDoWIPTags','AzDoAreaNodes','AzDoIterationNodes','AzDoBranchPolicy','AzDoVariableGroup','AzDoServiceConnection','AzDoPipelineEnvironment','AzDoAgentPool','AzDoAgentQueue','AzDoTeam','AzDoTeamMember','AzDoPipeline','AzDoPipelinePermission','AzDoEnvironmentApproval','AzDoVariableGroupPermission','AzDoServiceConnectionPermission','AzDoAgentPoolPermission','AzDoSecurityNamespacePermission','AzDoArtifactFeed','AzDoArtifactFeedPermission','AzDoDeploymentGroup','AzDoTaskGroup','AzDoOrganizationSettings','AzDoExtension','AzDoAuditStream','AzDoProjectPermission','AzDoEnvironmentPermission','AzDoWiki','AzDoNotificationSubscription','AzDoRepositorySettings','AzDoCheckConfiguration','AzDoTeamSettings','AzDoArtifactFeedSettings','AzDoArtifactFeedView','AzDoProcess','AzDoProcessPermission','AzDoUserEntitlement','AzDoServiceHook','AzDoPipelineSettings','AzDoQueryFolder','AzDoWorkItemQuery','AzDoQueryPermission','AzDoWIPTagHygiene','AzDoSecureFile','AzDoSecureFilePermission','AzDoPipelineFolder','AzDoPipelineFolderPermission','AzDoGroupEntitlement','AzDoServicePrincipalEntitlement','AzDoPicklist','AzDoProcessWorkItemType','AzDoProcessField','AzDoProcessState','AzDoProcessRule','AzDoProcessBehavior','AzDoPipelineAuthorization','AzDoEnvironmentKubernetesResource','AzDoEnvironmentVMResource','AzDoDeploymentGroupTarget','AzDoReleaseFolder','AzDoReleaseFolderPermission','AzDoReleaseDefinitionPermission','AzDoTestVariable','AzDoTestConfiguration','AzDoTestPlan','AzDoTestSuite','AzDoBuildRetentionSettings','AzDoWikiPage','AzDoTaggingPermission','AzDoAnalyticsPermission','AzDoAnalyticsViewsPermission','AzDoDashboard','AzDoOrganizationPermission','AzDoTaskGroupPermission')

    RequiredAssemblies   = @()

    # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
    PrivateData          = @{

        PSData = @{
            # Set to a prerelease string value if the release should be a prerelease.
            Prerelease   = 'preview1'

            # Tags applied to this module. These help with module discovery in online galleries.
            Tags         = @('DesiredStateConfiguration', 'DSC', 'DSCResourceKit', 'DSCResource', 'DSCv3', 'AzureDevOps')

            # A URL to the license for this module.
            LicenseUri   = 'https://github.com/ZanattaMichael/AzureDevOpsDsc/blob/main/LICENSE'

            # A URL to the main website for this project.
            ProjectUri   = 'https://github.com/ZanattaMichael/AzureDevOpsDsc'

            # A URL to an icon representing this module.
            IconUri      = 'https://dsccommunity.org/images/DSC_Logo_300p.png'

            # ReleaseNotes of this module.
            # Per-release notes are generated from the CHANGELOG's [Unreleased] section
            # at build time and attached to the GitHub Release. This link is the fallback
            # shown on the Gallery listing.
            ReleaseNotes = '## [1.3.0-preview1] - 2026-10-18
 
### Changed
 
- Build
  - `azure-pipelines.yml` brought in line with the GitHub workflows so it runs
    on current Microsoft-hosted agents: full-history checkout for GitVersion,
    GitVersion.Tool 5.12.0 run with `DOTNET_ROLL_FORWARD=Major` (hosted images
    no longer ship .NET 6/7), unit tests run under Pester 5.7.1 as in
    `unit-tests.yml` instead of the Pester 4 `build.ps1 -tasks test` path,
    HQRM reported but non-gating, and the retired Codecov bash uploader and
    `PublishCodeCoverageResults@1` removed. The Deploy stage no longer requires a
    `dsccommunity` organization; it runs only on a `v*` tag with the pipeline
    variable `PublishRelease` set to `true`.
  - The integration workflows now cope with more than one runner carrying the
    `AZDO-AGENT` label. The v2 and v3 live-organization jobs share one
    concurrency group, so they no longer run against the organization at the same
    time and delete each other''s test objects in their pre-run teardown.
  - The integration workflows no longer move module copies out of, and back
    into, the runner''s standard module folders around each run - a failed move
    there stranded copies in `RUNNER_TEMP`, which the runner empties between
    jobs. They load the module only from their workspace build and now fail,
    naming each one, if a copy is installed in a standard module folder.
    `tests/Integration/README.md` lists what an `AZDO-AGENT` machine needs.
  - `scripts/redeploy-module.ps1` no longer copies the build into
    `Documents\PowerShell\Modules`. It puts `output\builtModule` at the front of
    `PSModulePath` for the current session and warns about any copy in a
    standard module folder.
- AzureDevOpsDscNative
  - `AzDoProject.ProcessTemplate` no longer restricts a project to the four
    system processes (`Agile`, `Scrum`, `CMMI`, `Basic`) via `ValidateSet` -
    any process name known to the organization, including an inherited
    process, is accepted and resolved against the `LiveProcesses` cache,
    falling back to a live lookup. `Set` can now change a project''s process: `Get-AzDoProject`
    compares the project''s current process against the desired one and, when
    they differ, resolves both to their system-process ancestor
    (`Get-AzDoProcessFamilyRootId`) to decide whether Azure DevOps will permit
    the migration. A compatible change (moving between a system process and
    one of its inherited children, or between two inherited children of the
    same parent) is applied via the new `projectprocessmigration` endpoint
    (`Move-DevOpsProjectProcess`); an incompatible one - crossing unrelated
    process families - is refused with a clear error in both `Get` and `Set`,
    since `Get`''s `Error` status still routes to `Set` and the refusal has to
    be repeated there to hold. Previously, `ProcessTemplate` was listed in
    `GetDscResourcePropertyNamesWithNoSetSupport()` and `Update-DevOpsProject`
    took a `ProcessTemplateId` parameter it never referenced, so a process
    change on an existing project was always a silent no-op ([issue #75](https://github.com/ZanattaMichael/AzureDevOpsDsc/issues/75)).
 
- Performance
  - `Set()` now runs the resource''s `Get` function once instead of twice:
    `SetToDesiredState()` reads the current state and passes it to a new
    `GetDscRequiredAction([Hashtable])` overload rather than reading it again.
  - The fixed 2-second sleep after every `Set()` is gone. `GetPostSetWaitTimeMs()`
    now defaults to `0`; a resource whose API needs time before a write is
    readable overrides it.
  - Caches are loaded on first use. `Construct()` no longer initializes every
    cache type (with `-Debug` forced on) each time a resource is instantiated;
    `Get-CacheObject` already imports a cache when it is first read. A missing
    cache file is now reported as verbose output rather than a warning, since
    it is the normal state for a cache nothing has written yet.
  - Cache lookups no longer copy or scan the whole cache:
    - `Get-CacheObject` returns the live cache list instead of letting
      PowerShell unroll it into a copy on every read. An empty cache comes back
      as an empty list rather than `$null`, and a cache stored as an array is
      normalized to a `List[CacheItem]` once.
    - `Add-CacheItem` and `Get-CacheItem` find a key through a new
      case-insensitive key index (`Get-CacheKeyIndex`) instead of a
      `Where-Object` scan, so filling a cache is linear rather than quadratic.
      The index is rebuilt whenever the cache list is replaced or its count
      changes, and a cache holding a duplicate key falls back to scanning.
    - `Add-CacheItem` replaces an existing key in place instead of copying the
      cache through `Remove-CacheItem`.
    - `Find-Identity` filters the group, user and service-principal caches with
      `.Where()` instead of copying each one per ACE.
    - `CacheItem` stamps its creation time with `[datetime]::Now` rather than
      a `Get-Date` call.
 
- Documentation
  - `docs/ResourceRoadmap.md` gains §12, the resources a code review found
    missing that neither the roadmap nor an open issue tracked: permission
    resources for the `Collection`, `MetaTask`, `ServiceHooks`, `AuditLog`,
    `EventSubscription` and `VersionControlItems` namespaces and for deployment
    groups and project agent queues; `AzDoWorkItemTemplate` and
    `AzDoTaskboardColumn`; `AzDoAgent`, agent pool maintenance and per-pipeline
    settings on `AzDoPipeline`; `AzDoTestResultRetentionSettings` and
    organization-wide feed permissions; and project properties and notification
    delivery settings. It also records what was checked and found already
    covered or not buildable, and a suggested order. §2, §8 and §9 point to it.
  - `docs/ResourceRoadmap.md` gains §13 for open issues #66 (Copilot code
    review) and #67 (GitHub Advanced Security), which the roadmap did not
    mention. It lists the proposed resources and the questions each issue
    must settle first, and gives an order. §8 and §9 point to it.
 
### Fixed
 
- Integration tests
  - `AzDoQueryPermission` and `AzDoWorkItemQuery` create their query folder
    through the new `Invoke-TestQueryFolderSet`. It retries the
    `TF401256: You do not have Write permissions for query Shared Queries` 403
    that a project returns for a few seconds after it reaches `wellFormed`.
    `Invoke-AzDevOpsApiRestMethod` no longer retries a 403, and that retry
    used to hide the delay.
  - `AzDoReleaseDefinitionPermission` calls `Enable-TestClassicPipeline` before
    creating its classic release definition, and skips when an organization
    policy forbids classic pipelines, as `AzDoTaskGroup` and
    `AzDoDeploymentGroup` already do.
  - `AzDoPicklist` polls for up to 30 seconds for the removed picklist to
    drop out of the picklist list. The list could still return it for a moment
    after a successful DELETE.
  - `Get-MIToken` requests its token with `-NoAuthentication`, which
    `Invoke-APIRestMethod` now honours. It used to build an Authorization
    header from `$Global:DSCAZDO_AuthenticationToken`, so after a test whose
    token request failed had left that global null, the post-run teardown
    threw before cleaning up or reporting the test results.
- AzureDevOpsDscNative
  - Reloading a cache no longer writes the whole cache to the output stream
    ([issue #55](https://github.com/ZanattaMichael/AzureDevOpsDsc/issues/55)).
    `Import-CacheObject` returns the collection it imported, and
    `Initialize-CacheObject`, `Refresh-CacheObject` and `Refresh-AzDoCache`
    called it for its side effect without discarding that value. Every `New-`
    and `Set-` function that ends by refreshing its cache printed the cache as
    a `Key`/`Value` table during `Invoke-DscResource`.
  - `Get-AzDoProjectServices` reports `NotFound` for a project that does not
    exist, rather than failing with `TF200016`. Its live fallback lookup now
    goes through `Resolve-AzDoProject`, which treats a 404 as no project.
  - `Invoke-AzDevOpsApiRestMethod -AzureArcAuthentication` retries a 408, 429
    or 5xx from the Azure Arc token endpoint like any other request. It used to
    hand every error straight back to `Get-AzManagedIdentityToken`, which
    only needs the 401 challenge unchanged, so a throttled token request failed
    the resource being constructed. It showed up as a 429 failing
    `[AzDoProject]::Export()` and `[AzDoGitRepository]::Export()`.
  - `Invoke-AzDevOpsApiRestMethod` no longer throws "Unable to find type
    [APIRateLimit]" on a 429 or a response carrying rate-limit headers. The
    class lives in the root module, which the nested `AzureDevOpsDsc.Common`
    module cannot resolve type names from; it now builds the object through
    the new `New-APIRateLimit` function, as the token classes already do.
  - The nested `AzureDevOpsDsc.Common` module now imports the
    `DscResource.Common` copy shipped beside it. It called `Get-LocalizedData`
    without importing it, so the command was found through auto-discovery as an
    export of `AzureDevOpsDscNative`, which was itself still loading. Every
    process that had not already imported `DscResource.Common` globally failed
    to load the module with "the module could not be loaded". That included
    each `dsc resource get/test/set` call through the DSC v3 PowerShell adapter.
  - The identity cache initializer (`AzDoAPI_7_IdentitySubjectDescriptors`)
    re-added every group, user and service principal with the whole cache item
    as its value, so `.value.ACLIdentity` read `$null` for all of them and every
    descriptor lookup in `Find-Identity` missed the cache and fell through to
    the API. The resolved identity is now stamped onto the cached item in place.
  - `Invoke-AzDevOp'


        } # End of PSData hashtable

    } # End of PrivateData hashtable
}