Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 23 additions & 41 deletions eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@ function Get-ReleasePlan-Link($releasePlanWorkItemId)
return $workItem["fields"]
}

function Get-ReleasePlansForCPEXAttestation($releasePlanWorkItemId = $null, $targetServiceTreeId = $null)
function Get-ReleasePlansForCPEXAttestation()
{
$fields = @()
$fields += "Custom.ProductServiceTreeID"
Expand All @@ -1127,32 +1127,23 @@ function Get-ReleasePlansForCPEXAttestation($releasePlanWorkItemId = $null, $tar
$fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", "

$query = "SELECT ${fieldList} FROM WorkItems WHERE [System.WorkItemType] = 'Release Plan'"

if ($releasePlanWorkItemId){
$query += " AND [System.Id] = '$releasePlanWorkItemId'"
} else {
$query += " AND [System.State] = 'Finished'"
$query += " AND [Custom.AttestationStatus] IN ('', 'Pending')"
$query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'"
$query += " AND [System.Tags] NOT CONTAINS 'Release Planner Test App'"
$query += " AND [System.Tags] NOT CONTAINS 'non-APEX tracking'"
$query += " AND [System.Tags] NOT CONTAINS 'out of scope APEX'"
$query += " AND [System.Tags] NOT CONTAINS 'APEX out of scope'"
$query += " AND [System.Tags] NOT CONTAINS 'validate APEX out of scope'"
$query += " AND [Custom.ProductServiceTreeID] <> ''"
$query += " AND [Custom.ProductLifecycle] <> ''"
$query += " AND [Custom.ProductType] IN ('Feature', 'Offering', 'Sku')"
}

if ($targetServiceTreeId){
$query += " AND [Custom.ProductServiceTreeID] = '${targetServiceTreeId}'"
}
$query += " AND [System.State] = 'Finished'"
$query += " AND [Custom.AttestationStatus] IN ('', 'Pending')"
$query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'"
$query += " AND [System.Tags] NOT CONTAINS 'Release Planner Test App'"
$query += " AND [System.Tags] NOT CONTAINS 'non-APEX tracking'"
$query += " AND [System.Tags] NOT CONTAINS 'out of scope APEX'"
$query += " AND [System.Tags] NOT CONTAINS 'APEX out of scope'"
$query += " AND [System.Tags] NOT CONTAINS 'validate APEX out of scope'"
$query += " AND [Custom.ProductServiceTreeID] <> ''"
$query += " AND [Custom.ProductLifecycle] <> ''"
$query += " AND [Custom.ProductType] IN ('Feature', 'Offering', 'Sku')"

$workItems = Invoke-Query $fields $query
return $workItems
}

function Get-TriagesForCPEXAttestation($triageWorkItemId = $null, $targetServiceTreeId = $null)
function Get-TriagesForCPEXAttestation()
{
$fields = @()
$fields += "Custom.ProductServiceTreeID"
Expand All @@ -1167,25 +1158,16 @@ function Get-TriagesForCPEXAttestation($triageWorkItemId = $null, $targetService
$fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", "

$query = "SELECT ${fieldList} FROM WorkItems WHERE [System.WorkItemType] = 'Triage'"

if ($triageWorkItemId){
$query += " AND [System.Id] = '$triageWorkItemId'"
} else {
$query += " AND ([Custom.DataplaneAttestationStatus] IN ('', 'Pending') OR [Custom.ManagementPlaneAttestationStatus] IN ('', 'Pending'))"
$query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'"
$query += " AND [System.Tags] NOT CONTAINS 'Release Planner Test App'"
$query += " AND [System.Tags] NOT CONTAINS 'non-APEX tracking'"
$query += " AND [System.Tags] NOT CONTAINS 'out of scope APEX'"
$query += " AND [System.Tags] NOT CONTAINS 'APEX out of scope'"
$query += " AND [System.Tags] NOT CONTAINS 'validate APEX out of scope'"
$query += " AND [Custom.ProductServiceTreeID] <> ''"
$query += " AND [Custom.ProductLifecycle] <> ''"
$query += " AND [Custom.ProductType] IN ('Feature', 'Offering', 'Sku')"
}

if ($targetServiceTreeId){
$query += " AND [Custom.ProductServiceTreeID] = '$targetServiceTreeId'"
}
$query += " AND ([Custom.DataplaneAttestationStatus] IN ('', 'Pending') OR [Custom.ManagementPlaneAttestationStatus] IN ('', 'Pending'))"
$query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'"
$query += " AND [System.Tags] NOT CONTAINS 'Release Planner Test App'"
$query += " AND [System.Tags] NOT CONTAINS 'non-APEX tracking'"
$query += " AND [System.Tags] NOT CONTAINS 'out of scope APEX'"
$query += " AND [System.Tags] NOT CONTAINS 'APEX out of scope'"
$query += " AND [System.Tags] NOT CONTAINS 'validate APEX out of scope'"
$query += " AND [Custom.ProductServiceTreeID] <> ''"
$query += " AND [Custom.ProductLifecycle] <> ''"
$query += " AND [Custom.ProductType] IN ('Feature', 'Offering', 'Sku')"

$workItems = Invoke-Query $fields $query
return $workItems
Expand Down
25 changes: 25 additions & 0 deletions eng/pipelines/cpex-attestation-automation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
trigger: none
pr: none

pool:
name: azsdk-pool
demands: ImageOverride -equals ubuntu-24.04

parameters:
- name: TableName
type: string
default: 'ProdKpiEvidenceStream'

jobs:
- job: AttestCPEXKPIs
steps:
- checkout: self
- task: AzureCLI@2
displayName: Invoke CPEX KPI Attestation Automation
inputs:
azureSubscription: opensource-api-connection
scriptType: pscore
scriptPath: eng/scripts/Invoke-CPEX-Attestation-Automation.ps1
arguments: >
-AzureSDKEmailUri "$(AzureSDKEmailerSasURL)"
-TableName "$(TableName)"
54 changes: 54 additions & 0 deletions eng/pipelines/cpex-attestation-manual.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
trigger: none
pr: none

pool:
name: azsdk-pool
demands: ImageOverride -equals ubuntu-24.04

parameters:
- name: TableName
type: string
default: 'ProdKpiEvidenceStream'
displayName: Table Name
- name: ActionItemId
type: string
displayName: KPI ID
- name: TargetId
type: string
displayName: Product ID
- name: TargetType
type: string
values:
- Service
- Offering
- ProductSku
- Feature
displayName: Target Type
- name: Status
type: int
displayName: Status (0 - Incomplete, 1 - Complete, 3 - N/A)
values:
- 0
- 1
- 3
Comment thread
smw-ms marked this conversation as resolved.
- name: EvidenceUrl
type: string
displayName: Evidence Link

jobs:
- job: ManualAttestationEntry
steps:
- checkout: self
- task: AzureCLI@2
displayName: Submit Manual CPEX KPI Entry
inputs:
azureSubscription: opensource-api-connection
scriptType: pscore
scriptPath: eng/scripts/Add-CPEX-Attestation-Entry.ps1
arguments: >
-TableName "$(TableName)"
-ActionItemId "$(ActionItemId)"
-TargetId "$(TargetId)"
-TargetType "$(TargetType)"
-Status "$(Status)"
-EvidenceUrl "$(EvidenceUrl)"
37 changes: 0 additions & 37 deletions eng/pipelines/cpex-attestation.yml

This file was deleted.

80 changes: 80 additions & 0 deletions eng/scripts/Add-CPEX-Attestation-Entry.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<#
.SYNOPSIS
Adds a CPEX KPI attestation entry to the Kusto database.

.DESCRIPTION
This script is designed to support both automated and manual workflows for CPEX KPI attestation.
It inserts a single attestation record into the specified Kusto table, capturing key metadata such as the action item ID, target product ID and type, attestation status, and evidence URL.
In automated scenarios, it can be invoked as part of a larger pipeline to log attestations programmatically.
For manual use, it enables engineers to insert individual entries when automation is not applicable or when validating edge cases.

.PARAMETER TableName
The name of the Kusto table where attestation entries will be written.

.PARAMETER ActionItemId
The unique identifier for the KPI action item being attested.

.PARAMETER TargetId
The Service Tree ID of the product being attested.

.PARAMETER TargetType
The type of the product (e.g., ProductSku, Feature, Offering).

.PARAMETER Status
The attestation status value (e.g., 0 for Incomplete, 1 for Completed, 3 for Not Applicable).

.PARAMETER EvidenceUrl
The URL pointing to the evidence supporting the attestation, i.e. link to release plan
#>

param (
[Parameter(Mandatory = $true)]
[string] $TableName,

[Parameter(Mandatory = $true)]
[string] $ActionItemId,

[Parameter(Mandatory = $true)]
[string] $TargetId,

[Parameter(Mandatory = $true)]
[string] $TargetType,

[Parameter(Mandatory = $true)]
[int] $Status,

[Parameter(Mandatory = $true)]
[string] $EvidenceUrl

)

function InvokeKustoCommand($command) {
try {
$clusterUri = "https://azsdk-cpex-attestation.westus2.kusto.windows.net"
$databaseName = "CPEX_Attestation_DB"
$accessToken = az account get-access-token --resource "https://api.kusto.windows.net" --query "accessToken" --output tsv
$headers = @{ Authorization="Bearer $accessToken" }
$body = @{ csl = $command; db = $databaseName } | ConvertTo-Json -Depth 3
Invoke-RestMethod -Uri "$clusterUri/v1/rest/mgmt" -Headers $headers -Method Post -Body $body -ContentType "application/json"
} catch {
Write-Error "Failed to invoke Kusto command: $command"
Write-Error "Exception message: $($_.Exception.Message)"
throw "Terminating due to failure in invoking kusto command"
}

}

$command = @"
.append $TableName <|
print
ActionItemId = "$ActionItemId",
TargetId = "$TargetId",
TargetType = "$TargetType",
Status = int($Status),
CreatedTime = datetime(now),
EvidenceUrl = "$EvidenceUrl"
"@

Write-Host "Adding attestation entry for product [$targetId], with status [$status] for KPI action id [$actionItemId]."
InvokeKustoCommand $command
Write-Host "Added attestation entry for product [$targetId], with status [$status] for KPI action id [$actionItemId]."
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,13 @@ The Uri of the app used to send email notifications

.PARAMETER TableName
The name of the Kusto table where attestation entries will be written.

.PARAMETER ReleasePlanWorkItemId
Specifies a single release plan work item to process for KPI attestation. If omitted, all eligible items are considered.

.PARAMETER TriageWorkItemId
Specifies a single triage work item to process for KPI attestation. If omitted, all eligible items are considered.

.PARAMETER TargetServiceTreeId
Specifies the Serivice Tree Id of the product whose attestation status should be evaluated. Used to scope triage and release plan queries to a specific product.
#>
param (
[Parameter(Mandatory = $true)]
[string] $AzureSDKEmailUri,

[Parameter(Mandatory = $true)]
[string] $TableName,

[Parameter(Mandatory = $false)]
[string] $ReleasePlanWorkItemId,

[Parameter(Mandatory = $false)]
[string] $TriageWorkItemId,

[Parameter(Mandatory = $false)]
[string] $TargetServiceTreeId
[string] $TableName
)

Set-StrictMode -Version 3
Expand Down Expand Up @@ -74,37 +56,8 @@ foreach ($kpiId in $KPI_ID_TO_TITLE.Keys) {

$failedAttestations = @()

function InvokeKustoCommand($command) {
try {
$clusterUri = "https://azsdk-cpex-attestation.westus2.kusto.windows.net"
$databaseName = "CPEX_Attestation_DB"
$accessToken = az account get-access-token --resource "https://api.kusto.windows.net" --query "accessToken" --output tsv
$headers = @{ Authorization="Bearer $accessToken" }
$body = @{ csl = $command; db = $databaseName } | ConvertTo-Json -Depth 3
Invoke-RestMethod -Uri "$clusterUri/v1/rest/mgmt" -Headers $headers -Method Post -Body $body -ContentType "application/json"
} catch {
Write-Error "Failed to invoke Kusto command: $command"
Write-Error "Exception message: $($_.Exception.Message)"
throw "Terminating due to failure in invoking kusto command"
}

}

function AddAttestationEntry($targetId, $actionItemId, $status, $targetType, $url, $productName) {
$command = @"
.append $TableName <|
print
ActionItemId = "$actionItemId",
TargetId = "$targetId",
TargetType = "$targetType",
Status = int($status),
CreatedTime = datetime(now),
EvidenceUrl = "$url"
"@

Write-Host "Adding attestation entry for product [$targetId], with status [$status] for KPI action id [$actionItemId]."
InvokeKustoCommand $command
Write-Host "Added attestation entry for product [$targetId], with status [$status] for KPI action id [$actionItemId]."
& (Join-Path $PSScriptRoot "Add-CPEX-Attestation-Entry.ps1") -TableName $TableName -ActionItemId $actionItemId -TargetId $targetId -TargetType $targetType -Status $status -EvidenceUrl $url

$successfulAttestations[$actionItemId] += @{
productId = $targetId
Expand Down Expand Up @@ -193,7 +146,7 @@ function BuildFailureEmailBody {
return $body
}

$triages = Get-TriagesForCPEXAttestation -triageWorkItemId $TriageWorkItemId -targetServiceTreeId $TargetServiceTreeId
$triages = Get-TriagesForCPEXAttestation

foreach ($triage in $triages) {
try {
Expand Down Expand Up @@ -267,7 +220,7 @@ foreach ($triage in $triages) {
}
}

$releasePlans = Get-ReleasePlansForCPEXAttestation -releasePlanWorkItemId $ReleasePlanWorkItemId -targetServiceTreeId $TargetServiceTreeId
$releasePlans = Get-ReleasePlansForCPEXAttestation

foreach ($releasePlan in $releasePlans) {
try {
Expand Down
Loading